You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-client/pym/client/client.py

1466 lines
58 KiB

9 years ago
# -*- coding: utf-8 -*-
# Copyright 2010-2016 Mir Calculate. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import stat
import re
import sys
import subprocess
import ldap
import time
import getpass
from os import path
9 years ago
from calculate.lib.cl_ini_parser import iniParser
from calculate.lib.cl_print import color_print
9 years ago
from calculate.lib.utils.ip import Pinger, IPError
from calculate.lib.utils.files import (getModeFile, removeDir,
pathJoin, tarLinks, readFile, writeFile,
9 years ago
listDirectory, process, find, STDOUT,
checkUtils)
from calculate.lib.utils.samba import Samba, SambaError
from calculate.lib.utils.mount import isMount
9 years ago
from calculate.lib.utils.common import cmpVersion
from calculate.desktop._cl_keys import getKey, clearKey
from calculate.lib.convertenv import convertEnv
from calculate.lib.encrypt import encrypt
from client_cache import userCache
from shutil import copy2
from socket import gethostbyname
import tarfile
from calculate.desktop.desktop import Desktop
from calculate.client.rsync import ProfileSyncer, ProfileSyncerError
from calculate.lib.utils.common import get_fastlogin_domain_path
9 years ago
from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate
_ = lambda x: x
setLocalTranslate('cl_client3', sys.modules[__name__])
__ = getLazyLocalTranslate(_)
9 years ago
class ClientError(Exception):
pass
9 years ago
class RsyncProgressBar:
"""
Rsync with using setProgress
"""
# get number of send file from info rsync stream
senderre = re.compile("\[sender\] i=(\d+) ", re.S)
# get number of receivce file from info rsync rtream
receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S)
pipe = None
maximum = 1
copyStarting = False
9 years ago
def __init__(self, title, secondtitle, rsyncstr, parent, maximum=1,
rsyncver="3.0.9"):
self.title = title
self.secondtitle = secondtitle
self.maximum = maximum
self.rsyncstr = rsyncstr
self.parent = parent
self.value = 0
self.errors = []
if cmpVersion(rsyncver, "3.1.0") >= 0:
self.extraopts = " --msgs2stderr"
self.use_stderr = True
else:
self.extraopts = ""
self.use_stderr = False
def getFilesNum(self):
"""
Get files count created by generator
"""
if self.pipe:
return self.value
def getExitCode(self):
"""
Get rsync exitcode
"""
if self.pipe:
return self.pipe.wait()
return 255
def getErrMessage(self):
"""
Rsync error message
"""
if self.pipe:
if self.use_stderr:
return "".join(self.errors)
else:
return self.pipe.stderr.read()
return _("RsyncProgressBar: Wrong pipe")
def runsilent(self):
"""
Run rsync without progressbar
"""
9 years ago
self.pipe = subprocess.Popen(self.rsyncstr + self.extraopts,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, shell=True)
while True:
if self.use_stderr:
s = self.pipe.stderr.readline()
else:
s = self.pipe.stdout.readline()
if len(s) == 0:
break
q = self.receiverre.search(s)
if q:
self.value = int(q.groups()[0])
def run(self):
"""
Run rsync with progressbar
"""
9 years ago
self.parent.startTask(self.title, progress=True)
self.pipe = subprocess.Popen(self.rsyncstr + self.extraopts,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, shell=True)
oldpercent = 0
while True:
if self.use_stderr:
s = self.pipe.stderr.readline()
else:
s = self.pipe.stdout.readline()
if len(s) == 0:
break
if s.startswith("rsync:"):
self.errors.append(s)
q = self.receiverre.search(s)
if q:
if not self.copyStarting:
self.parent.endTask()
9 years ago
self.parent.startTask(self.secondtitle, progress=True)
# self.parent.setProgress(0)
self.copyStarting = True
self.value = int(q.groups()[0])
if self.maximum:
9 years ago
newvalue = int(100 * self.value / self.maximum)
newvalue = min(newvalue, 99)
if newvalue > oldpercent:
self.parent.setProgress(newvalue)
oldpercent = newvalue
self.parent.setProgress(100)
def close(self):
self.parent.endTask()
9 years ago
class commandServer(color_print):
"""Server command object"""
def setServerCommand(self, command, varsCommand, fileConfig,
uid=None, gid=None):
"""Set server command"""
pathConfig = os.path.split(fileConfig)[0]
# create user config directory
if not os.path.exists(pathConfig):
os.makedirs(pathConfig)
if not uid is None and not gid is None:
os.chown(pathConfig, uid, gid)
objConfig = iniParser(fileConfig)
9 years ago
varsRun = {"run": "on"}
varsRun.update(varsCommand)
9 years ago
if not objConfig.setVar(["command"] + command, varsRun):
raise ClientError(_("Failed to write variables in file %s") \
% fileConfig)
if not uid is None and not gid is None:
os.chown(fileConfig, uid, gid)
return True
def checkUserPwdLDAP(self, server, userDN, password):
"""Проверка пароля Unix пользователя на сервере"""
9 years ago
ldapInit = ldap.initialize("ldap://%s" % server)
errMessage = ""
9 years ago
try:
ldapInit.bind_s(userDN, password)
except ldap.INVALID_CREDENTIALS:
errMessage = _("Wrong password")
return False, errMessage
except ldap.LDAPError, e:
errMessage = e[0]['desc']
return False, errMessage
return True, errMessage
def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False):
"""Получить пароль у пользователя
options - полученные опции командной строки
optDialog - опция командной строки для вывода диалога для получения
пароля
optStdIn - опция командной строки для получения пароля из
стандартного ввода (stdin)
pwDialog - структура для вывода приглашения в режиме диалога
"""
if optStdIn and options.has_key(optStdIn):
pwdA = sys.stdin.readline().rstrip()
pwdB = sys.stdin.readline().rstrip()
elif optDialog and options.has_key(optDialog):
if not pwDialog:
pwDialog = [_("New password"),
_("Retype the new password")]
9 years ago
pwdA = getpass.getpass(pwDialog[0] + _(":"))
pwdB = getpass.getpass(pwDialog[1] + _(":"))
else:
return ""
if not pwdA or not (pwdA == pwdB):
self.printERROR(_("ERROR") + _(": ") + \
_("passwords do not match"))
return False
userPwd = pwdA
return userPwd
9 years ago
class Client(commandServer, encrypt, Desktop):
"""
Клиентский модуль работы с доменом
"""
# user calculate config directory
pathConfig = ".calculate"
# client soft config file
configFileSoft = os.path.join(pathConfig, "ini.env")
# client config file
configFileDesktop = os.path.join(pathConfig, "desktop.env")
# client config for server
configFileServer = os.path.join(pathConfig, "server.env")
# file list of user profile
listTemplFile = os.path.join(pathConfig, "files.txt")
# files which not cleaning from user home directory
skipHomeFile = ["Laptop", "Home", "Disks", "Share", "FTP",
configFileDesktop]
# option dict of services from /var/calculate/remote/server.env
optionsInfo = {}
# convertation object from old remote env
convObj = None
# private user files
privateFiles = [configFileServer]
# private user directories
9 years ago
privateDirs = []
def init(self):
self.domainnames = {}
def removeVars(self):
"""
Remove domain variables. Useing on remove package
and undomain action
"""
self.clVars.Delete("cl_remote_host", "local")
self.clVars.Delete("cl_remote_pw", "local")
self.clVars.Delete("os_remote_auth")
self.clVars.Set("cl_remote_host", "", True)
self.clVars.Set("cl_remote_pw", "", True)
self.clVars.Set("os_remote_auth", "", True)
return True
9 years ago
def mountSambaRes(self, host, userName, userPwd, res, rpath, uid=None,
gid=None):
"""
Подключить samba ресурс
"""
mountCmd = checkUtils('/bin/mount')
addon = ""
if cmpVersion(self.clVars.Get('cl_cifs_ver'), "2.05") >= 0:
addon = ",nomapposix"
cache_method = self.clVars.Get('cl_cifs_cache')
domain = self.get_server_domainname(host)
cifs_mount_vers = self.clVars.Get('cl_cifs_mount_vers')
if not uid is None:
# mount by uid
9 years ago
p = process(mountCmd, "-t", "cifs", "-o",
"cache=%s,domain=%s,user=%s,uid=%d,gid=%d,noperm%s" % (
cache_method, domain,
userName, uid, gid, addon),
"//%s/%s" % (host, res), rpath,
envdict={"PASSWD": userPwd}, stderr=STDOUT)
return p.success()
else:
if cmpVersion(self.clVars.Get('cl_cifsutils_ver'), "6.9_rc73") >= 0:
addon += ",forcemandatorylock"
if rpath in ("unix", "remote_profile"):
versopt = ",vers=%s" % cl_cifs_mount_vers
else:
versopt = ""
9 years ago
p = process(mountCmd, "-t", "cifs", "-o",
"cache=%s,domain=%s%s,user=%s%s" % (cache_method,
domain, versopt, userName, addon),
"//%s/%s" % (host, res), rpath,
envdict={"PASSWD": userPwd}, stderr=STDOUT)
return p.success()
def get_server_domainname(self, host):
vardomain = self.clVars.Get('cl_remote_domain')
if vardomain:
return vardomain
if host not in self.domainnames:
try:
self.domainnames[host] = Samba().get_server_domainname(host)
except SambaError as e:
raise ClientError(str(e))
return self.domainnames[host]
9 years ago
def mountSleepRes(self, host, userName, userPwd, res, rpath, uid=None,
gid=None):
"""
Подключить пользовательский ресурс
"""
9 years ago
for waittime in [0.5, 2, 5]:
if not self.mountSambaRes(host, userName, userPwd, res, rpath,
uid, gid):
# check on mount path
if isMount(rpath):
if not self.umountSleepPath(rpath):
return False
# wait
time.sleep(waittime)
else:
return True
return False
def syncUser(self, uid, gid, userHome, sync, remoteProfile,
9 years ago
host="default", skipList=()):
"""
Синхронизация профиля пользователя
"""
execStr = ""
skipPaths = self.clVars.Get("cl_sync_skip_path")
if not skipPaths:
self.printERROR(
9 years ago
_("Variable 'cl_sync_skip_path' empty") % userHome)
return False
deletePaths = self.clVars.Get("cl_sync_del_path")
if not deletePaths:
deletePaths = []
9 years ago
excludePaths = " ".join(map(
lambda x: '--exclude="/%s"' % x.replace('"', "").replace(
"'", ""),
skipPaths + deletePaths + list(skipList)))
if sync == "login":
9 years ago
if os.path.exists(userHome) and \
os.path.exists(remoteProfile):
filterPath = " ".join(map(
lambda x: '--filter="P /%s"' % x.replace(
'"', "").replace("'", ""),
skipPaths))
execStr = ('/usr/bin/rsync --delete-excluded --delete %s %s '
'-rlptgo -x -v -v -v %s/ %s/'
% (excludePaths, filterPath,
remoteProfile, userHome))
elif sync == "logout":
9 years ago
if os.path.exists(userHome) and os.listdir(userHome) and \
os.path.exists(remoteProfile):
execStr = ('/usr/bin/rsync --delete-excluded --delete %s '
'-rlpt -x -v -v -v %s/ %s/' % (
excludePaths, userHome, remoteProfile))
else:
raise ClientError(
9 years ago
_("Method syncUser: wrong option sync=%s") % str(sync))
if execStr:
9 years ago
host = "<i>" + host + "</i>"
if sync == "login":
9 years ago
rsync = RsyncProgressBar(
_("Fetching the file list from %s") % host,
_("Fetching the user profile from %s") % host,
execStr, self, rsyncver=self.clVars.Get('cl_rsync_ver'))
else:
9 years ago
rsync = RsyncProgressBar(
_("Sending the file list to %s") % host,
_("Uploading the user profile to %s") % host,
execStr, self, rsyncver=self.clVars.Get('cl_rsync_ver'))
pathConfig = os.path.join(remoteProfile,
self.pathConfig)
# remove old ini file
9 years ago
prevIniFile = os.path.join(remoteProfile, ".calculate.ini")
if os.path.exists(prevIniFile):
os.remove(prevIniFile)
# create home directory
if not os.path.exists(pathConfig):
9 years ago
self.createUserDirectory(pathConfig, uid, gid)
numfiles = 0
configFileName = os.path.join(remoteProfile, self.configFileDesktop)
if sync == "login":
# get rsync files
try:
9 years ago
numfiles = iniParser(
configFileName).getVar('rsync', 'files')
if numfiles is False:
if os.path.exists(configFileName):
os.remove(configFileName)
numfiles = 0
else:
numfiles = int(numfiles)
9 years ago
except Exception:
numfiles = 0
rsync.maximum = numfiles
if sync == "login":
rsync.run()
if sync == "logout":
rsync.run()
try:
9 years ago
if iniParser(configFileName).setVar(
'rsync', {'files': rsync.getFilesNum()}):
os.chmod(configFileName, 0600)
9 years ago
os.chown(configFileName, uid, gid)
except Exception:
pass
rsync.close()
try:
9 years ago
if iniParser(configFileName).setVar(
'rsync', {'exitcode': rsync.getExitCode()}):
os.chmod(configFileName, 0600)
9 years ago
os.chown(configFileName, uid, gid)
except Exception:
pass
if rsync.getExitCode() != 0:
self.printERROR(rsync.getErrMessage())
self.printERROR(_("Failed to execute rsync") + " " + \
9 years ago
str(sync))
return False
# change permissions
changeDirs = [userHome, remoteProfile]
9 years ago
for changeDir in filter(path.exists, changeDirs):
# get directory permissions
mode = getModeFile(changeDir, mode="mode")
# if permission wrong( not 0700) then change it
if mode != 0700:
9 years ago
os.chmod(changeDir, 0700)
return True
def clearHomeDir(self, homeDir):
"""
Очистить домашнюю директорию
"""
skipHomeFile = list(set(self.skipHomeFile) |
set(self.clVars.Get('cl_moved_skip_path')))
rmFiles = list(set(os.listdir(homeDir)) - set(skipHomeFile))
for rmFile in rmFiles:
9 years ago
delFile = os.path.join(homeDir, rmFile)
if os.path.islink(delFile):
os.unlink(delFile)
elif os.path.isfile(delFile):
os.remove(delFile)
elif os.path.isdir(delFile):
if not removeDir(delFile):
return False
elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]):
os.remove(delFile)
self.printSUCCESS(_("Cleaning the local user profile"))
return True
9 years ago
def syncLoginProfile(self, host, uid, gid, homeDir, resourceName,
profileName, clearHomeDir=True):
"""
Получить профиль пользователя из домена
"""
homeProfile = path.join(
self.clVars.Select('cl_client_user_mount_path',
where='cl_client_user_mount_name',
9 years ago
eq=resourceName, limit=1),
profileName)
# if currect server has any files then sync it
9 years ago
if any(x not in ('.calculate',) for x in listDirectory(homeProfile)):
if not self.syncUser(uid, gid, homeDir, "login",
homeProfile, host=host):
return False
# remove home directory on workstation
else:
if clearHomeDir:
# clean home directory
if not self.clearHomeDir(homeDir):
return False
return True
9 years ago
def syncLogoutProfile(self, host, uid, gid, homeDir, resourceName,
profileName, skipList):
"""
Закачать профиль пользователя в домен
"""
homeProfile = path.join(
self.clVars.Select('cl_client_user_mount_path',
where='cl_client_user_mount_name',
9 years ago
eq=resourceName, limit=1),
profileName)
# if currect server has any files then sync it
if listDirectory(homeDir):
if not path.exists(homeProfile):
9 years ago
self.createUserDirectory(homeProfile, uid, gid)
if not self.syncUser(uid, gid, homeDir, "logout",
9 years ago
homeProfile, host=host, skipList=skipList):
return False
return True
9 years ago
def strftime(self, timeobj, dateformat="%Y-%m-%d %H:%M:%S"):
return time.strftime(dateformat, timeobj)
def packRemote(self, resourceName, localTime, currentTime, profileName,
uid, gid):
"""
Запаковать удаленный профиль
"""
remoteProfilePath = path.join(
9 years ago
self.clVars.Select('cl_client_user_mount_path',
where='cl_client_user_mount_name',
eq=resourceName,
limit=1), profileName)
fileServer = path.join(remoteProfilePath,
9 years ago
Client.configFileServer)
if not localTime:
return False
9 years ago
varsConfig = {"arch_date": self.strftime(localTime),
"curr_time": currentTime}
9 years ago
self.setServerCommand(["pack"], varsConfig, fileServer)
fileDesktop = path.join(remoteProfilePath,
9 years ago
Client.configFileDesktop)
self.setVarToConfig(uid, gid, "main", {"status_sync": "success"},
fileDesktop)
return self.umountRemoteUserRes(False, resourceName)
def waitingArchFile(self, packTime, profileName, resourceName):
"""
Ожидание архива профиля от домена
"""
9 years ago
arch_fn_success, arch_fn_process = self._getArchNames(
packTime, profileName, resourceName)
def arch_exists():
return any(os.path.exists(x) for x in (arch_fn_process,
arch_fn_success))
for sleeptime in [0.1, 0.2, 0.5]:
# archive in packing process found
9 years ago
if arch_exists():
break
time.sleep(sleeptime)
else:
return False
# wait finish packing
9 years ago
if arch_exists():
while arch_exists():
for waittime in [0.5, 1, 2]:
if path.exists(arch_fn_success):
return True
try:
9 years ago
startSize = os.stat(arch_fn_process).st_size
time.sleep(waittime)
9 years ago
if startSize != os.stat(arch_fn_process).st_size:
break
9 years ago
except OSError:
if path.exists(arch_fn_success):
return True
else:
return False
return False
def unpackArch(self, homeDir, packTime, profileName, resourceName):
"""
Распаковать архив с профилем
"""
9 years ago
def fileReader(fileName, stdin, maxSize):
"""
Прочитать файл по блокам
"""
self.addProgress()
fd = os.open(fileName, os.O_RDONLY)
currSize = 0
# default buffer size
9 years ago
buffSize = 131072
dataBlock = os.read(fd, buffSize)
while dataBlock:
currSize += len(dataBlock)
9 years ago
self.setProgress(currSize * 100 / maxSize)
stdin.write(dataBlock)
dataBlock = os.read(fd, buffSize)
stdin.close()
os.close(fd)
9 years ago
archFile, drop = self._getArchNames(packTime, profileName, resourceName)
archiveSize = os.stat(archFile).st_size
9 years ago
# os.system('tar tf %s'%archFile)
execStr = "tar -C '%s' -xzf -" % homeDir
# запустить процесс распаковки
pipe = subprocess.Popen(execStr,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
env=os.environ, shell=True)
9 years ago
try:
# прочитать в конвеер
fileReader(archFile, pipe.stdin, archiveSize)
self.endTask()
finally:
ret = pipe.wait()
pipe.stdout.close()
pipe.stderr.close()
if ret:
return False
removedFileList = path.join(
9 years ago
self.clVars.Select('cl_client_user_mount_path',
where='cl_client_user_mount_name',
eq=resourceName, limit=1), profileName,
self.listTemplFile)
return self.removeFilesInProfile(homeDir, removedFileList)
def cleanArchs(self, packTime, profileName, resourceName):
"""
Удалить архивы использованные для синхронизации пользовательского
профиля
"""
9 years ago
for rmFile in filter(lambda x: x and path.exists(x),
self._getArchNames(packTime, profileName,
resourceName)):
os.remove(rmFile)
return True
9 years ago
def _getArchNames(self, packTime, profileName, resourceName):
"""
Получить имена файлов архивов по времени, профилю и ресурсу
"""
remoteProfile = self.clVars.Select('cl_client_user_mount_path',
9 years ago
where='cl_client_user_mount_name',
eq=resourceName, limit=1)
extSuccess = "gz"
extProcess = "process"
9 years ago
packTime = packTime.replace(".", "_")
archPathTmp = os.path.join(remoteProfile, "profile.%s.%s.tar"
9 years ago
% (profileName, packTime))
# result archive file name
9 years ago
archPathSuccess = "%s.%s" % (archPathTmp, extSuccess)
# archive in pack process
9 years ago
archPathProcess = "%s.%s" % (archPathTmp, extProcess)
return archPathSuccess, archPathProcess
def setVarToConfig(self, uid, gid, nameSection, varsDict, configFileName):
"""Записывает переменную в файл конфигурации"""
# Создаем директорию для конфигурационных файлов
pathConfig = os.path.split(configFileName)[0]
if not os.path.exists(pathConfig):
9 years ago
self.createUserDirectory(pathConfig, uid, gid)
try:
if iniParser(configFileName).setVar(nameSection, varsDict):
os.chmod(configFileName, 0600)
9 years ago
os.chown(configFileName, uid, gid)
except Exception:
return False
return True
9 years ago
def createUserDirectory(self, userdir, uid, gid):
"""
Создать директорию с правами пользователя
"""
if not os.path.exists(userdir):
try:
pdir = os.path.dirname(userdir)
if not os.path.exists(pdir):
self.createUserDirectory(pdir, uid, gid)
os.mkdir(userdir)
os.chown(userdir, uid, gid)
9 years ago
os.chmod(userdir, 0700)
except OSError:
9 years ago
raise ClientError(_("Failed to create the directory") + ". " +
_("Permission denied: '%s'") % userdir)
return True
9 years ago
def mountUserDomainRes(self, userName, userPwd, uid, gid, *resources):
"""
Mount all local samba resource
"""
9 years ago
for rpath, res, host in \
self.clVars.Select(['cl_client_user_mount_path',
'cl_client_user_mount_resource',
'cl_client_user_mount_host'],
where='cl_client_user_mount_name',
_in=resources):
if not rpath:
continue
9 years ago
self.createUserDirectory(rpath, uid, gid)
if isMount(rpath):
continue
else:
if not self.mountSleepRes(host, userName, userPwd,
9 years ago
res, rpath, uid, gid):
raise ClientError(
9 years ago
_("Failed to mount the Samba volume [%s]") % res)
else:
return True
9 years ago
def setSyncStatus(self, userDir, uid, gid, status):
"""
Установить статус синхронизации
"""
# сохранение текущего статуса перед изменением
self.clVars.Get('cl_client_sync_status')
9 years ago
fileDesktop = path.join(userDir, Client.configFileDesktop)
return self.setVarToConfig(uid, gid, "main",
9 years ago
{"status_sync": status}, fileDesktop)
9 years ago
def tarSymLinks(self, uid, gid, userHome, delPath, skipPath):
"""
Создать tar архив с симлинками
"""
9 years ago
linkArch = pathJoin(userHome, ".calculate/links.tar.bz2")
try:
9 years ago
tarLinks(userHome, linkArch, skip=delPath + skipPath)
if path.exists(linkArch):
9 years ago
os.chown(linkArch, uid, gid)
except Exception:
return False
return True
9 years ago
def unpackLinks(self, userHome):
"""
Распаковать архив с симлинками
"""
9 years ago
linksArch = path.join(userHome, ".calculate/links.tar.bz2")
try:
if os.path.exists(linksArch):
tf = tarfile.open(linksArch)
tf.extractall(userHome)
tf.close()
9 years ago
except Exception:
return False
return True
def moveHomeDir(self, userHome, movedDir, resourceName, skipPaths):
"""
Переместить непрофильные файлы из корня домашней директории
в Home/Moved
"""
desktopDir = "Desktop"
resourcePath = self.clVars.Select('cl_client_user_mount_path',
9 years ago
where='cl_client_user_mount_name',
eq=resourceName,
limit=1)
if not isMount(resourcePath):
9 years ago
raise ClientError(_("Unable to mount %s") % resourcePath)
movedPath = path.join(resourcePath, movedDir)
9 years ago
movedLink = path.join(userHome, movedDir)
if not skipPaths:
skipPaths = ['Laptop', 'Disks', 'Share', 'Home',
'Moved', 'FTP', 'Desktop']
filesAndDir = filter(lambda x: not (
9 years ago
path.islink(x) or
x[len(userHome) + 1:].startswith('.') or
x[len(userHome) + 1:] in skipPaths),
listDirectory(userHome, fullPath=True))
filesDir = []
for fd in filesAndDir:
if os.path.islink(fd):
os.unlink(fd)
else:
filesDir.append(fd)
# find files in Desktop
9 years ago
pathDesktop = path.join(userHome, desktopDir)
filesDirDesk = filter(lambda x: (not path.islink(x) and
9 years ago
not path.split(x)[1].startswith(
'.') and
not x.rpartition('.')[2] == 'desktop'),
listDirectory(pathDesktop, fullPath=True))
filesDir += filesDirDesk
9 years ago
if not filesDir or not path.exists(resourcePath):
# remove empty Moved folder
if path.exists(movedPath) and not listDirectory(movedPath):
os.rmdir(movedPath)
# remove link to Moved in home directory
if path.islink(movedLink) and not path.exists(movedPath):
os.unlink(movedLink)
return True
if not path.exists(movedPath):
os.mkdir(movedPath)
9 years ago
directFile = path.join(movedPath, ".directory")
if not path.exists(directFile):
9 years ago
with open(directFile, 'w') as f:
f.write("[Desktop Entry]\nIcon=folder-development")
if not path.exists(movedLink):
9 years ago
os.symlink(movedPath[len(userHome) + 1:], movedLink)
cpCmd, rmCmd = checkUtils('/bin/cp', '/bin/rm')
for fd in filesDir:
9 years ago
if process(cpCmd, '-xr', fd, movedPath).failed():
self.printERROR(_("Failed to copy {ffrom} to {fto}")
9 years ago
.format(ffrom=fd, fto=movedPath))
return False
9 years ago
if process(rmCmd, '-rf', '--one-file-system', fd).failed():
self.printERROR(_("Failed to remove " % fd))
return False
return True
def removeNoiseFiles(self, userHome):
"""
Remove files which hinder dm
"""
noiseFiles = []
for nsFile in noiseFiles:
rmFile = path.join(userHome, nsFile)
if os.path.exists(rmFile):
os.remove(rmFile)
return True
def getPrivateFiles(self, userHome):
"""
Get all private files relative user home directory
"""
privateFiles = []
for privateHomeDir in self.privateDirs:
9 years ago
privateDir = os.path.join(userHome, privateHomeDir)
if os.path.isdir(privateDir):
# .ssh files relative user home directory
9 years ago
privateFiles += map(lambda x: os.path.join(privateHomeDir, x),
filter(lambda x: \
os.path.isfile(
os.path.join(privateDir, x)),
os.listdir(privateDir)))
return self.privateFiles + privateFiles
def removePrivateFiles(self, userHome):
"""
Remove private files
"""
privateFiles = self.getPrivateFiles(userHome)
for prFile in privateFiles:
rmFile = os.path.join(userHome, prFile)
if os.path.exists(rmFile):
os.remove(rmFile)
return True
def copyPrivateFiles(self, serverProfileDir, userHomeDir):
"""
Copy privates files from server to home directory
"""
privateFiles = self.getPrivateFiles(serverProfileDir)
for prFile in privateFiles:
src = os.path.join(serverProfileDir, prFile)
dst = os.path.join(userHomeDir, prFile)
if os.path.exists(src):
dstPath = os.path.dirname(dst)
if not os.path.exists(dstPath):
listElPath = []
for el in filter(lambda x: x,
9 years ago
dstPath.partition(userHomeDir)[2].split(
"/")):
listElPath.append(el)
joinPath = "/".join(listElPath)
dPath = os.path.join(userHomeDir, joinPath)
if not os.path.exists(dPath):
sPath = os.path.join(serverProfileDir, joinPath)
sMode, sUid, sGid = getModeFile(sPath)
9 years ago
os.mkdir(dPath, sMode)
os.chown(dPath, sUid, sGid)
copy2(src, dst)
sUid, sGid = getModeFile(src, mode="owner")
os.chown(dst, sUid, sGid)
return True
def getListFilesProfile(self, homeDir):
"""
Generation file list in user home directory, exclude mount dirs
"""
9 years ago
lHomeDir = len(homeDir) + 1
return map(lambda x: x[lHomeDir:],
find(homeDir, onefilesystem=True))
def removeFilesInProfile(self, homeDir, pathListFile):
"""
Удалить файлы которые отсутствуют в пользовательском профиле
"""
# read file list from config
try:
filesProfileTxt = readFile(pathListFile)
9 years ago
except IOError:
return False
9 years ago
listFilesProfile = filter(lambda x: x.strip(),
filesProfileTxt.split("\n"))
filesProfile = set(listFilesProfile)
# get files in home directory
listFilesHome = self.getListFilesProfile(homeDir)
if listFilesHome is False:
return False
filesHome = set(listFilesHome)
filesRemove = list(filesHome - filesProfile)
try:
9 years ago
for rmFile in sorted(filesRemove, reverse=True):
rmPath = os.path.join(homeDir, rmFile)
if path.ismount(rmPath):
continue
if os.path.islink(rmPath):
os.unlink(rmPath)
elif os.path.isfile(rmPath):
os.remove(rmPath)
elif os.path.isdir(rmPath):
os.rmdir(rmPath)
else:
os.remove(rmPath)
9 years ago
except Exception:
return False
return True
def getRunCommandsWithEnv(self):
"""List run program"""
9 years ago
def getCmd(procNum):
9 years ago
cmdLineFile = '/proc/%s/cmdline' % procNum
environFile = '/proc/%s/environ' % procNum
try:
if os.path.exists(cmdLineFile) and \
9 years ago
os.path.exists(environFile):
return (readFile(cmdLineFile).strip(),
readFile(environFile).strip())
9 years ago
except (OSError, IOError):
pass
9 years ago
return "", ""
if not os.access('/proc', os.R_OK):
return []
return map(getCmd,
9 years ago
filter(lambda x: x.isdigit(),
os.listdir('/proc')))
def clearUserKey(self, userName):
"""
Clear user key from kernel
"""
# find user key in kernel and check on relogout
if getKey(userName) and not \
9 years ago
filter(lambda x: "xdm/xdm\x00--login" in x[0] and
("USER=%s" % userName) in x[1],
self.getRunCommandsWithEnv()):
# clean
ret = clearKey(userName)
if ret == 0:
return True
else:
9 years ago
self.printERROR(_("Failed to clear the kernel key for user %s") \
% userName)
return False
return True
9 years ago
def setLogoutDate(self, homeDir, uid, gid):
"""
Установить дату выхода из сеанса
"""
9 years ago
configFileName = path.join(homeDir, self.configFileDesktop)
currentDateStr = self.strftime(time.localtime())
return self.setVarToConfig(uid, gid, "main",
9 years ago
{"date_logout": currentDateStr},
configFileName)
9 years ago
def umountRemoteUserRes(self, removeEmpty, *resourceNames):
"""
Отключить пользовательский ресурс
"""
retRes = True
for resourceName in resourceNames:
resourcePath = self.clVars.Select('cl_client_user_mount_path',
9 years ago
where='cl_client_user_mount_name',
eq=resourceName,
limit=1)
if resourcePath:
result = self.umountSleepPath(resourcePath)
retRes &= result
if (result and removeEmpty and path.exists(resourcePath) and
9 years ago
not listDirectory(resourcePath)):
os.rmdir(resourcePath)
resourcePath = os.path.dirname(resourcePath)
if (resourceName in ("unix", "remote_profile") and
path.exists(resourcePath) and
not listDirectory(resourcePath)):
os.rmdir(resourcePath)
return retRes
9 years ago
def umountUserRes(self, umountPaths):
"""
Отключить пользовательские ресурсы
"""
for umountPath in umountPaths:
if not self.umountSleepPath(umountPath):
return False
self.umountRemoteUserRes(True,
9 years ago
*self.clVars.Get('cl_client_user_mount_name'))
return True
def getDefaultRunlevelDaemons(self):
"""
Получить все службы из default уровня загрузки
"""
rcUpdateCmd = checkUtils("/sbin/rc-update")
9 years ago
p = process(rcUpdateCmd, "show")
if p.success():
# получить список названии служб default уровня
9 years ago
return map(lambda x: x[0].strip(),
# [\s*<название_службы>\s*,\s*default\s*]
filter(lambda x: len(x) > 1 and "default" in x[1],
# [\s*<название_службы>\s*,\s*уровень запуска\s*]
map(lambda x: x.split('|'),
# \s*<название_службы>\s*|
# \s*<уроверь запуска>?\s*
p.readlines())))
else:
raise ClientError(_("ERROR") + _(": ") + p.read())
def delDaemonAutostart(self, daemon):
"""
Удалить службу из автозапуска
"""
9 years ago
rcUpdateCmd = checkUtils('/sbin/rc-update')
defaultDaemons = self.getDefaultRunlevelDaemons()
if daemon in defaultDaemons:
9 years ago
p = process(rcUpdateCmd, 'del', daemon, 'default')
if p.success():
return True
else:
self.printERROR(_("ERROR") + _(": ") + p.read())
self.printERROR(_("Failed to delete from the default runlevel"))
return False
return True
def addDaemonAutostart(self, daemon):
"""
Добавить службу в автозагрузку
"""
9 years ago
rcUpdateCmd = checkUtils('/sbin/rc-update')
if daemon in self.getDefaultRunlevelDaemons():
return True
9 years ago
p = process(rcUpdateCmd, 'add', daemon, 'default')
if p.success():
return True
else:
self.printERROR(_("ERROR") + _(": ") + p.read())
self.printERROR(_("Failed to add to the default runlevel"))
return False
def getInfoService(self, service, option, envFile=False):
"""
Получить параметр севирса из удаленного env
"""
if not envFile:
9 years ago
if self.convObj is None:
# file /var/calculate/remote/server.env
envFile = self.clVars.Get("cl_env_server_path")
if os.access(envFile, os.R_OK):
self.convObj = False
elif os.access("/var/calculate/remote/calculate.env", os.R_OK):
self.convObj = convertEnv()
if self.convObj:
return self.convObj.getVar(service, option)
if not self.optionsInfo:
if not envFile:
# file /var/calculate/remote/server.env
envFile = self.clVars.Get("cl_env_server_path")
optInfo = self.clVars.getRemoteInfo(envFile)
if optInfo is False:
return False
if optInfo:
self.optionsInfo = optInfo
value = ''
if service in self.optionsInfo and option in self.optionsInfo[service]:
9 years ago
value = self.optionsInfo[service][option]
return value
9 years ago
def checkDomainServer(self, domainName, netDomain):
"""
Проверить указанный адрес на то, что он может быть доменом
"""
# get domain name
if "." in domainName:
domain = domainName
else:
import socket
9 years ago
domain = "%s.%s" % (domainName, netDomain)
try:
gethostbyname(domain)
except socket.gaierror as e:
domain = domainName
# check domain by ping
9 years ago
for i in range(0, 1):
try:
9 years ago
Pinger().ping(domain, 1000, 16)
break
except IPError as e:
pass
else:
9 years ago
self.printERROR(_("Server %s does not respond") % domain)
return False
smbClientCmd = checkUtils('/usr/bin/smbclient')
p = process(smbClientCmd, "-N", "//{}/remote".format(domain),
stderr=STDOUT)
if "NT_STATUS_ACCESS_DENIED" in p.read():
self.get_server_domainname(domain)
return True
else:
9 years ago
self.printERROR(_("Samba server not found in %s") % domain)
return False
def getDomainPassword(self, host):
"""
Получить пароль для ввода в домен
"""
9 years ago
def passwdQueue():
remotePw = self.clVars.Get('cl_remote_pw')
if remotePw:
yield remotePw
if remotePw:
self.printERROR(_("Wrong password"))
9 years ago
yield self.askPassword(
_("Domain password for the desktop"), False)
self.printERROR(_("Wrong password"))
samba = Samba()
for pwdRemote in passwdQueue():
pwdRemote = pwdRemote or ""
domain = self.get_server_domainname(host)
if samba.password_check("client", pwdRemote, host, "remote",
domain):
9 years ago
self.clVars.Set('cl_remote_pw', pwdRemote)
return True
return False
9 years ago
def writeClientVars(self, domain, currentVersion, pwdRemote):
"""
Записать переменные переменные клиента
"""
# write domain to config
self.clVars.Write("os_remote_auth", domain, True)
# write current program version
self.clVars.Write("cl_remote_host", domain, True, "local")
self.clVars.Write("cl_remote_pw", pwdRemote, True, "local")
return True
9 years ago
def checkDomainInfo(self, domain):
"""
Проверить наличие настроек на сервере для конфигурации LDAP
"""
servDn = self.getInfoService("ldap", "services_dn")
unixDn = self.getInfoService("unix", "dn")
bindDn = self.getInfoService("unix", "bind_dn")
bindPw = self.getInfoService("unix", "bind_pw")
sambaHost = self.getInfoService("samba", "host")
# check info from server
if not (servDn and unixDn and bindDn and bindPw):
raise ClientError(_("Info not found on the server") + _(": ") +
9 years ago
_(
"services DN or unix DN or bind DN or bind password"))
self.clVars.Set("os_remote_auth", domain)
try:
self.getDomainPassword(sambaHost)
except ClientError:
self.printWARNING(
_("Failed to authorize on %s") % sambaHost)
self.printWARNING(
_("Wrong value of variable sr_samba_host in %s") %
"/var/calculate/remote/calculate.env")
return True
def applyClientTemplates(self, hostAuth=""):
"""
Применить шаблоны клиента
Установка события домен/не домен по удаленному хосту
Установка переменной os_remote_auth, необходимой для корректного
выполнения шаблонов
Выполнение шаблонов
Установка необходимых переменных в calculate.env
"""
if hostAuth:
# add to domain
9 years ago
self.clVars.Set("cl_action", "domain", True)
else:
# del from domain
9 years ago
self.clVars.Set("cl_action", "undomain", True)
self.clVars.Set("os_remote_auth", hostAuth)
# apply system templates
9 years ago
self.applyTemplates(None, False)
if hostAuth:
self.printSUCCESS(_("The workstation was configured for work "
9 years ago
"in the domain"))
self.clVars.Write("os_remote_auth", hostAuth, True)
else:
self.printSUCCESS(_("The workstation was configured for work "
9 years ago
"outside the domain"))
self.clVars.Delete("os_remote_auth")
return True
def cDelLdapSysUsersAndSyncCache(self):
"""
Удалить LDAP пользователей из системы (/etc/passwd и т.д.) и
синхронизировать кэш
"""
9 years ago
cacheObj = userCache(self)
if not cacheObj.deleteCacheUsersFromSystem():
return False
if not cacheObj.syncCacheToLdap():
return False
return True
def cAddCacheUsersFromSystem(self):
"""
Добавить кэш пользователей из системы
"""
9 years ago
cacheObj = userCache(self)
if not cacheObj.addCacheUsersFromSystem():
return False
return True
def cAddUserToCache(self, userName, userPwd):
"""
Добавить пользователя в кэш
"""
9 years ago
cacheObj = userCache(self)
pwdHash = self.getHashPasswd(userPwd, "shadow_ssha256")
if not cacheObj.addUserToCache(userName, pwdHash):
return False
return True
def cDelLdapSysUsersAndClearCache(self):
"""
Удалить LDAP пользователей из системы и очистить кэш
"""
9 years ago
cacheObj = userCache(self)
if not cacheObj.deleteCacheUsersFromSystem():
return False
if not cacheObj.clearCache():
return False
return True
9 years ago
def mountRemoteRes(self, pwdRemote, pathRemote, *domains):
"""
Подключить удаленный ресурс remote
"""
# первый домен из списка
9 years ago
domain = reduce(lambda x, y: x if x else y, domains, "")
foundMountRemote = isMount(pathRemote)
if foundMountRemote:
9 years ago
self.printWARNING(_("Samba volume [%s] mounted") % "remote")
return True
else:
if not (domain and pwdRemote):
9 years ago
self.printERROR(_("Variable not found") +
_(": ") + "cl_remote_pw")
return False
if not os.path.exists(pathRemote):
os.makedirs(pathRemote)
9 years ago
if not self.mountSambaRes(domain, "client", pwdRemote,
"remote", pathRemote):
self.printERROR(
9 years ago
_("Failed to mount the Samba volume [%s]") % "remote")
return False
self.printSUCCESS(_("Samba volume [%s] mounted") % "remote")
return True
9 years ago
def clientPasswd(self, userLogin, uid, gid, homeDir, password, curPassword):
"""
Изменить пароль пользователя. Пароль пользователя меняется на сервере
после выхода из сеанса
"""
9 years ago
varsConfig = {"unix_hash": self.getHashPasswd(password, "ssha"),
"samba_lm_hash": self.getHashPasswd(password, "lm"),
"samba_nt_hash": self.getHashPasswd(password, "nt"),
"samba_nt_hash_old": self.getHashPasswd(curPassword,
"nt")}
if filter(lambda x: not x, varsConfig.values()):
return False
# ~/.calculate/server.env
fileConfig = os.path.join(homeDir, self.configFileServer)
try:
9 years ago
res = self.setServerCommand(["passwd_samba"], varsConfig,
fileConfig,
uid, gid)
except OSError as e:
if e.errno == 13:
self.printERROR(_("Permission denied"))
return False
9 years ago
return False
return res
def get_syncer(self, remotehost, user, pwd):
return ProfileSyncer(remotehost, 2009, user, pwd)
def checkSync(self, remotehost):
status = self.get_syncer(remotehost, None, None).check()
self.clVars.Set('cl_client_rsync_profile_set',
"on" if status else "off",
force=True)
return status
def syncLoginProfileNew(self, host, username, pwd,
uid, gid, homeDir,
profileName, replHost=False,
clearHomeDir=True):
"""
Получить профиль пользователя из домена
"""
ps = self.get_syncer(host, username, pwd)
# if currect server has any files then sync it
remoteProfile = "::profile/{}".format(profileName)
if replHost:
remoteOpts = ("-c", "--zc=zstd")
else:
remoteOpts = ()
if not self.syncUserNew(ps, uid, gid, homeDir, "login",
remoteProfile, host=host,
rsyncopts=remoteOpts):
error_output = ps.output
if remoteOpts and re.search(r"(on remote machine.*unknown option|"
"unknown compress name:)",
error_output):
if self.syncUserNew(ps, uid, gid, homeDir, "login",
remoteProfile, host=host):
return True
if re.search(r'change_dir "\?{resPath}" \(in {resName}\) '
'failed: No such file or directory'.format(
resPath=profileName,
resName="profile"), error_output) or \
not ps.exists(remoteProfile):
if clearHomeDir:
# clean home directory
if not self.clearHomeDir(homeDir):
return False
return True
else:
self.printERROR(error_output)
self.printERROR(_("Failed to execute rsync"))
return False
return True
def syncLogoutProfileNew(self, host, username, pwd,
uid, gid, homeDir,
profileName, skipList):
"""
Закачать профиль пользователя в домен
"""
skipList = [os.path.relpath(x, homeDir) for x in skipList]
ps = self.get_syncer(host, username, pwd)
# if currect server has any files then sync it
remoteProfile = "::profile/{}".format(profileName)
if listDirectory(homeDir):
with writeFile(os.path.join(homeDir,".logout")) as f:
f.write("SUCCESS")
if not self.syncUserNew(ps, uid, gid, homeDir, "logout",
remoteProfile, host=host, skipList=skipList):
self.printERROR(ps.output)
self.printERROR(_("Failed to execute rsync"))
return False
return True
def syncUserNew(self, ps, uid, gid, userHome, sync,
remoteProfile,
host="default", skipList=(), rsyncopts=()):
"""
Синхронизация профиля пользователя
"""
execStr = ""
rsyncopts = list(rsyncopts)
skipPaths = self.clVars.Get("cl_sync_skip_path")
if not skipPaths:
self.printERROR(
_("Variable 'cl_sync_skip_path' empty") % userHome)
return False
deletePaths = self.clVars.Get("cl_sync_del_path")
if not deletePaths:
deletePaths = []
excludePaths = ['--exclude=/%s'% x
for x in skipPaths + deletePaths + list(skipList)
if x not in {".logout"} ]
rsyncParams = []
if sync == "login":
if os.path.exists(userHome):
filterPath = ['--filter=P /%s'% x for x in skipPaths
if x not in {".logout"}
]
rsyncParams = ['--delete-excluded',
'--delete'] + excludePaths + filterPath + [
'-rlptgo', '-x'
]
source, target = remoteProfile, userHome
elif sync == "logout":
if os.path.exists(userHome) and os.listdir(userHome):
rsyncParams = ['--delete-excluded',
'--delete'] + excludePaths + [
'-rlpt', '-x'
]
source, target = userHome, remoteProfile
else:
raise ClientError(
_("Method syncUser: wrong option sync=%s") % str(sync))
if rsyncParams:
host = "<i>" + host + "</i>"
if sync == "login":
title = _("Fetching the user profile from %s") % host
elif sync == "logout":
title = _("Uploading the user profile to %s") % host
self.startTask(title)
self.addProgress()
rsyncParams.extend(rsyncopts)
for i in ps.sync("%s/"%source, "%s/"%target, *rsyncParams):
self.setProgress(i)
pathConfig = os.path.join(userHome,
self.pathConfig)
try:
if iniParser(configFileName).setVar(
'rsync', {'exitcode': rsync.getExitCode()}):
os.chmod(configFileName, 0600)
os.chown(configFileName, uid, gid)
except Exception:
pass
if ps.exitstatus != 0:
return False
# change permissions
changeDirs = [userHome]
for changeDir in filter(path.exists, changeDirs):
# get directory permissions
mode = getModeFile(changeDir, mode="mode")
# if permission wrong( not 0700) then change it
if mode != 0700:
os.chmod(changeDir, 0700)
self.endTask()
return True
def update_fastlogin_domain_path(self):
fn = '/var/lib/calculate/calculate-desktop/fastlogin-domain'
try:
with writeFile(fn) as f:
f.write("{}\n".format(
"\n".join(get_fastlogin_domain_path(self.clVars))))
except Exception:
if os.path.exists(fn):
try:
os.unlink(fn)
except Exception:
pass
return True