#-*- coding: utf-8 -*- # Copyright 2010 Calculate Ltd. 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 pwd import subprocess import ldap import time import types import getpass from datavars import DataVarsClient, DataVars, __version__,__app__ from calculate.lib.cl_template import (Template, iniParser,TemplatesError, ProgressTemplate) from calculate.lib.cl_print import color_print from calculate.lib.cl_ldap import ldapUser from calculate.lib.utils.ip import Pinger, isOpenPort, IPError from calculate.lib.utils.files import (runOsCommand, getModeFile, removeDir, isMount, readFile, pathJoin, tarLinks) from calculate.lib.utils.common import (getpathenv, appendProgramToEnvFile, removeProgramToEnvFile) from _cl_keys import getKey, clearKey from calculate.lib.convertenv import convertEnv from calculate.lib.encrypt import encrypt from cl_client_cache import userCache from shutil import copy2 from socket import gethostbyname import tarfile from collections import OrderedDict from calculate.core.server.func import safetyWrapper from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate setLocalTranslate('cl_client3',sys.modules[__name__]) __ = getLazyLocalTranslate(_) class ClientError(Exception): pass 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 def __init__(self, title, secondtitle, rsyncstr, parent,maximum=1): self.title = title self.secondtitle = secondtitle self.maximum = maximum self.rsyncstr = rsyncstr self.parent = parent self.value = 0 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: return self.pipe.stderr.read() return _("RsyncProgressBar: Wrong pipe") def runsilent(self): """ Run rsync without progressbar """ self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) while True: 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 """ self.parent.startTask(self.title,progress=True) self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) oldpercent = 0 while True: s = self.pipe.stdout.readline() if len(s) == 0: break q = self.receiverre.search(s) if q: if not self.copyStarting: self.parent.endTask() self.parent.startTask(self.secondtitle,progress=True) #self.parent.setProgress(0) self.copyStarting = True self.value = int(q.groups()[0]) if self.maximum: 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() class ldapData(ldapUser): """LDAP methods""" def getReplDN(self): """ Get from LDAP last domain server Branch has ((username,systemname,host)) """ usersDN = self.getUsersDN() if usersDN: partDN = "ou=Worked,ou=Replication,ou=LDAP" servicesDN = "ou=Services" baseDN = usersDN.rpartition(servicesDN+",")[2] replDN = self.addDN(partDN, servicesDN, baseDN) return replDN return False def searchPrevHost(self, userName, osLinuxShort): """Find server which user use""" connectData = self.getBindConnectData() if connectData: bindDn, bindPw, host = connectData replDN = self.getReplDN() # find string for service replication branch userAndOsName = "%s@%s"%(userName,osLinuxShort) findAttr = "uid=%s"%userAndOsName # connect to LDAP if not self.ldapConnect(bindDn, bindPw, host): return False resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL, findAttr, ["host"]) return resSearch return False def _gethostbyname(self,hostname): try: return gethostbyname(hostname) except: pass return None def getNameRemoteServer(self,userName, osLinuxShort, curHost): """ Get remote domain hostname or empty if profile is keeped on current server """ searchPrevHost = self.searchPrevHost(userName, osLinuxShort) if searchPrevHost and searchPrevHost[0][0][1].has_key('host'): prevHost = searchPrevHost[0][0][1]['host'][0] else: prevHost = None # get ip address of previous server and current server prevIp = self._gethostbyname(prevHost) curIp = self._gethostbyname(curHost) # if actual profile not found or on local if not prevHost or curIp and prevIp == curIp: return False else: return prevHost def isRepl(self): """ Is on or off replication on server """ connectData = self.getBindConnectData() if connectData: bindDn, bindPw, host = connectData usersDN = self.getUsersDN() partDN = "ou=Replication,ou=LDAP" servicesDN = "ou=Services" baseDN = usersDN.rpartition(servicesDN+",")[2] replDN = self.addDN(partDN, servicesDN, baseDN) findAttr = "ou=Worked" # connect to LDAP if not self.ldapConnect(bindDn, bindPw, host): return False resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL, findAttr, [findAttr.partition("=")[0]]) if resSearch: return True return False 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) varsRun = {"run":"on"} varsRun.update(varsCommand) 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 пользователя на сервере""" ldapInit = ldap.initialize("ldap://%s"%server) errMessage = "" 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 - структура для вывода приглашения в режиме диалога """ userPwd = "" 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")] pwdA = getpass.getpass(pwDialog[0]+":") pwdB = getpass.getpass(pwDialog[1]+":") if (optStdIn and options.has_key(optStdIn)) or\ (optDialog and options.has_key(optDialog)): if not pwdA or not (pwdA == pwdB): self.printERROR (_("ERROR") + ": " +\ _("passwords do not match")) return False userPwd = pwdA return userPwd class Client(commandServer, encrypt): """ Client logic object Has fundamental methods: mountUserResAndSync - mount user resources and sync profile from server umountUserResAndSync - umount user resources and sync profile to server mountRemote - mount remote domain resource delDomain - remove workstation from domain addDomain - add workstation to domain changePasswd - change password on server """ # object for user data in LDAP ldapDataObj = ldapData() # 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 = ["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 privateDirs = [] def __init__(self): self.clVars = None self.clTempl = None def initVars(self,datavars=None): """Primary variables initialization""" if not datavars: self.clVars = DataVarsClient() self.clVars.importClient() self.clVars.flIniFile() else: self.clVars = datavars 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.Delete("os_remote_client") self.clVars.Set("cl_remote_host", "", True) self.clVars.Set("cl_remote_pw", "", True) self.clVars.Set("os_remote_auth", "", True) self.clVars.Set("os_remote_client", "", True) def applyTemplatesFromSystem(self): """Apply templates for system""" if self.clVars.Get('cl_action') == 'domain': self.startTask(_("Appling domain templates"),progress=True) else: self.startTask(_("Appling undomain templates"),progress=True) if self.clTempl: self.closeClTemplate() self.clTempl = ProgressTemplate(self.setProgress,self.clVars, cltObj=True) dirsFiles = self.clTempl.applyTemplates() if self.clTempl.getError(): self.printERROR(self.clTempl.getError().strip()) return False else: self.endTask() return dirsFiles def isTwoSessionsUser(self, userName): """ Check on second user login """ xSession = False foundTwoSession = False resWho = self.execProg("who") if resWho: for pattern in ("%s\s+\d+\s+","%s\s+tty\d+.*:\d+"): reFoundUser = re.compile(pattern%(userName)) for string in resWho: if reFoundUser.search(string): if xSession: self.printERROR(\ _("Second X session for user %s cannot be opened.")\ %userName) return True else: xSession = True return False def isDomain(self): """ Check domain or not workstation """ foundMountRemote = isMount("/var/calculate/remote") remoteHost = self.clVars.Get("cl_remote_host") if remoteHost and foundMountRemote: return True return False def getSyncStatus(self,rpath): """ Get local config 'date' and 'status_sync' or None """ fileConfig = os.path.join(rpath, self.configFileDesktop) # get data from desktop config on success run get by archive if os.path.exists(fileConfig): objConfig = iniParser(fileConfig) data = self.getDataInConfig("main", ["status_sync"], objConfig) if data: return data.get("status_sync",None) return None def getUserMountResources(self, userName, homeDir, flagRemoteServer): """ Get information about remote profile resource """ home = os.path.split(homeDir)[0] dictResources = OrderedDict(# share resource share={"resource":"share", "path":os.path.join(homeDir,"Share")}, profile={"resource":"unix", "path":os.path.join(home,"."+userName)}, # resource - home directory home={"resource":"homes", "path":os.path.join(homeDir,"Home")}) if self.getInfoService("ftp", "host"): # resource ftp dictResources["ftp"] = {"resource":"ftp", "path":os.path.join(homeDir,"FTP")} if flagRemoteServer: # remote user profile dictResources["remote_profile"] = {"resource":"unix", "path":os.path.join(home,"."+userName+"."+"remote")} return dictResources def mountSambaRes(self,host,userName,userPwd,uid,gid,res,rpath, mountUidList=['ftp','homes','share']): """Mount samba resource""" if res in mountUidList: # mount by uid mountStr = "mount -t cifs -o user=%s,uid=%s,gid=%s,noperm"\ %(userName,uid,gid) +\ " //%s/%s %s" %(host, res, rpath) else: # mount by root mountStr = "mount -t cifs -o user=%s"%(userName)+\ " //%s/%s %s" %(host, res, rpath) textLine = self.execProg(mountStr, envProg={"PASSWD":userPwd}) return textLine def mountSleepRes(self,host,userPwd,res,rpath): """ Mount user resource with waiting if need """ for waittime in [0.5,2,5]: if self.mountSambaRes(host,self.userName,userPwd, self.uid,self.gid,res,rpath) is False: # check on mount path if isMount(rpath): if not self.umountSleepPath(rpath): return False # wait time.sleep(waittime) else: return True return False def copyTemplateDir(self, srcDir, destDir): """ Move directory srcDir to destDir """ if os.system("mv %s %s &>/dev/null"%(srcDir, destDir)) != 0: raise ClientError(_("Failed to move %s")%srcDir + " " +\ _("to %s")%destDir) def upgradeUserProfile(self, userName, userHome, pathTemplates): """ Update user profile (example /home/.user/.CLD to /home/.user/CLD) """ # directory which keop old profile home = os.path.split(userHome)[0] if os.path.exists(pathTemplates): if self.clVars.Get("cl_profile_all_set") == "on": osLinuxShort = "all" else: osLinuxShort = self.clVars.Get("os_linux_shortname") pathNewTemplate = os.path.join(pathTemplates, osLinuxShort) pathOldTemplate = os.path.join(pathTemplates, "."+osLinuxShort) if not os.path.exists(pathNewTemplate) and\ os.path.exists(pathOldTemplate) and\ os.listdir(pathOldTemplate): # move profile self.copyTemplateDir(pathOldTemplate,pathNewTemplate) if not os.path.exists(pathNewTemplate): # create directory for profile keeping os.mkdir(pathNewTemplate) os.chmod(pathNewTemplate, 0700) if os.path.exists(pathOldTemplate) and\ not os.listdir(pathOldTemplate): os.rmdir(pathOldTemplate) return True def syncUser(self, userName, userHome, sync, homeTemplate, \ host="default"): """ Sync user profile from server or to server """ flagError = False execStr = "" skipPaths = self.clVars.Get("cl_sync_skip_path") if not skipPaths: self.printERROR( _("The variable 'cl_sync_skip_path' is empty")%userHome) return False deletePaths = self.clVars.Get("cl_sync_del_path") if not deletePaths: deletePaths = [] excludePaths = " ".join(map(lambda x: '--exclude="/%s"'\ %x.replace('"',"").replace("'",""), skipPaths + deletePaths)) if sync == "login": if os.path.exists(userHome) and\ os.path.exists(homeTemplate): 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, homeTemplate, userHome) elif sync == "logout": if os.path.exists(userHome) and os.listdir(userHome) and\ os.path.exists(homeTemplate): execStr = '/usr/bin/rsync --delete-excluded --delete %s \ -rlptgo -x -v -v -v %s/ %s/'%(excludePaths, userHome, homeTemplate) else: raise ClientError( _("Method syncUser: option sync=%s incorrect")%str(sync)) if execStr: host = "" + host +"" if sync == "login": rsync = RsyncProgressBar(\ _("Receiving the file list from %s") % host, _("Downloading the user profile from %s") % host, execStr,self) else: rsync = RsyncProgressBar(\ _("Sending the file list to %s") % host, _("Uploading the user profile to %s") % host, execStr,self) pathConfig = os.path.join(homeTemplate, self.pathConfig) # remove old ini file prevIniFile = os.path.join(homeTemplate,".calculate.ini") if os.path.exists(prevIniFile): os.remove(prevIniFile) # create home directory if not os.path.exists(pathConfig): self.createUserDirectory(pathConfig) numfiles = 0 configFileName = os.path.join(homeTemplate, self.configFileDesktop) if sync == "login": # get rsync files try: numfiles = \ iniParser(configFileName).getVar('rsync','files') if numfiles is False: if os.path.exists(configFileName): os.remove(configFileName) numfiles = 0 else: numfiles = int(numfiles) except: numfiles = 0 rsync.maximum = numfiles if sync == "login": rsync.run() if sync == "logout": rsync.run() try: if iniParser(configFileName).setVar('rsync', {'files':rsync.getFilesNum()}): os.chmod(configFileName, 0600) os.chown(configFileName,self.uid,self.gid) except: pass rsync.close() if rsync.getExitCode() != 0: try: if iniParser(configFileName).setVar(\ 'rsync',{'exitcode':rsync.getExitCode()}): os.chmod(configFileName, 0600) os.chown(configFileName,self.uid,self.gid) except: pass self.printERROR(rsync.getErrMessage()) self.printERROR(_("Failed to execute rsync") + " " + \ str(sync) + " ...") return False # change permissions changeDirs = [userHome, homeTemplate] for changeDir in changeDirs: # get directory permissions mode = getModeFile(changeDir, mode="mode") # if permission wrong( not 0700) then change it if mode != 0700: os.chmod(changeDir,0700) return True def clearHomeDir(self, homeDir): """Clear home directory""" rmFiles = list(set(os.listdir(homeDir))-\ set(self.skipHomeFile)) for rmFile in rmFiles: 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) return True def syncLoginProfile(self, host, homeDir, homeProfile, flagClearHomeDir=True): """ Get user profile from server """ # if currect server has any files then sync it if filter(lambda x: not x in ('.calculate',), os.listdir(homeProfile)): if not self.syncUser(self.userName, homeDir, "login", homeProfile, host=host): return False # remove home directory on workstation else: if flagClearHomeDir: # clean home directory if not self.clearHomeDir(homeDir): return False return True def getDataInConfig(self, section, listVars, objConfig): """ Read variables listVars from section in configuration file """ varsConfig = {} for varName in listVars: varsConfig[varName] = objConfig.getVar(section,varName) if objConfig.getError(): return False return varsConfig def foundArchFile(self,strCurrentTime,archPathProcess, archPathSuccess, remoteServer=False): """Found user profile archive""" self.startTask(_("Packing the archive on the server"), progress=True) for sleeptime in [0.1,0.2,0.5]: # archive in packing process found if os.path.exists(archPathProcess) or \ os.path.exists(archPathSuccess): break time.sleep(sleeptime) else: # archive not found self.endTask(False) return False # wait finish packing if os.path.exists(archPathProcess) or \ os.path.exists(archPathSuccess): while os.path.exists(archPathProcess) or \ os.path.exists(archPathSuccess): for waittime in [0.5,1,2]: if os.path.exists(archPathSuccess): self.endTask() return True try: startSize = os.stat(archPathProcess).st_size time.sleep(waittime) if startSize != os.stat(archPathProcess).st_size: break except OSError as e: if os.path.exists(archPathSuccess): self.endTask() return True # run out of time size change else: # archive size was not change above 4s => archive not found self.endTask(False) return False self.endTask(False) return False def fileReader(self, fileName, stdin, maxSize): """ Read file by block """ fd = os.open(fileName, os.O_RDONLY) currSize = 0 # default buffer size buffSize=131072 dataBlock = os.read(fd, buffSize) while dataBlock: currSize += len(dataBlock) self.setProgress(currSize*100/maxSize) stdin.write(dataBlock) dataBlock = os.read(fd, buffSize) stdin.close() os.close(fd) def moveArch(self, srcArchFile, dstArchFile, remoteServer=False, mode=0600): """ Move archive from one directory to other """ class copyTo(color_print): def __init__(self, destFile): self.FD = open(destFile, "w") os.chmod(destFile, mode) def write(self, data): self.FD.write(data) def close(self): self.FD.close() try: copyToObj = copyTo(dstArchFile) except: raise ClientError(_("Failed to create file '%s'")%dstArchFile) archiveSize = os.stat(srcArchFile).st_size or 1 try: self.fileReader(srcArchFile, copyToObj, archiveSize) except: raise ClientError(_("Failed to copy '%(from)s' -> '%(to)s'")\ %{'from':srcArchFile, 'to':dstArchFile}) def unpackArch(self, homeDir, archFile, remoteServer=False): """ Unpack archive into user home directory """ archiveSize = os.stat(archFile).st_size execStr = "tar -C '%s' -xzf -" %homeDir # run unpacking process pipe = subprocess.Popen(execStr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=os.environ, shell=True) # read file into pipe self.fileReader(archFile, pipe.stdin,archiveSize) ret = pipe.wait() pipe.stdout.close() pipe.stderr.close() if ret: self.printWARNING(_("Failed to execute %s") %execStr) self.printWARNING(_("Failed to unpack %s") %archFile) return False return True def setVarToConfig(self, nameSection, varsDict, configFileName): """Записывает переменную в файл конфигурации""" # Создаем директорию для конфигурационных файлов pathConfig = os.path.split(configFileName)[0] if not os.path.exists(pathConfig): self.createUserDirectory(pathConfig) try: if iniParser(configFileName).setVar(nameSection, varsDict): os.chmod(configFileName, 0600) os.chown(configFileName,self.uid,self.gid) except: return False return True def convertDate(self,strdate,dateformat="%Y-%m-%d %H:%M:%S"): """ Convert date from string format (dateformat) to stuct or None """ if strdate: try: return time.strptime(strdate,dateformat) except ValueError: pass return None def getDateObjClientConf(self, fileConfig): """ Return time object from config file .calculate/desktop.env """ if os.path.exists(fileConfig): objConfig = iniParser(fileConfig) data = self.getDataInConfig("main", ["date", "date_logout"], objConfig) timeLogout = data["date_logout"] timeConfig = data["date"] dates = filter(None, [self.convertDate(timeLogout), self.convertDate(timeConfig)]) if dates: return dates[0] return False def createUserDirectory(self,userdir): """ Create directory with user permissions """ if not os.path.exists(userdir): try: os.mkdir(userdir) os.chown(userdir, self.uid, self.gid) os.chmod(userdir,0700) except OSError: raise ClientError(_("Error creating the directory")+ _("Permission denied: '%s'")%userdir) def checkNeedSync(self,rpath,curTimeObj,curStatusSync,osLinuxShort): """ Check about need sync with domain profile by profile time """ # upgrade user profile or create need profile directory self.upgradeUserProfile(self.userName, self.homeDir, rpath) # profile directory fileConfig = os.path.join(self.homeDir, self.configFileServer) pathProfile = os.path.join(rpath, osLinuxShort) if not readFile(fileConfig).strip(): return True,pathProfile fileSoftConfigThis = os.path.join(pathProfile, self.configFileSoft) fileSoftConfigCur = os.path.join(self.homeDir, self.configFileSoft) xSessionCur = iniParser(fileSoftConfigCur).getVar('main','xsession') xSessionThis = iniParser(fileSoftConfigThis).getVar('main','xsession') # check profile date on current server fileConfigThis = os.path.join(pathProfile, self.configFileDesktop) if iniParser(fileConfigThis).getVar('main','status_sync') == "success": self.setVarToConfig("main", {"status_sync":"success_mount"}, fileConfigThis) thisTimeObj = self.getDateObjClientConf(fileConfigThis) if curStatusSync == "success_logout" and \ xSessionCur == xSessionThis and \ thisTimeObj and curTimeObj and \ curTimeObj >= thisTimeObj: return False,pathProfile return True,pathProfile def mountLocalDomainRes(self,userPwd,dictRes): """ Mount all local samba resource """ res = True configFileName = os.path.join(self.homeDir, self.configFileDesktop) for name in dictRes.keys(): if name == "remote_profile": continue rpath = dictRes[name]["path"] res = dictRes[name]["resource"] # create user directory self.createUserDirectory(rpath) # is directory already mount if isMount(rpath): continue else: if self.mountSleepRes(self.domain, userPwd, res, rpath) is False: if name == "profile": self.printWARNING(_("Failed to mount user profile")) res = False continue else: raise ClientError( _("Failed to mount Samba resource [%s]")%res) elif name == "profile": # upgrade user profile or create need profile directory self.upgradeUserProfile(self.userName, self.homeDir, rpath) else: return res return False def syncLocalDomainRes(self,userPwd,dictRes,curTimeObj,curStatusSync, osLinuxShort,fromRemote=False): """ Sync profile with current server fromRemote - method called by remote sync method """ if self.mountLocalDomainRes(userPwd,dictRes): if not self.sync: return True if "profile" in dictRes: rpath = dictRes["profile"]["path"] needSync,lDomainProfile = self.checkNeedSync(rpath, curTimeObj, curStatusSync,osLinuxShort) if not needSync: if fromRemote: self.printSUCCESS(_("Local user profile does not need " "to update with local domain")) else: self.printSUCCESS(_("Local user profile will be used")) # Copy private files from the server self.copyPrivateFiles(lDomainProfile, self.homeDir) return True else: # sync profile with current server return self.syncLoginProfile(self.domain,self.homeDir, lDomainProfile) return False def _getRemoteServerProfileByArch(self,dictRes,rHomeProfile, dateDefaultTemplate,userPwd,curTimeObj, curStatusSync,remoteServer, osLinuxShort): """ Try get profile from remote server """ # current time currTime = str(float(time.time())) archPathSuccess,archPathProcess,archPathSrc,archPathDst = \ (None,None,None,None) try: rpath = dictRes["remote_profile"]["path"] res = dictRes["remote_profile"]["resource"] # create command for create incremental arch fileConfig = \ os.path.join(rHomeProfile,self.configFileServer) varsConfig = {"arch_date":dateDefaultTemplate, "curr_time":currTime} try: self.setServerCommand(["pack"], varsConfig, fileConfig) except ClientError as e: return False rFileConfig = os.path.join(rHomeProfile, self.configFileDesktop) self.setVarToConfig("main", {"status_sync":"success"}, rFileConfig) self.umountSleepPath(rpath) if not self.syncLocalDomainRes(userPwd,dictRes,curTimeObj, curStatusSync,osLinuxShort, fromRemote=True): return False if self.mountSleepRes(remoteServer, userPwd, res, rpath) is False: return False self.setVarToConfig("main", {"status_sync":"success_mount"}, rFileConfig) extSuccess = "gz" extProcess = "process" strCurrentTime = currTime.replace(".","_") archPathTmp = os.path.join(rpath, "profile.%s.%s.tar"\ %(osLinuxShort, strCurrentTime)) # result archive file name archPathSuccess = "%s.%s"%(archPathTmp,extSuccess) # archive in pack process archPathProcess = "%s.%s"%(archPathTmp,extProcess) # find archive file if not self.foundArchFile(strCurrentTime, archPathProcess, archPathSuccess, remoteServer): self.printWARNING(_("Failed to find profile archive from %s")% remoteServer) return False if os.path.exists(archPathSuccess): archPathSrc = archPathSuccess archPathDst = ".".join(filter(lambda x: x!="/", archPathSuccess.rpartition("/"))) # get archive self.startTask(_("Copying archive from the server"), progress=True) try: self.moveArch(archPathSrc, archPathDst, remoteServer) except ClientError as e: self.printWARNING(str(e)) return False self.endTask() # unpack archive into home directory self.startTask(_("Unpack profile archive"),progress=True) if not self.unpackArch(self.homeDir, archPathDst, remoteServer): self.printERROR(_("Failed to unpack archive")) return False # get removed files list pathListFile = os.path.join(rHomeProfile, self.listTemplFile) if not self.removeFilesInProfile(self.homeDir,pathListFile): self.printWARNING(_("Unable to remove needless files")) return False self.endTask() return True return False finally: for rmFile in (archPathSuccess,archPathProcess, archPathSrc,archPathDst): if rmFile and os.path.exists(rmFile): os.remove(rmFile) def syncRemoteDomainRes(self,userPwd,dictRes,remoteServer,curTimeObj, curStatusSync,osLinuxShort): """ Sync if actual profile is kept on a remote server """ # mount directories path if not "remote_profile" in dictRes: return False if not self.sync: self.mountLocalDomainRes(userPwd,dictRes) return True rpath = dictRes["remote_profile"]["path"] res = dictRes["remote_profile"]["resource"] # create user directory self.createUserDirectory(rpath) # mount remote profile if isMount(rpath): self.printWARNING(_("Remote profile already mounted")) return False else: if self.mountSleepRes(remoteServer, userPwd, res, rpath) is False: return False try: rpath = dictRes["remote_profile"]["path"] needSync,pathRProfile = self.checkNeedSync(rpath,curTimeObj, curStatusSync,osLinuxShort) if not needSync: # Copy private files from the server self.printSUCCESS(_("Local user profile will be used")) if not self.mountLocalDomainRes(userPwd,dictRes): return False self.copyPrivateFiles(pathRProfile, self.homeDir) return True localServer = self.domain # mount local samba resources if self.mountLocalDomainRes(userPwd,dictRes): lpath = dictRes["profile"]["path"] homeLProfile = os.path.join(lpath, osLinuxShort) if not os.path.exists(homeLProfile): homeLProfile = os.path.join(lpath, "." + osLinuxShort) lFileConfig = os.path.join(homeLProfile, self.configFileDesktop) # get data from desktop config on success run get by archive if os.path.exists(lFileConfig): objConfig = iniParser(lFileConfig) data = self.getDataInConfig("main", ["status_sync", "date","date_logout"], objConfig) if data: status = data.get("status_sync") date = data.get("date") date_logout = data.get("date_logout") if date and date_logout: diffdate = \ abs(time.mktime(self.convertDate(date))- time.mktime(self.convertDate(date_logout))) else: diffdate = None # if diff date less 10 min if diffdate is None or diffdate < 600: if date and "success" in status: if self._getRemoteServerProfileByArch(dictRes, pathRProfile,date,userPwd, curTimeObj, curStatusSync, remoteServer, osLinuxShort): return True else: self.printWARNING( _("Difference time between server and " "client more 10 minute")) self.printWARNING(_("Profile synchronization " "will be used without archiving")) # sync local before remote sync self.syncLocalDomainRes(userPwd,dictRes,curTimeObj, curStatusSync,osLinuxShort, fromRemote=True) # get user profile from remote domain return self.syncLoginProfile(remoteServer, self.homeDir, pathRProfile, False) finally: # unmount remote profile if self.sync and "remote_profile" in dictRes: if isMount(rpath): if not self.umountSleepPath(rpath): raise ClientError(_("Failed to unmount remote profile")) # remote directory for remote if os.path.exists(rpath) and not os.listdir(rpath): os.rmdir(rpath) def errorUmountUserRes(self,error): """ Post action for umount user res and write status_sync=error """ self.closeClTemplate() if error and self.homeDir: umountResult = self.umountUserRes(self.homeDir) configFileName = os.path.join(self.homeDir, self.configFileDesktop) uid = self.clVars.Get('ur_uid') gid = self.clVars.Get('ur_gid') self.setVarToConfig("main", {"status_sync":"error"}, configFileName) self.printERROR(_("Failed to get user profile from domain")) return umountResult return True def initEnv(self): """ Init object variables """ try: self.uid = int(self.clVars.Get('ur_uid')) self.gid = int(self.clVars.Get('ur_gid')) except ValueError as e: raise ClientError(_("Failed to determinate UID and GID")) self.sync = self.clVars.Get('cl_client_sync') == 'on' self.userName = self.clVars.Get("ur_login") self.homeDir = self.clVars.Get('ur_home_path') self.domain = self.clVars.Get("cl_remote_host") @safetyWrapper(native_errors=(TemplatesError,ClientError), man_int=__("Manually interrupted"), post_action=errorUmountUserRes) def mountUserResAndSync(self,dv): """Mount user resources and sync profile""" self.initVars(dv) self.initEnv() if self.domain: foundMountRemote = isMount("/var/calculate/remote") if not foundMountRemote: self.mountRemote(); # check on two session user login if self.isTwoSessionsUser(self.userName): return False hostAuth = self.clVars.Get("os_remote_auth") # if not domain if not hostAuth or not self.domain: self.printSUCCESS(_("The local profile will be used")) return True try: passwdUsers = map(lambda x: x[0], map(lambda x: x.split(':'), map(lambda x: x.strip(), open("/etc/passwd").readlines()))) except: self.printERROR(_("Failed to open /etc/passwd")) return False if self.userName in passwdUsers: try: pwdObj = pwd.getpwnam(self.userName) except: raise ClientError(_("Failed to found user %s")%self.userName) self.printWARNING(_("User information from /etc/passwd is used")) return True # check for domain workstation and [remote] was mounted if not self.isDomain(): raise ClientError(_("The computer is not in the domain")) # user config filename configFileName = os.path.join(self.homeDir, self.configFileDesktop) # user time object from config file currentTimeObj = self.getDateObjClientConf(configFileName) currentStatusSync = self.getSyncStatus(self.homeDir) # create home directory if it is not exists if not os.path.exists(self.homeDir): os.makedirs(self.homeDir) os.chown(self.homeDir,self.uid,self.gid) os.chmod(self.homeDir,0700) # get local date and statusSync # write into config status "process" self.setVarToConfig("main", {"status_sync":"process"}, configFileName) # get user password from key kernel userPwd = getKey(self.userName) if not userPwd or userPwd == "XXXXXXXX": raise ClientError(_("User password not found")) # user cache if not self.cAddUserToCache(self.userName, userPwd): self.printWARNING(_("Unable cache user info")) # profile name if self.clVars.Get("cl_profile_all_set") == "on": osLinuxShort = "all" else: osLinuxShort = self.clVars.Get("os_linux_shortname") # if replication is on replOn = self.ldapDataObj.isRepl() # remote domain name if replOn: remoteServer = self.ldapDataObj.getNameRemoteServer( self.userName, osLinuxShort, self.domain) else: remoteServer = "" # get mount directory dictRes = self.getUserMountResources(self.userName, self.homeDir, remoteServer) if isMount(dictRes["profile"]["path"]): self.sync = False useRemoteProfile = remoteServer and replOn profileServer = self.domain # if profile on remote server if useRemoteProfile: if not self.syncRemoteDomainRes(userPwd,dictRes, remoteServer,currentTimeObj, currentStatusSync,osLinuxShort): syncStatus = "error" useRemoteProfile = False self.printWARNING( _("Error synchronizing with the remote server %s") %remoteServer) else: syncStatus = "success" profileServer = remoteServer # if profile not remote or remote sync is failed if not useRemoteProfile: if self.syncLocalDomainRes(userPwd,dictRes,currentTimeObj, currentStatusSync,osLinuxShort): # if not fallback for remote sync if not(remoteServer and replOn): syncStatus = "success" else: syncStatus = "error" self.printWARNING( _("Error synchronizing with the remote server %s") %self.domain) if self.sync: self.setVarToConfig("main", {"status_sync":syncStatus}, configFileName) # unpack link from link arch self.unpackLinks(self.homeDir) self.printSUCCESS(_("Mounted user resource of the domain")) if self.sync: if syncStatus == "error": self.printWARNING( _("Get a user fallback profile from the %s domain") %profileServer) else: self.printSUCCESS(_("Get a user profile from the %s domain")% profileServer) return True def isSessionUser(self, userName): """ Check for user in X session """ reFoundUser = re.compile("%s\s+:\d+\s+"%(userName)) resWho = self.execProg("who") if resWho: if any(reFoundUser.search(x) for x in resWho): return True return False def tarSymLinks(self,userHome,uid,gid): """Create tar archive of symlinks""" linkArch = pathJoin(userHome,".calculate/links.tar.bz2") try: for filename in tarLinks(userHome,linkArch, skip=self.clVars.Get("cl_sync_del_path")+ self.clVars.Get("cl_sync_skip_path")): try: os.unlink(filename) except OSError: self.printWARNING(_("Failed to remove %s")%filename) except: self.printWARNING(_("Failed to make links archive")) try: if os.path.exists(linkArch): os.chown(linkArch,uid,gid) except: self.printWARNING(_("Failed to make links archive")) def unpackLinks(self,userHome): """Unpack archive of symlinks""" linksArch = pathJoin(userHome,".calculate/links.tar.bz2") try: if os.path.exists(linksArch): tf = tarfile.open(linksArch) tf.extractall(userHome) tf.close() except: self.printWARNING(_("Failed to unpack links archive")) def moveHomeDir(self, userHome): """ Move non profile files in root of home profile to Home/Moved """ pathProg = os.getcwd() os.chdir(userHome) dirs = [] files = [] movedLink = os.path.join('Moved') movedPath = os.path.join('Home',"Moved") skipPaths = self.clVars.Get("cl_moved_skip_path") if not skipPaths: skipPaths = ['Disks','Share','Home','Moved','FTP','Desktop'] filesAndDir = filter(lambda x: not ('.' in x[0] or x in\ skipPaths or os.path.islink(x)), os.listdir('.')) filesDir = [] for fd in filesAndDir: if os.path.islink(fd): os.unlink(fd) else: filesDir.append(fd) # find files in Desktop pathDesktop = './Desktop' filesDirDesk = [] if os.path.exists(pathDesktop): filesDirDesk = filter(lambda x: not os.path.islink(x) and\ not os.path.split(x)[1].startswith('.') and\ not x.rpartition('.')[2]=='desktop', map(lambda x: os.path.join(pathDesktop,x),\ os.listdir(pathDesktop))) movedPathDesk = os.path.join(movedPath, pathDesktop) filesDir += filesDirDesk if not filesDir or not os.path.exists("Home"): # remote empty Moved folder if os.path.exists(movedPath) and not os.listdir(movedPath): os.rmdir(movedPath) # remote link to Moved in home directory if os.path.islink(movedLink) and not os.path.exists(movedPath): os.unlink(movedLink) return True if not os.path.exists(movedPath): os.mkdir(movedPath) directFile = os.path.join(movedPath,".directory") if not os.path.exists(directFile): txt = "[Desktop Entry]\nIcon=folder-development" fd = os.open(directFile, os.O_CREAT) os.close(fd) FD = open (directFile, "r+") FD.write(txt) FD.close() if not os.path.exists(movedLink): os.symlink(movedPath, movedLink) for fd in filesDir: execStr = "cp -r '%s' '%s'" %(fd, movedPath) textLine = self.execProg(execStr) if textLine is False: self.printERROR(_("Failed to execute") + " " + str(execStr)) return False execStr = "rm -rf '%s'" %fd textLine = self.execProg(execStr) if textLine is False: self.printERROR(_("Failed to execute") + " " + str(execStr)) return False os.chdir(pathProg) return True def removeNoiseFiles(self, userHome): """ Remove files which hinder dm """ noiseFiles = [] for nsFile in noiseFiles: rmFile = os.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: privateDir = os.path.join(userHome,privateHomeDir) if os.path.isdir(privateDir): # .ssh files relative user home directory 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, 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) 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 scanDirectory(self, scanDir, listFiles, skipPath=[], prefix=False, flagDir=False): """ Generate file/directory list """ if not prefix: prefix = os.path.join(scanDir,"") if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]): for fileOrDir in os.listdir(scanDir): absPath = os.path.join(scanDir,fileOrDir) relPath = absPath.split(prefix)[1] if relPath in skipPath: continue listFiles.append(relPath) stInfo = os.lstat(absPath) statInfo = stInfo[stat.ST_MODE] if stat.S_ISDIR(statInfo): self.scanDirectory(absPath, listFiles, skipPath, prefix, True) def getListFilesTemplate(self, homeDir): """ Generation file list in user home directory, exclude mount dirs """ home = os.path.join(homeDir, "") execStr = "mount" textLines = self.execProg(execStr) # skip directories in scanning skipPaths = [] if textLines: for line in textLines: if home in line: skipPath =\ line.partition(home)[2].rpartition(" type")[0] skipPaths.append(skipPath) # file list in user profile listFiles = [] if not skipPaths: self.printERROR(_("Mounting point for server resources not found")) return False self.scanDirectory(homeDir, listFiles, skipPaths) return listFiles def removeFilesInProfile(self, homeDir, pathListFile): """ Remove files which is absend in user profile """ # read file list from config try: filesTemplateTxt = open(pathListFile).read() except: return False listFilesTemplate = filter(lambda x: x.strip(), filesTemplateTxt.split("\n")) filesTemplate = set(listFilesTemplate) # get files in home directory listFilesHome = self.getListFilesTemplate(homeDir) if listFilesHome is False: return False filesHome = set(listFilesHome) filesRemove = list(filesHome - filesTemplate) filesRemove.sort(lambda x, y: cmp(len(y), len(x))) rmPath = "" try: for rmFile in filesRemove: rmPath = os.path.join(homeDir, rmFile) 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) except: return False return True def getRunCommandsWithEnv(self): """List run program""" def getCmd(procNum): cmdLineFile = '/proc/%s/cmdline'%procNum environFile = '/proc/%s/environ'%procNum try: if os.path.exists(cmdLineFile) and \ os.path.exists(environFile): return (open(cmdLineFile,'r').read().strip(), open(environFile,'r').read().strip()) except: pass return ("","") if not os.access('/proc',os.R_OK): return [] return map(getCmd, 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 \ 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: self.printERROR(_("Failed to clear the kernel key for user %s")\ %userName) return False return True @safetyWrapper(native_errors=(TemplatesError,ClientError), man_int=__("Manually interrupted"), post_action=errorUmountUserRes) def umountUserResAndSync(self, dv): """ Umount user resouces and synchronizatoin user profile """ self.initVars(dv) self.initEnv() try: passwdUsers = map(lambda x: x[0], map(lambda x: x.split(':'), map(lambda x: x.strip(), open("/etc/passwd").readlines()))) except: self.printERROR(_("Failed to open /etc/passwd")) return False if not os.path.exists(self.homeDir): self.printERROR(_("User home directory %s not found")%self.homeDir) return False currentDateStr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # domain workstation work in local mode if self.userName in passwdUsers: configFileName = os.path.join(self.homeDir, self.configFileDesktop) self.setVarToConfig("main", {"date_logout":currentDateStr}, configFileName) self.printWARNING(_("User information from /etc/passwd is used")) self.printSUCCESS(_("The local profile will be used")) return True hostAuth = self.clVars.Get("os_remote_auth") # local mode if not hostAuth or not self.domain: self.printSUCCESS(_("The local profile will be used")) return True # check for domain workstation and [remote] was mounted if not self.isDomain(): raise ClientError(_("The computer is not in the domain")) # check is user in X session, do not umount user resources if self.isSessionUser(self.userName): self.printERROR(_("User %s is already in X session")%self.userName) self.printERROR( _("Failed to unmount user %s resource")%self.userName) return False # user package config configFileName = os.path.join(self.homeDir, self.configFileDesktop) self.setVarToConfig("main", {"date_logout":currentDateStr}, configFileName) if os.path.exists(self.homeDir): self.moveHomeDir(self.homeDir) if self.sync: self.tarSymLinks(self.homeDir,self.uid,self.gid) else: raise ClientError(_("Directory %s not found") % self.homeDir) # get status sync exitStr = iniParser(configFileName).getVar('main','status_sync') # check on error or net full executed -> process if exitStr == "process" or exitStr == "error": self.sync = False if self.clVars.Get("cl_profile_all_set") == "on": osLinuxShort = "all" else: osLinuxShort = self.clVars.Get("os_linux_shortname") # get mount resources dictRes = self.getUserMountResources(self.userName, self.homeDir,False) pathUnix = dictRes["profile"]["path"] homeProfile = os.path.join(pathUnix, osLinuxShort) if not isMount(pathUnix): self.printERROR(_("Mounted remote resources for user %s not found")% self.userName) return False try: if self.sync: # sync profiles local to domain if not self.syncUser(self.userName, self.homeDir, "logout", homeProfile,host=self.domain): return False finally: # remove files, which hinder correct dm work self.removeNoiseFiles(self.homeDir) # remove private files self.removePrivateFiles(self.homeDir) # clean user password from root keys self.clearUserKey(self.userName) # umount user res if not self.umountUserRes(self.homeDir): return False # remove empty directories for name in dictRes.keys(): rpath = dictRes[name]["path"] if os.path.exists(rpath) and not os.listdir(rpath): try: os.rmdir(rpath) except: self.printERROR(_("Failed to remove dir %s")% path) return False # remove Disks directory if it empty pathDisks = os.path.join(self.homeDir,"Disks") if os.path.exists(pathDisks) and not os.listdir(pathDisks): try: os.rmdir(pathDisks) except: self.printERROR(_("Failed to remove dir %s")% pathDisks) return False if self.sync: self.printSUCCESS(_("User profile saved in the domain")) self.setVarToConfig("main", {"status_sync":"success_logout"}, configFileName) self.printSUCCESS(_("Domain user resource unmounted")) return True def getMountUserPaths(self, homeDir=False): """ Get users mount paths """ if not homeDir: userName = self.clVars.Get("ur_login") try: homeDir = pwd.getpwnam(userName).pw_dir except: homeDir = os.path.join("/home",userName) dirStart, dirEnd = os.path.split(homeDir) mountTemplateDir = os.path.join(dirStart, ".%s" %dirEnd) mountRemoteTemplateDir = os.path.join(dirStart, ".%s.remote" %dirEnd) return filter(lambda x: x.startswith(homeDir) or\ x.startswith(mountTemplateDir) or\ x.startswith(mountRemoteTemplateDir), map(lambda x: x.split(" ")[1],\ open("/proc/mounts").readlines())) def execProg(self, cmdStrProg, inStr=False, envProg={}): """ Execute external program """ env_path = {"PATH":getpathenv()} env = {} env.update(os.environ.items() + env_path.items() + envProg.items()) retCode,programOut = runOsCommand(cmdStrProg,in_str=inStr,env_dict=env) if not retCode: return programOut return False def umountSleepPath(self, rpath): """ Unmount path, sleep by failed and repeat """ # check for mount if isMount(rpath): for waittime in [0,0.5,1,2]: time.sleep(waittime) if not self.execProg("umount %s"%rpath) is False \ or not isMount(rpath): if not isMount(rpath): return True self.execProg("fuser -km %s"%rpath) for waittime in [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]: time.sleep(waittime) if not self.execProg("umount %s"%rpath) is False \ or not isMount(rpath): if not isMount(rpath): return True else: if isMount(rpath): raise ClientError(_("Failed to unmount path %s")%rpath) return True def umountUserRes(self, homeDir=False, removeEmpty=False): """ Unmount user directories """ for umountPath in self.getMountUserPaths(homeDir): if not self.umountSleepPath(umountPath): return False if os.path.exists(umountPath) and not os.listdir(umountPath): os.rmdir(umountPath) return True def getDefaultRunlevelDaemons(self): """ Get all daemons from default runlevel """ execStr = "rc-update show" textLine = self.execProg(execStr) if textLine is False: self.printERROR(_("ERROR") + ": " + execStr) return False else: splLines = filter(lambda x: len(x)==2 and "default" in x[1],\ map(lambda x: x.split("|"),textLine)) splLines = map(lambda x: x[0].strip(), splLines) return splLines def delDaemonAutostart(self, daemon): """ Remove daemon from autorun """ defaultDaemons = self.getDefaultRunlevelDaemons() if defaultDaemons is False: return False if daemon in defaultDaemons: execStr = "rc-update del %s default" %daemon textLine = self.execProg(execStr) if textLine is False: self.printERROR(_("ERROR") + ": " + execStr) self.printERROR(_("Failed to delete from the default runlevel")) return False else: return True return True def delDomain(self): """ Remove from domain """ pathRemote = "/var/calculate/remote" foundMountRemote = isMount(pathRemote) domain = self.clVars.Get("cl_remote_host") if foundMountRemote: textLineUmount = self.umountSleepPath(pathRemote) if not textLineUmount: return False if not domain: self.printWARNING(_("The computer is not in the domain")) return True # remove domain vars from env files self.removeVars() # apply templates for undomain self.clVars.Set("cl_action", "undomain", True) if not self.applyTemplatesFromSystem(): self.printERROR(_("Failed to apply undomain templates")) return False # Delete LDAP users from system and clear cache if not self.cDelLdapSysUsersAndClearCache(): self.printERROR(_("Failed to clear user cache")) return False # restart dbus self.restartDBus() if not self.delDaemonAutostart("client"): self.printERROR( _("Failed to remove client service from autostart")) return False self.printSUCCESS(_("Computer removed from domain %s")%domain + " ...") return True def getUserPassword(self, pwDialog=False): """Получить пароль у пользователя pwDialog - приглашение ввода пароля """ if os.readlink('/proc/self/fd/0') == '/dev/console': os.system('chvt 1 &>/dev/null') if not pwDialog: pwDialog = _("Password") userPwd = getpass.getpass(pwDialog+":") return userPwd def addDaemonAutostart(self, daemon): """Прописывает демона в автозагрузку""" defaultDaemons = self.getDefaultRunlevelDaemons() if defaultDaemons is False: return False if daemon in defaultDaemons: return True execStr = "rc-update add %s default" %daemon textLine = self.execProg(execStr) if textLine is False: self.printERROR(_("ERROR") + ": " + execStr) self.printERROR(_("Failed to add to the default runlevel")) return False else: return True def restartDBus(self): """Restart D-Bus service""" dbusDaemon = 'rc-service -i dbus' existsDaemon = 'rc-service -e dbus' # if exists dbus and running if os.system(existsDaemon) == 0 and \ os.system(dbusDaemon + ' status &>/dev/null') == 0: # restart if os.system(dbusDaemon + ' restart -- -s &>/dev/null') != 0: self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...") return False return True def getInfoService(self, service, option, envFile=False): """ Get service parameter from env """ if not envFile: if self.convObj == 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]: value = self.optionsInfo[service][option] return value @safetyWrapper(native_errors=(TemplatesError,ClientError), man_int=__("Manually interrupted")) def domain(self,dv): self.initVars(dv) if self.clVars.Get('cl_client_mount_set') == 'on': return self.mountRemote() else: if self.clVars.Get('cl_localhost_set') == "on": return self.delDomain() else: return self.addDomain() def addDomain(self): """ Add to domain """ domainName = self.clVars.Get('cl_remote_host_new') remoteHost = self.clVars.Get("cl_remote_host") netDomain = self.clVars.Get("os_net_domain") # get domain name if "." in domainName: domain = domainName else: domain = "%s.%s" %(domainName,netDomain) # check domain by ping for i in range(0,1): try: Pinger().ping(domain,1000,16) break except IPError as e: pass else: self.printERROR(_("Server %s does not respond")%domain) return False reFoundHostSamba = re.compile("Server=\[Samba.+\]") resSmbClient = self.execProg("smbclient -N -L %s" %domain) if not resSmbClient is False: for string in resSmbClient: if reFoundHostSamba.search(string): foundHostSamba = True break if not foundHostSamba: self.printERROR(_("Samba server not found in %s")%domain) return False if remoteHost and self.clVars.Get('cl_localhost_set') != 'on': self.printERROR(_("The computer is already in the domain %s")\ %remoteHost) self.printWARNING(_("Before joining the domain, " "you need to remove it from the previous domain")) self.printWARNING(_("Run 'cl-client -r'")) return False foundMountRemote = isMount("/var/calculate/remote") if foundMountRemote: self.printERROR(_("Samba resource [%s] mounted")%\ "remote" + " ...") return False else: def passwdQueue(): remotePw = self.clVars.Get('cl_remote_pw') if remotePw: yield remotePw if remotePw: self.printERROR(_("Wrong password")) yield self.askPassword(\ _("Domain password for the desktop"),False) pathRemote = "/var/calculate/remote" for pwdRemote in passwdQueue(): if not os.path.exists(pathRemote): os.makedirs(pathRemote) if not self.mountSambaRes(domain,"client",pwdRemote, 0,0,"remote",pathRemote) is False: self.printSUCCESS( _("Samba resource [%s] mounted")%"remote") self.clVars.Write("cl_remote_host", domain, True, "local") self.clVars.Write("cl_remote_pw", pwdRemote, True, "local") break else: self.printERROR( _("Failed to mount Samba resource [%s]")%"remote") return False servDn = self.getInfoService("ldap", "services_dn") unixDn = self.getInfoService("unix", "dn") bindDn = self.getInfoService("unix", "bind_dn") bindPw = self.getInfoService("unix", "bind_pw") # check info from server if not (servDn and unixDn and bindDn and bindPw): self.printERROR(_("Info not found on the server") + ": " +\ _("services DN or unix DN or bind DN or bind password")) return False self.clVars.Set("cl_action", "domain", True) self.clVars.Set("os_remote_auth", domain) if not self.applyTemplatesFromSystem(): self.printERROR( _("Failed to apply install templates or domain templates")) return False # restart dbus self.restartDBus() if not self.addDaemonAutostart("client"): return False # write domain to config self.clVars.Write("os_remote_auth", domain, True) # write current program version currentVersion = self.clVars.Get("cl_ver") self.clVars.Write("os_remote_client", currentVersion, True) self.printSUCCESS(_("Computer added to domain %s")%domain) return True def relevanceTemplates(self, hostAuth): """ Determine relevance appling templates by program version """ # '' or hostname if hostAuth != self.clVars.Get("os_remote_auth"): return False if self.clTempl: self.closeClTemplate() self.clTempl = ProgressTemplate(self.setProgress,self.clVars, cltObj=True) # current program version currentVersion = self.clVars.Get("cl_ver") # version of program which appled templates previousVersion = self.clVars.Get("os_remote_client") cVersion,pVersion = self.clTempl._convertVers(currentVersion,previousVersion) if cVersion != pVersion: return False return True def closeClTemplate(self): if self.clTempl: if self.clTempl.cltObj: self.clTempl.cltObj.closeFiles() self.clTempl.closeFiles() self.clTempl = None def applyRelevanceTemplates(self, hostAuth=""): """ Appling relevance templates """ if not self.relevanceTemplates(hostAuth): if hostAuth: # add to domain self.clVars.Set("cl_action","domain",True) else: # del from domain self.clVars.Set("cl_action","undomain",True) self.clVars.Set("os_remote_auth", hostAuth) # apply system templates dirsAndFiles = self.applyTemplatesFromSystem() if not dirsAndFiles: if hostAuth: self.printERROR(_("Failed to apply domain templates")) else: self.printERROR(_("Failed to apply undomain templates")) return False if hostAuth: self.printSUCCESS(_("Templates set for network mode")) currentVersion = self.clVars.Get("cl_ver") self.clVars.Write("os_remote_client", currentVersion, True) self.clVars.Write("os_remote_auth", hostAuth, True) else: self.printSUCCESS(_("Templates set for local mode")) self.clVars.Delete("os_remote_auth") self.clVars.Delete("os_remote_client") return True def cDelLdapSysUsersAndSyncCache(self): """Delete LDAP users from system and synchronize cache""" cacheObj = userCache() if not cacheObj.deleteCacheUsersFromSystem(): return False if not cacheObj.syncCacheToLdap(): return False return True def cAddCacheUsersFromSystem(self): """Add cache users from system""" cacheObj = userCache() if not cacheObj.addCacheUsersFromSystem(): return False return True def cAddUserToCache(self, userName, userPwd): """Add user to cache""" cacheObj = userCache() pwdHash = self.getHashPasswd(userPwd, "shadow_ssha256") if not cacheObj.addUserToCache(userName, pwdHash): return False return True def cDelLdapSysUsersAndClearCache(self): """Delete LDAP users from system and clear cache""" cacheObj = userCache() if not cacheObj.deleteCacheUsersFromSystem(): return False if not cacheObj.clearCache(): return False return True def moveHomeDirs(self): """Move home dirs /var/calculate/client-home -> /home if user in cache """ cacheObj = userCache() loginUsersData = cacheObj.getLoginDomainUsers() if loginUsersData is False: return False previousHome = "/var/calculate/client-home" if isMount(previousHome): return True if os.path.exists(previousHome): flagMovedUsers = False for userName,x,uid,gid,gecos,directory,shell in loginUsersData: homeDir = directory pathUserList = filter(lambda x: x, directory.split('/')) if not pathUserList: continue pathUser = "/".join(pathUserList[1:]) srcDir = pathJoin(previousHome, pathUser) if os.path.exists(srcDir) and not os.path.exists(homeDir): flagMovedUsers = True destDir = os.path.dirname(homeDir) self.printWARNING(_("Moved %(src)s to %(dest)s")\ %{"src":srcDir,"dest":homeDir}) if not self.copyTemplateDir(srcDir, destDir): return False if flagMovedUsers and not os.listdir(previousHome): os.rmdir(previousHome) return True def mountRemote(self): """ Mount remote domain resource for domain workstation. Alsa add to domain if found hostname and password """ domain = self.clVars.Get("cl_remote_host") if domain: foundMountRemote = isMount("/var/calculate/remote") else: self.printWARNING(_("This computer is not in the domain")) if not self.applyRelevanceTemplates(): return False return True if foundMountRemote: self.printWARNING(_("Samba resource [%s] mounted")%"remote" + \ " ...") # apply domain templates if domain: self.clVars.flIniFile() if not self.applyRelevanceTemplates(domain): return False # Delete LDAP users from system and synchronize cache if not self.cDelLdapSysUsersAndSyncCache(): return False return True else: pathRemote = "/var/calculate/remote" pwdRemote = self.clVars.Get("cl_remote_pw") if not (domain and pwdRemote): self.printERROR(_("Variable not found")+\ ": cl_remote_pw ...") return False if not os.path.exists(pathRemote): os.makedirs(pathRemote) if self.mountSambaRes(domain,"client",pwdRemote, 0,0,"remote",pathRemote) is False: self.printWARNING(_("Failed to mount Samba resource [%s]")%\ "remote" + " ...") beforeRemoteAuth = self.clVars.Get('os_remote_auth') # apply templates if current up-to-date if not self.applyRelevanceTemplates(): return False if not self.cAddCacheUsersFromSystem(): return False if not self.moveHomeDirs(): return False if beforeRemoteAuth != self.clVars.Get('os_remote_auth'): self.restartDBus() return True self.printSUCCESS(_("Samba resource [%s] mounted") % "remote" +\ " ...") # apply domain templates if domain: self.clVars.flIniFile() beforeRemoteAuth = self.clVars.Get('os_remote_auth') if not self.applyRelevanceTemplates(domain): return False # Delete LDAP users from system and synchronize cache if not self.cDelLdapSysUsersAndSyncCache(): return False if beforeRemoteAuth != self.clVars.Get('os_remote_auth'): self.restartDBus() return True @safetyWrapper(native_errors=(TemplatesError,ClientError), man_int=__("Manually interrupted")) def clientPasswd(self,dv): """ Change user password on server """ self.initVars(dv) self.initEnv() # check on root if self.uid == 0: self.printERROR(_("The user is root")) self.printWARNING(\ _("The program can be executed for a non-root user only")) return False password = self.clVars.Get('ur_user_new_pw') curPassword = self.clVars.Get('ur_user_pw') 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()): raise ClientError(_("Failed to change password")) # ~/.calculate/server.env fileConfig = os.path.join(self.homeDir, self.configFileServer) if not self.setServerCommand(["passwd_samba"], varsConfig, fileConfig, self.uid,self.gid): raise ClientError(_("Failed to change password")) self.printSUCCESS(_("%s's password changed")% self.clVars.Get('ur_login')) self.printSUCCESS(_("The password will be changed when you log " "out from the X session")) return True