#-*- coding: utf-8 -*- # Copyright 2008-2010 Mir 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 re import sys from cl_lang import lang from cl_data import DataVars from cl_template import iniParser, template, xmlShare, _file from cl_help import opt, cl_help from cl_utils import _error, getpathenv, runOsCommand from cl_ldap import ldapFun from cl_string import prettyColumnStr from cl_lib import shareFile import _cl_keys from ldap import SCOPE_ONELEVEL import types import getpass import time import stat import subprocess Version = "calculate-client 2.2.0" lang().setLanguage(sys.modules[__name__]) pcs = prettyColumnStr class printNoColor: def colorPrint(self,attr,fg,bg,string): sys.stdout.write(string) class ProgressBar: suffixSet = 'org.freedesktop.DBus.Properties.Set \ org.kde.kdialog.ProgressDialog' execenv = {"HOME":"/root"} max = 100 kdialog = None label = None def __init__(self,title,kdialog=None): self.title = title if kdialog == None: self.openDialog(self.title) def openDialog(self,title,max=None): if max != None: self.max = max self.title = title if os.system('which kdialog >/dev/null') == 0: self.label ="LOGINKDIALOG=%d" % os.getpid() env = {} env.update(os.environ.items() + self.execenv.items() +\ [tuple(self.label.split("="))]) pipe = subprocess.Popen('/usr/bin/kdialog --progressbar "%s" %d'\ %(" "*(len(title)+20), self.max), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=env, shell=True) pipe.stdin.close() if pipe.poll() == None: # ожидание в 5 сек for t in range(500): time.sleep(0.01) if pipe.poll() != None: break if pipe.poll() == 0: self.kdialog = pipe.stdout.readline().strip() while not "org.kde.kdialog" in self.kdialog: s = pipe.stdout.readline() if s == "": pipe.stdout.close() pipe.stderr.close() self.shutdownDialog() break self.kdialog = s.strip() self.setTitle(self.title) pipe.stdout.close() pipe.stderr.close() else: pipe.stdout.close() pipe.stderr.close() self.shutdownDialog() def shutdownDialog(self): '''Принудительно уничтожить процесс kdialog''' self.kdialog = None pipe = subprocess.Popen("/bin/ps axeo pid,cmd", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) if self.label != None: for s in pipe.stdout.readlines(): if self.label in s: try: os.kill( int(s.split()[0]), 9 ) except (OSError,ValueError): pass self.label = None def setValue(self,value): '''Установить текущее значения для прогресса''' if self.kdialog and value <= self.max: env = "" if self.execenv: env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\ self.execenv)) + " " os.system(env + '/usr/bin/qdbus %s %s value %d >/dev/null'\ %(self.kdialog,self.suffixSet, value)) def setMaximum(self,max): '''Установить максимальное значения для прогресса''' self.max = max if self.kdialog: env = "" if self.execenv: env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\ self.execenv)) + " " os.system(env + '/usr/bin/qdbus %s %s maximum %d >/dev/null'\ %(self.kdialog,self.suffixSet, self.max)) def setTitle(self,title): '''Установить описания прогресса''' self.title = title if self.kdialog: env = "" if self.execenv: env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\ self.execenv)) + " " os.system(env + '/usr/bin/qdbus %s setLabelText "%s" >/dev/null'\ %(self.kdialog,self.title)) def close(self): '''Закрыть прогресс''' self.shutdownDialog() class ProgressTemplate(template): def __init__(self, vars): template.__init__(self,vars) self.progress = ProgressBar(_("Setting up user profile") + " ...") def numberAllTemplates(self, number): self.progress.setMaximum(number) return True def numberProcessTemplates(self,number): self.progress.setValue(number) return True class RsyncProgressBar(ProgressBar): '''Объект запуска rsync для получения количества созданных файлов и при необходимости вывода progressbar ''' # получение номера передаваемого файла из инф потока rsync senderre = re.compile("\[sender\] i=(\d+) ", re.S) # получение номера получаемого файла из потока rsync receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S) pipe = None maximum = 1 copyStarting = False def __init__(self, title, secondtitle, rsyncstr, maximum=1): self.title = title self.secondtitle = secondtitle self.maximum = maximum self.rsyncstr = rsyncstr def getFilesNum(self): '''Получить количество файлов созданных генератором''' if self.pipe: return self.value def getExitCode(self): '''Получить код выхода rsync''' if self.pipe: return os.WEXITSTATUS(self.pipe.wait()) def runsilent(self): '''Запустить rsync без 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): '''Запустить rsync с progressbar''' self.openDialog(self.title,0) 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.senderre.search(s) if q: maximum = int(q.groups()[0]) if self.maximum < maximum: self.maximum = maximum continue q = self.receiverre.search(s) if q: if not self.copyStarting: self.shutdownDialog() self.max = 100 self.openDialog(self.secondtitle) self.copyStarting = True self.value = int(q.groups()[0]) newpercent = self.value * 100 / self.maximum if oldpercent < newpercent: oldpercent = newpercent self.setValue(oldpercent) class cl_client(_error, xmlShare, cl_help, shareFile): """Основной класс для работы клиентских приложений""" # Пути к шаблонам объединяемых с системными # относительный путь при объединении '/' rootTemplatePaths=['/usr/lib/calculate/calculate-client/templates', '/var/calculate/remote/templates', '/var/calculate/templates'] def __init__(self, cmdName): # объект для форматированного вывода cl_help.__init__(self, cmdName) servName = "" if "user" in cmdName: servName = _("user") self.chapter = [\ # расположение разделов на странице # имя раздела, видимый или невидимый, кол. "\n" после # названия раздела, кол. "\n" после раздела # тип раздела ("Copyright",False,0,2,""), (_("Usage"),True,0,1,""), ("Function",False,0,2,""), (_("Examples"),True,1,1,""), (_("Common options"),True,1,0,"options"), ] # имена используемых программ и их номера для доступа к переменным # self.data self.progName = { 'cl-client':0, 'cl-createhome':1, 'cl-sync':2, } # Cвязь длинных опций помощи и выводимых разделов помощи с опциями self.relOptions = {"h":[_("Common options")],} # список разделов, которые на наличие в ней информации # используется для автоматического отображения/скрытия # опций help-имя # Пример: self.relOption = # { "help-all":[_("Common options"], _("Unix service options"), # _("Samba service options"), _("LDAP service options")}] # self.relChapterPass = (_("Common options"),) # это означается что опция будет активна, если только в разделах # кроме Common options есть хоть одна доступная опция. self.relChapterPass = (_("Common options"),) self.data = [\ { #Copyright #'progAccess':(3,), 'helpChapter':"Copyright", 'help':Version }, #Usage { 'progAccess':(0,), 'helpChapter':_("Usage"), 'help': cmdName + " [" + _("options") + "] " + _("domain") }, { 'progAccess':(1,), 'helpChapter':_("Usage"), 'help': cmdName + " " + _("user") }, { 'progAccess':(2,), 'helpChapter':_("Usage"), 'help': cmdName + " [" + _("options") + "] " + _("user") }, # Function { 'progAccess':(0,), 'helpChapter':"Function", 'help':_("Changes settings for connecting to domain \ (calculate-server)") }, { 'progAccess':(1,), 'helpChapter':"Function", 'help':_("Create home directory for the new user account") }, { 'progAccess':(2,), 'helpChapter':"Function", 'help':_("Mounting resources and synchronize the user preferences") }, # Examples { 'progAccess':(0,), 'helpChapter':_("Examples"), 'help':pcs( " cl-client 192.168.0.1", self.column_width, "# " + _("Adds settings for connecting to domain") + \ " (ip 192.168.0.1)", self.consolewidth-self.column_width ) }, # Options {'shortOption':"h", 'longOption':"help", 'helpChapter':_("Common options"), 'help':_("display this help and exit") }, {'progAccess':(0,), 'shortOption':"r", 'helpChapter':_("Common options"), 'help':_("remove the settings for connecting to a domain") }, {'progAccess':(2,), 'longOption':'progress', 'helpChapter':_("Common options"), 'help':_("show progress bar for kde startup (works only with options \ --login)") }, {'progAccess':(1,), 'longOption':'progress', 'helpChapter':_("Common options"), 'help':_("show progress bar for kde startup") }, {'progAccess':(0,1,2), 'longOption':"vars", 'optVal':_("TYPE_VAR"), 'helpChapter':_("Common options"), 'help':_("print variables (TYPE_VAR - all:full var)") }, {'progAccess':(0,1,2), 'longOption':"color", 'optVal':_("WHEN"), 'helpChapter':_("Common options"), 'help':_("control whether color is used. \ WHEN may be 'never', 'always', or 'auto'") }, {'progAccess':(1,), 'shortOption':"f", 'longOption':"force", 'helpChapter':_("Common options"), 'help':_("always join the user templates and preferences") }, {'progAccess':(0,), 'longOption':"install", 'helpChapter':_("Common options"), 'help':_("add use of scripts this package for window manager") }, {'progAccess':(0,), 'longOption':"uninstall", 'helpChapter':_("Common options"), 'help':_("remove use of scripts this package for window manager and, \ if necessary, removes from domain") }, {'progAccess':(0,), 'longOption':"mount", 'helpChapter':_("Common options"), 'help':_("mount [remote] resource for Samba (calculate-server)") }, {'progAccess':(2,), 'longOption':"login", 'helpChapter':_("Common options"), 'help':_("mount user resource")+ " " +\ _("and synchronize the user preferences") }, {'progAccess':(2,), 'longOption':"logout", 'helpChapter':_("Common options"), 'help':_("synchronize the user preferences") + " " +\ _("and umount user resource") }, {'progAccess':(2,), 'longOption':"nosync", 'helpChapter':_("Common options"), 'help':_("not synchronize the user preferences, is used in \ conjunction with the 'login' or 'logout'") }, #{'progAccess':(0,), #'shortOption':"p", #'longOption':"prvar", #'optVal':_("TYPES_VAR"), #'helpChapter':_("Common options"), #'help':_("print variable (filter type - comma delimited)") #}, ] self._cl_help__setParamHelp() # Удаляем ненужный аттрибут класса xmlShare self._createElement = False delattr(self, "_createElement") # Переменная объект Vars self.clVars = False # Переменная объект ldapFunction self.ldapObj = False # Переменная соединение с LDAP сервером self.conLdap = False # Базовый DN LDAP сервера self.baseDN = False # DN сервисов относительно базового ServicesDN = "ou=Services" relGrDN = 'ou=Groups' relUsDN = 'ou=Users' relServDN = 'ou=Unix' relDN = self.addDN(relServDN,ServicesDN) # DN пользователей, относительно базового DN self.relUsersDN = self.addDN(relUsDN, relDN) # DN групп, относительно базового DN self.relGroupsDN = self.addDN(relGrDN, relDN) # DN Samba групп self.relSambaGroupsDN = self.addDN(relGrDN,"ou=Samba", ServicesDN) # Объект хранения переменных self.clVars = False # Файл в ресурсе share для обработки подключения клиента self.replRunFileShare = ".reprun" # файл с дополнительной информацией о профиле пользователя # на данный момент только количество файлов для rsync # используемое для прогрессбара self.configFile = ".calculate.ini" # При включениии репликации # Временные задержки для монтирования в секундах self.sleeps = [0.5, 2, 5] # DN хранения последнего посещенного пользователя self.replHostsDN = self.addDN("ou=Worked","ou=Replication", "ou=LDAP", ServicesDN) # Файл репликации self.replLogoutFile = ".logout" self.skipHomeFile = ["Home","Disks","FTP", self.replLogoutFile] # Если атрибут установлен то значит (ошибка и отмонтируются # пользовательские ресурсы) self.errorAndUnmountUserRes = False # Имя пользователя self.userName = "" def exit(self, exitCode): """Метод выхода при ошибке""" self.errorExit() sys.exit(exitCode) def errorExit(self): """Отмонтирование пользовательских ресурсов при ошибке""" if self.errorAndUnmountUserRes and self.userName: self.umountUserResNoSync(self.userName, False, False, False, True) def __del__(self): """Выполняется при удалении объекта""" self.errorExit() def isRoot(self): """Определяет является ли пользователь root""" if os.getuid() == 0 and os.getgid() == 0: return True else: self.printERROR(_("The user is not root")) return False def createClVars(self, clVars=False): """Создает объект Vars""" if not clVars: clVars = DataVars() clVars.flClient() clVars.flIniFile() # Устанавливаем у объекта объект Vars self.clVars = clVars return True def addDN(self, *arg): """Складывает текстовые элементы DN""" DNs = [] for dn in arg: if dn: DNs.append(dn) return ','.join(DNs) def searchLdapDN(self, name, relDN, attr, retAttr=None): """Находит DN в LDAP""" baseDN = self.clVars.Get("ld_base_dn") DN = self.addDN(relDN,baseDN) searchScope = SCOPE_ONELEVEL searchFilter = "%s=%s" %(attr,name) retrieveAttributes = retAttr resSearch = self.ldapObj.ldapSearch(DN, searchScope, searchFilter, retrieveAttributes) return resSearch def searchPrevHost(self, userName): """Находит сервер к которому был подключен пользователь""" # Короткое имя системы osLinuxShort = self.clVars.Get("os_linux_shortname") # Имя для поиска в служебной ветке репликации userLogin = "%s@%s"%(userName,osLinuxShort) resSearch = self.searchLdapDN(userLogin, self.replHostsDN, "uid") return resSearch def searchUnixUser(self, userName): """Находит пользователя сервиса Unix""" resSearch = self.searchLdapDN(userName, self.relUsersDN, "uid") return resSearch def searchUnixGid(self, groupId): """Находит группу сервиса Unix по ёе id в ветке Unix""" resSearch = self.searchLdapDN(str(groupId), self.relGroupsDN, "gidNumber") return resSearch def searchSambaGid(self, groupId): """Находит группу сервиса Unix по ёе id в ветке Samba""" resSearch = self.searchLdapDN(str(groupId), self.relSambaGroupsDN, "gidNumber") return resSearch def getLdapObjBind(self, host, printError=True): """Получаем объект ldapFunction Соединяемся пользователем bind В выходном объекте есть соединение с LDAP сервером: self.conLdap """ self.createClVars(self.clVars) bindDn = self.clVars.Get("ld_bind_dn") bindPw = self.clVars.Get("ld_bind_pw") if not (bindDn or bindPw): if printError: self.printERROR(_("Not found LDAP bind DN or password") +\ " ...") return False ldapObj = ldapFun(bindDn, bindPw, host) if ldapObj.getError(): if printError: self.printERROR (_("LDAP connect error") + ": " +\ ldapObj.getError().strip()) return False # Устанавливаем у объекта соединение и объект LDAP функций self.ldapObj = ldapObj self.conLdap = ldapObj.conLdap return True def searchLineInFile(self, name, fileName, numEl=0): """Ищет строку в которой есть название разделенное ':' в файле похожем на /etc/passwd""" if os.path.exists(fileName): FD = open(fileName) lines = FD.readlines() FD.close() lineFound = "" for line in lines: if name == line.split(":")[numEl]: lineFound = line break if lineFound: return lineFound else: return False def searchPasswdUser(self, userName): """Ищет пользователей в /etc/passwd""" filePasswd = "/etc/passwd" return self.searchLineInFile(userName, filePasswd) def searchGroupGid(self, groupId): """Ищет gid в /etc/group""" gid = str(groupId) fileGroup = "/etc/group" return self.searchLineInFile(gid, fileGroup, 2) def getUserLdapInfo(self, userName, printError=True): """Выдаем uid и gid пользователя""" searchUser = self.searchUnixUser(userName) if not searchUser: if printError: self.printERROR(_("User %s not found in Unix service")\ %str(userName)) return False uid = False gid = False fullName = "" mail = "" jid = "" group = "" if searchUser[0][0][1].has_key('uidNumber'): uid = searchUser[0][0][1]['uidNumber'][0] if searchUser[0][0][1].has_key('gidNumber'): gid = searchUser[0][0][1]['gidNumber'][0] searchGroup = self.searchUnixGid(gid) if searchGroup and searchGroup[0][0][1].has_key('cn'): group = searchGroup[0][0][1]['cn'][0] else: searchGroup = self.searchSambaGid(gid) if searchGroup and searchGroup[0][0][1].has_key('cn'): group = searchGroup[0][0][1]['cn'][0] if searchUser[0][0][1].has_key('cn'): fullName = searchUser[0][0][1]['cn'][0] if searchUser[0][0][1].has_key('mail'): mail = searchUser[0][0][1]['mail'][0] if searchUser[0][0][1].has_key('registeredAddress'): jid = searchUser[0][0][1]['registeredAddress'][0] if searchUser[0][0][1].has_key('homeDirectory'): home = searchUser[0][0][1]['homeDirectory'][0] if uid and gid: return (uid, gid, fullName, mail, jid ,home, group) else: return () def applyTemplatesFromUser(self,progress=False): """Применяем шаблоны для пользователя""" # Cоздаем объект шаблон if progress: clTempl = ProgressTemplate(self.clVars) else: clTempl = template(self.clVars) # Объединяем шаблоны dirsFiles = clTempl.applyTemplates() if progress: clTempl.progress.close() if clTempl.getError(): self.printERROR(clTempl.getError()) return False else: return dirsFiles def applyTemplatesFromSystem(self): """Применяем шаблоны для cистемы""" # Cоздаем объект шаблон clTempl = template(self.clVars) # Объединяем шаблоны dirsFiles = clTempl.applyTemplates() if clTempl.getError(): self.printERROR(clTempl.getError()) return False else: return dirsFiles def getUserPassword(self, pwDialog=False): """Получить пароль у пользователя pwDialog - приглашение ввода пароля """ if not pwDialog: pwDialog = _("Password") userPwd = getpass.getpass(pwDialog+":") return userPwd def execProg(self, cmdStrProg, inStr=False, retFull=True, envProg={}): """Выполняет внешнюю программу Параметры: cmdStrProg внешняя программа inStr данные передаваемые программе на страндартный вход. Возвращаемые параметры: строка которую выведет внешняя программа или False в случае ошибки """ env_path = {"PATH":getpathenv()} env = {} env.update(os.environ.items() + env_path.items() + envProg.items()) retCode,programOut = runOsCommand(cmdStrProg,inStr,retFull,env) if not retCode: return programOut return False def getUserPasswdInfo(self, userName): """получаем uid и gid пользователя из /etc/passwd""" resPasswd = self.searchPasswdUser(userName) if resPasswd: uid = resPasswd.split(":")[2] gid = resPasswd.split(":")[3] fullName = resPasswd.split(":")[4] mail = "" group = "" jid = "" home = os.path.join("/home",userName) resGroup = self.searchGroupGid(gid) if resGroup: group = resGroup.split(":")[0] return (uid, gid, fullName, mail, jid, home, group) else: return False def setDaemonAutostart(self, daemon): """Прописывает демона в автозагрузку""" execStr = "rc-update add %s default" %daemon textLine = self.execProg(execStr) if textLine: return True else: self.printERROR(_("ERROR") + ": " + execStr) self.printERROR(_("Can not add at default runlevel")) return False def delDaemonAutostart(self, daemon): """Удаляет демона из автозагрузки""" execStr = "rc-update del %s default" %daemon textLine = self.execProg(execStr) if textLine: return True else: self.printERROR(_("ERROR") + ": " + execStr) self.printERROR(_("Can not delete from default runlevel")) return False def createHome(self, userName, applyAlways=False, progress=False): """Создание пользовательской директории с настройками для kde4""" # Имя пользователя self.userName = userName # Проверяем на root if not self.isRoot(): return False # Создаем объект переменных self.createClVars() uidGid = False # Подсоединяемся к серверу domain = self.clVars.Get("cl_remote_host") connectLdap = False if domain: if not self.getLdapObjBind(domain): return False connectLdap = True if connectLdap: # uid и gid и mail из Ldap uidGid = self.getUserLdapInfo(userName,False) if not domain: # uid и gid и mail из passwd uidGid = self.getUserPasswdInfo(userName) if not uidGid: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True self.printERROR(_("Not found user uid and gid")) return False uid = int(uidGid[0]) gid = int(uidGid[1]) fullName = uidGid[2] mail = uidGid[3] jid = uidGid[4] homeDir = uidGid[5] group = uidGid[6] if not group: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True self.printERROR(_("Not found user primary group (gid=%s)"%str(gid))) return False # Создаем пользовательскую директорию homeExists = os.path.exists(homeDir) # Первый проход self.clVars.Set('cl_pass_step','first',True) if homeExists: self.printWARNING(_("Home dir %s exists")%homeDir) if set(os.listdir(homeDir))-set(self.skipHomeFile): if not applyAlways: # Второй и последующие проходы self.clVars.Set('cl_pass_step','next',True) self.printSUCCESS(_("Apply always templates") + " ...") # Записываем переменные self.clVars.Set('ur_login', userName) self.clVars.Set('ur_fullname', fullName) self.clVars.Set('ur_mail', mail) self.clVars.Set('ur_jid', jid) self.clVars.Set('ur_group', group) # Применяем шаблоны для пользователя dirsAndFiles = self.applyTemplatesFromUser(progress) if not dirsAndFiles: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True self.printERROR(_("Can not apply user profile")) return False if not homeExists: self.printSUCCESS(_("Created home dir %s")%homeDir + " ...") self.printSUCCESS(_("User account is configured") + " ...") return True def isDomain(self): """Находится ли компьютер в домене""" self.createClVars(self.clVars) foundMountRemote =self.isMount("/var/calculate/remote" ,"cifs") foundMountHome =self.isMount("/var/calculate/client-home","none",False) if not (self.clVars.Get("cl_remote_host") and foundMountRemote and\ foundMountHome): self.printERROR(_("The computer is not in domain")) return False return (foundMountRemote,foundMountHome) def killRsync(self): """Убивает все процессы rsync и cl-sync --login""" listProcess = self.execProg("ps ax",False,False) if not listProcess: return False killPid = [] flagError = False for process in listProcess: if "rsync" in process: killPid.append(process.split(" ")[0]) if "--login" in process and "cl-sync" in process: killPid.append(process.split(" ")[0]) strPid = str(os.getpid()) # удалим свой pid из списка завершаемых процессов if strPid in killPid: killPid.remove(strPid) if killPid and " ".join(killPid).strip(): textLine = self.execProg("kill -9 %s" %" ".join(killPid)) if not (textLine == None): self.printERROR(_("Can not 'kill %s'")\ %" ".join(killPid)) flagError = True if flagError: return False else: return True def isMount(self, pathMount ,typeMount, secondPath=True): """Примонтирована ли директория""" path = os.path.realpath(pathMount) foundMount = False if secondPath: reFoundMount = re.compile("on\s+%s\s+type\s+%s"%(path,typeMount)) else: reFoundMount = re.compile("%s\s+.+type\s+%s"%(path,typeMount)) resMount = self.execProg("mount",False,False) if resMount and type(resMount) == types.ListType: for string in resMount: if reFoundMount.search(string): foundMount = string break return foundMount def relevanceTemplates(self, hostAuth): """Определяем актуальность наложенных шаблонов в зависимости от версии программы Перед запуском обязательно должен быть определен объект переменных self.clVars Если актуальны - True, если нет False """ # 'local' или имя хоста if hostAuth != self.clVars.Get("os_remote_auth"): return False clTempl = template(self.clVars) # Текущая версия программы currentVersion = self.clVars.Get("cl_ver") # Версия программы которая ранее работала с шаблонами previousVersion = self.clVars.Get("os_remote_client") cVersion, pVersion =clTempl._convertVers(currentVersion,previousVersion) # Если версии программ не равны то шаблоны не актуальные if cVersion != pVersion: return False return True def getPathTemplates(self, listPath): """Получаем список директорий хранения шаблонов""" """список накладываемых шаблонов при установке, наложении шаблонов""" profpath = [] for profPath in listPath: if os.path.isdir(profPath): profpath.append(profPath) return profpath def applyRelevanceTemplates(self, hostAuth): """Накладывает релевантные шаблоны Перед запуском обязательно должен быть определен объект переменных self.clVars """ if not self.relevanceTemplates(hostAuth): # Обнуляем переменную удаленный хост if hostAuth == "local": self.clVars.Set("cl_remote_host","",True) # Устанавливаем действие templates_domain self.clVars.Set("cl_pass_type","domain",True) # Наложим шаблоны templates/domain # Новые пути к шаблонам profPaths = self.getPathTemplates(self.rootTemplatePaths) if not profPaths: self.printERROR(_("Empty template paths %s")\ %", "(self.rootTemplatePaths)) return False # Изменяем переменную хранения шаблонов self.clVars.Set("cl_template_path",profPaths,True) # Наложим шаблоны dirsAndFiles = self.applyTemplatesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'templates/domain' templates")) return False if hostAuth == "local": self.printOK(_("Set templates of local mode")) else: self.printOK(_("Set templates of network mode")) currentVersion = self.clVars.Get("cl_ver") self.clVars.Write("os_remote_client", currentVersion) self.clVars.Write("os_remote_auth", hostAuth) return True def mountRemote(self): """Монтирование remote и домашней директории если компьютер в домене а так-же ввод в домен если найдено имя хоста и пароль для подключения """ # Проверяем на root if not self.isRoot(): return False self.createClVars(self.clVars) domain = self.clVars.Get("cl_remote_host") if domain: foundMountRemote = self.isMount("/var/calculate/remote" ,"cifs") foundMountHome = self.isMount("/var/calculate/client-home","none", False) else: self.printWARNING(_("This computer is not in domain")) # Если шаблоны не актуальны накладываем новую версию шаблонов if not self.applyRelevanceTemplates("local"): return False return True pathHome = "/var/calculate/client-home" if foundMountRemote: self.printWARNING(_("Samba resource [%s] is mount")%"remote" + \ " ...") if foundMountHome: self.printWARNING(str(pathHome) + " " +_("is mount")+ " ...") if foundMountHome and foundMountRemote: # Накладываем сетевые шаблоны if domain: self.clVars.flIniFile() if not self.applyRelevanceTemplates(domain): return False return True flagLocalTemplate = False if not foundMountRemote: pathRemote = "/var/calculate/remote" pwdRemote = self.clVars.Get("cl_remote_pw") if not (domain and pwdRemote): self.printERROR(_("Not found variable")+\ ": cl_remote_pw ...") return False if not os.path.exists(pathRemote): os.makedirs(pathRemote) #mountStr = "mount -t cifs -o user=client //%s/remote %s"\ # %(domain,pathRemote) #textLine= self.execProg(mountStr, None, True, {"PASSWD":pwdRemote}) textLine = self.mountSleepRes("client", pwdRemote, None, None, "remote", pathRemote) if not (textLine == None): self.printWARNING(_("Can not mount Samba resource [%s]")%\ "remote" + " ...") flagLocalTemplate = True # Если шаблоны не актуальны накладываем новую версию шаблонов if not self.applyRelevanceTemplates("local"): return False if foundMountHome: umountStr = "umount /home" textLine = self.execProg(umountStr) if not (textLine == None): self.printERROR(_("Can not unmount") + " /home") return False return True self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\ " ...") if (not foundMountHome) and (not flagLocalTemplate): if not os.path.exists(pathHome): os.makedirs(pathHome) mountStr = "mount -o bind %s /home" %pathHome textLine = self.execProg(mountStr) if not (textLine == None): self.printERROR(_("Can not mount") + " " + str(pathHome) +\ " ...") return False self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\ " ...") # Накладываем сетевые шаблоны if domain: self.clVars.flIniFile() if not self.applyRelevanceTemplates(domain): return False self.restartDBus() return True def delDomain(self): """выводим из домена""" # Проверяем на root if not self.isRoot(): return False self.createClVars() pathRemote = "/var/calculate/remote" pathHome = "/var/calculate/client-home" foundMountRemote = self.isMount(pathRemote ,"cifs") foundMountHome = self.isMount(pathHome ,"none",False) domain = self.clVars.Get("cl_remote_host") if not domain: self.printWARNING(_("The computer is not in domain")) return True if foundMountRemote: textLineUmount = self.umountSleepPath(pathRemote) if not textLineUmount: return False if foundMountHome: textLineUmount = self.umountSleepPath(pathHome ,"none", False) if not textLineUmount: return False self.clVars.Delete("cl_remote_host","local") self.clVars.Delete("cl_remote_pw","local") self.clVars.Set("cl_remote_host","",True) self.clVars.Set("cl_remote_pw","",True) # Наложим шаблоны templates/domain # Новые пути к шаблонам # Устанавливаем действие templates_domain self.clVars.Set("cl_pass_type","domain",True) # Новые пути к шаблонам profPaths = self.getPathTemplates(self.rootTemplatePaths) if not profPaths: self.printERROR(_("Empty template paths %s")\ %", "(self.rootTemplatePaths)) return False # Изменяем переменную хранения шаблонов self.clVars.Set("cl_template_path",profPaths,True) # Наложим шаблоны dirsAndFiles = self.applyTemplatesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'templates/domain' templates")) return False if not self.delDaemonAutostart("client"): return False self.printOK(_("Computer removed from domain %s")%domain + " ...") return True def uninstallClient(self): """Наложение шаблона клиента по умолчанию и при необходимости отключение от домена""" if not self.isRoot(): return False self.createClVars() clVars = self.clVars messageOk = _("Removed use of scripts this package for window \ manager") + " ..." # Устанавливаем переменную шаблона clVars.Set("cl_pass_run","off",True) # Устанавливаем действие templates_client clVars.Set("cl_pass_type","install",True) # Новые пути к шаблонам profPaths = self.getPathTemplates(self.rootTemplatePaths) if not profPaths: self.printERROR(_("Empty template paths %s")\ %", "(self.rootTemplatePaths)) return False # Изменяем переменную хранения шаблонов clVars.Set("cl_template_path",profPaths,True) # Наложим шаблоны dirsAndFiles = self.applyTemplatesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'templates/client' templates")) return False remoteHost = clVars.Get("cl_remote_host") if remoteHost: if not self.delDomain(): return False self.printOK(messageOk) return True def installClient(self, clVars=False, printSuccess=True): """Наложение шаблона клиента и при необходимости подключение к домену""" if not self.isRoot(): return False if not clVars: #Создаем объект переменных self.createClVars() clVars = self.clVars messageOk = _("Added use of scripts this package for window \ manager") + " ..." # Устанавливаем переменную шаблона clVars.Set("cl_pass_run","on",True) # Устанавливаем действие templates_client clVars.Set("cl_pass_type","install",True) # Новые пути к шаблонам profPaths = self.getPathTemplates(self.rootTemplatePaths) if not profPaths: self.printERROR(_("Empty template paths %s")\ %", "(self.rootTemplatePaths)) return False # Изменяем переменную хранения шаблонов clVars.Set("cl_template_path",profPaths,True) # Наложим шаблоны dirsAndFiles = self.applyTemplatesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'templates/client' templates")) return False if printSuccess: self.printOK(messageOk) return True def addDomain(self, domainName): """Вводим в домен""" # Проверяем на root if not self.isRoot(): return False # Создаем объект переменных self.createClVars() netDomain = self.clVars.Get("os_net_domain") # Получам имя сервера (домена) if "." in domainName: domain = domainName else: domain = "%s.%s" %(domainName,netDomain) execStr = "ping -c 2 -i 0.3 %s" %domain resPing = self.execProg(execStr, False, False) if not resPing: self.printERROR(_('Can not execute "%s"')%execStr) return False foudHost = False foudHostSamba = False foundMountRemote = False reFoundHost = re.compile("(\d+)\% packet loss") if resPing and type(resPing) == types.ListType and len(resPing)>=2: pingStr = resPing[-2].strip() reSearch = reFoundHost.search(pingStr) if reSearch and reSearch.group(1) == "0": foudHost = True if not foudHost: self.printERROR(_("Not found domain %s")%domain) return False reFoundHostSamba = re.compile("Server=\[Samba.+\]") resSmbClient = self.execProg("smbclient -N -L %s" %domain, False,False) if resSmbClient and type(resSmbClient) == types.ListType: for string in resSmbClient: if reFoundHostSamba.search(string): foudHostSamba = True break if not foudHostSamba: self.printERROR(_("Not found Samba server in %s")%domain) return False pwd = False if self.clVars.Get("cl_remote_host") and \ self.clVars.Get("cl_remote_host") != domain: if not self.delDomain(): return False elif self.clVars.Get("cl_remote_host") and \ self.clVars.Get("cl_remote_host") == domain: pwd = self.clVars.Get("cl_remote_pw") foundMountRemote =self.isMount("/var/calculate/remote" ,"cifs") foundMountHome =self.isMount("/var/calculate/client-home","none",False) if foundMountRemote: self.printWARNING(_("Samba resource [%s] is mount")%\ "remote" + " ...") else: if pwd: userPwd = pwd else: userPwd=self.getUserPassword(\ _("Domain password for the desktop")) pathRemote = "/var/calculate/remote" pwdRemote = userPwd if not os.path.exists(pathRemote): os.makedirs(pathRemote) mountStr = "mount -t cifs -o user=client //%s/remote %s"\ %(domain,pathRemote) textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote}) if not (textLine == None): self.printERROR(_("Can not mount Samba resource [%s]")%\ "remote" + " ...") return False else: self.printSUCCESS(_("Mount Samba resource [%s]")%"remote" + \ " ...") self.clVars.Write("cl_remote_host", domain, False, "local") self.clVars.Write("cl_remote_pw", userPwd, False, "local") pathHome = "/var/calculate/client-home" if foundMountHome: self.printWARNING(str(pathHome)+ " " +_("is mount")+ " ...") else: if not os.path.exists(pathHome): os.makedirs(pathHome) mountStr = "mount -o bind %s /home" %pathHome textLine = self.execProg(mountStr) if not (textLine == None): self.printERROR(_("Can not mount") + " " + str(pathHome) +\ " ...") return False self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\ " ...") self.clVars.flIniFile() #считаем переменные для клиента servDn = self.clVars.Get("ld_services_dn") unixDN = self.clVars.Get("ld_unix_dn") bindDn = self.clVars.Get("ld_bind_dn") bindPw = self.clVars.Get("ld_bind_pw") # запишем их if not (servDn and unixDN and bindDn and bindPw): self.printERROR(_("Not found variables") + ":") self.printERROR("ld_services_dn or ld_unix_dn \ or ld_bind_dn or ld_bind_pw") return False # Наложим шаблоны templates/client if not self.installClient(self.clVars, False): return False # Наложим шаблоны templates/domain # Устанавливаем действие templates_client self.clVars.Set("cl_pass_type","domain",True) # Новые пути к шаблонам profPaths = self.getPathTemplates(self.rootTemplatePaths) if not profPaths: self.printERROR(_("Empty template paths %s")\ %", "(self.rootTemplatePaths)) return False # Изменяем переменную хранения шаблонов self.clVars.Set("cl_template_path",profPaths,True) # Наложим шаблоны dirsAndFiles = self.applyTemplatesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'templates/domain' templates")) return False # Рестартуем dbus self.restartDBus() if not self.setDaemonAutostart("client"): return False self.printOK(_("Computer added to domain %s")%domain + " ...") return True def restartDBus(self): """Перезапускаем службу D-Bus""" dbusDaemon = '/etc/init.d/dbus' if os.path.exists(dbusDaemon): if os.system(dbusDaemon + ' restart &>/dev/null') != 0: self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...") return False return True def removePrivateFiles(self, userHome): """Удаление приватных файлов""" privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl'] for prFile in privateFiles: rmFile = os.path.join(userHome, prFile) if os.path.exists(rmFile): os.remove(rmFile) return True def umountSleepPath(self, path, typeMount='cifs', secondPath=True): """Отмонтирует путь при неудаче задержка потом повтор""" #Проверяем на монтирование директории if self.isMount(path, typeMount, secondPath): textLine = self.execProg("umount %s"%path) if textLine != None: i = 0 flagError = False while (i1: foundTwoSession = True self.printERROR(\ _("Second X session for user %s can not be opened.")\ %userName) break return foundTwoSession def mountSambaRes(self,userName,userPwd,uid,gid,res,path, mountUidList=['ftp','home','share']): """Монтирует Samba ресурсы""" if not uid is None and not gid is None and res in mountUidList: # Монтируем директории c uid mountStr="mount -t cifs -o user=%s,uid=%s,gid=%s,noperm"\ %(userName,uid,gid) +\ " //%s/%s %s" %(self.clVars.Get("cl_remote_host"), res, path) else: # Монтируем директории mountStr="mount -t cifs -o user=%s"%(userName)+\ " //%s/%s %s" %(self.clVars.Get("cl_remote_host"), res, path) textLine = self.execProg(mountStr, None, True, {"PASSWD":userPwd}) return textLine def isCorrectTemplateOnLocalServer(self,userName): """Узнать находится ли актуальный профиль пользователя на локальном сервере """ searchPrevHost = self.searchPrevHost(userName) if searchPrevHost and searchPrevHost[0][0][1].has_key('host'): prevHost = searchPrevHost[0][0][1]['host'][0] else: prevHost = None # если местоположение актуального профиля пользователя найти не удалось # или его местоположение не на локальном сервере if not prevHost or prevHost == self.clVars.Get('cl_remote_host'): return True else: return False def mountUserRes(self, userName, sync=True, progress=False): """Монтирование пользовательских ресурсов и синхронизация настроек""" # Имя пользователя self.userName = userName # Проверяем на root if not self.isRoot(): return False # Проверка на повторный вход пользователя if self.isTwoSessionsUser(userName): return False self.createClVars() # В случае компьютера вне домена if not self.clVars.Get("cl_remote_host"): self.printSUCCESS(_("To be used local profile.")) return True # Проверим что компьютер в домене и смонтирован [remote] connectDomain = self.isDomain() if not connectDomain: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False elif not connectDomain[0]: self.printERROR(_("Can not mount Samba resource [%s]")%"remote"+ \ " ...") # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Подсоединяемся к серверу domain = self.clVars.Get("cl_remote_host") if not self.getLdapObjBind(domain): # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # homeDir из LDAP resLdap = self.getUserLdapInfo(userName) if not resLdap: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False uid = int(resLdap[0]) gid = int(resLdap[1]) homeDir = resLdap[5] # При отсуствии создаем домашнюю директорию if not os.path.exists(homeDir): os.makedirs(homeDir) os.chown(homeDir,uid,gid) os.chmod(homeDir,0700) # записываем в .logout файл статус "в процессе" logOutFile = os.path.join(homeDir,self.replLogoutFile) self.createUserFile(logOutFile,"PROCESS", uid, gid) # Получаем пароль пользователя из ключей ядра userPwd = _cl_keys.getKey(userName) if not userPwd: self.printERROR(_("Not found user password")) # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False home = os.path.split(homeDir)[0] pathRemote = [] # Удаленный ресурс share pathRemote.append((os.path.join(homeDir,"Disks"), "share")) # Удаленный ресурс - профиль пользователя pathRemote.append((os.path.join(home,"." + userName), "unix")) # Удаленный ресурс home pathRemote.append((os.path.join(homeDir,"Home"), "homes")) if self.clVars.Get("cl_remote_ftp"): # Удаленный ресурс ftp pathRemote.append((os.path.join(homeDir,"FTP"), "ftp")) flagError = False # Запускаемый файл на сервере в случае репликации pathReplRun = "" # Путь к профилю пользователя по умолчанию defaultPath = "" # Хост пользователя по умолчанию defaultHost = self.clVars.Get("cl_remote_host") for path, res in pathRemote: # Создаем директории для монтирования if not os.path.exists(path): try: os.mkdir(path) os.chown(path, uid, gid) os.chmod(path,0700) except OSError: self.printERROR(_("Error creating directory")) self.printERROR(_("Permission denied: '%s'")%path) flagError = True break # Проверяем на монтирование директории if self.isMount(path, 'cifs'): continue # В случае репликации не монтируем профиль пользователя if pathReplRun and res=="unix": defaultPath = path continue # Монтируем Samba ресурс textLine = self.mountSambaRes(userName,userPwd,uid,gid,res,path) if not (textLine == None): self.printERROR(_("Can not mount Samba resource [%s]")\ %res + " ...") flagError = True break # В случае репликации if res == "share": # Проверка на репликацию pathReplRun = os.path.join(path, self.replRunFileShare) if os.path.exists(pathReplRun): FD = open(pathReplRun) # Создание директорий пользователя на сервере # в случае если их нет FD.close() else: pathReplRun = "" if flagError: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False flagFirstSync = False if pathReplRun or sync: # Ошибка при монтировании unix ресурса удаленного сервера при # включенной репликации replErrorMount = False # Ошибка при cинхронизации unix ресурса удаленного сервера при # включенной репликации replErrorSync = False # Синхронизируем настройки if sync: # Короткое имя системы osLinuxShort = self.clVars.Get("os_linux_shortname") # В случае репликации prevHost = "" # Монтирование по умолчанию (default - cвой сервер, remote - чужой) mountServer = "" if pathReplRun: searchPrevHost = self.searchPrevHost(userName) if searchPrevHost and searchPrevHost[0][0][1].has_key('host'): prevHost = searchPrevHost[0][0][1]['host'][0] # Монтируем ресурс unix текущего сервера mountServer = "default" textLine = self.mountSleepRes(userName,userPwd,uid,gid, "unix",defaultPath) if not (textLine == None): self.printERROR(_("Can not mount Samba resource [%s]")\ %"unix" + " ...") # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Переносим настройки пользователя в новую директорию # .CLD self.upgradeTemplateClient(userName, homeDir) # Если на текущем сервере в ресурсе unix есть файлы # то синхронируем настройки # Получаем директорию хранения профиля пользователя на сервере homeTemplate = os.path.join(home, "." + userName, osLinuxShort) if not os.path.exists(homeTemplate): homeTemplate = os.path.join(home, "." + userName, "." + osLinuxShort) if os.listdir(homeTemplate): if not self.syncUser(userName, homeDir, "login", uid, gid,\ progress=progress, host=self.clVars.Get('cl_remote_host')): # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False else: #Удаляем ненужные файлы 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 self.removeDir(delFile): # Отмонтируем пользовательские ресурсы # в случае ошибки self.errorAndUnmountUserRes = True return False elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]): os.remove(delFile) # Отмонтируем ресурс textLine = self.umountSleepPath(defaultPath) if not textLine: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False if prevHost and prevHost != self.clVars.Get("cl_remote_host"): # Монтируем настройки пользователя удаленного сервера # если сервер найден self.clVars.Set("cl_remote_host", prevHost, True) mountServer = "remote" textLine = self.mountSleepRes(userName,userPwd,uid,gid, "unix",defaultPath) self.clVars.Set("cl_remote_host", defaultHost, True) if not (textLine == None): if self.isMount(defaultPath, 'cifs'): textLine = self.umountSleepPath(defaultPath) if not textLine: # Отмонтируем пользовательские ресурсы # в случае ошибки self.errorAndUnmountUserRes = True return False # Монтируем текущий сервер если ошибка подключения к # к найденному серверу mountServer = "default" textLine = self.mountSleepRes(userName,userPwd,uid,gid, "unix",defaultPath) if not (textLine == None): self.printERROR(_("Can not mount Samba resource \ [%s]")%"unix" + " ...") # Отмонтируем пользовательские ресурсы # в случае ошибки self.errorAndUnmountUserRes = True return False replErrorMount = True else: mountServer = "default" # Монтируем текущий сервер если сервер не найден в LDAP textLine = self.mountSleepRes(userName,userPwd,uid,gid, "unix",defaultPath) if not (textLine == None): self.printERROR(_("Can not mount Samba resource \ [%s]")%"unix" + " ...") # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # если репликации нет, то prevHost и remote_host одинаковые else: prevHost=self.clVars.Get('cl_remote_host') # Синхронизируем настройки пользователя if not (pathReplRun and mountServer == "default"): # Переносим настройки пользователя в новую директорию # .CLD self.upgradeTemplateClient(userName, homeDir) # Получаем директорию хранения профиля пользователя на сервере homeTemplate = os.path.join(home, "." + userName, osLinuxShort) if not os.path.exists(homeTemplate): homeTemplate = os.path.join(home, "." + userName, "." + osLinuxShort) if os.listdir(homeTemplate): if not self.syncUser(userName, homeDir, "login", uid, gid,\ progress=progress,host=prevHost): if pathReplRun and mountServer == "remote": replErrorSync = True else: # Отмонтируем пользовательские ресурсы # в случае ошибки self.errorAndUnmountUserRes = True return False else: #Удаляем ненужные файлы 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 self.removeDir(delFile): # Отмонтируем пользовательские ресурсы # в случае ошибки self.errorAndUnmountUserRes = True return False elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]): os.remove(delFile) if pathReplRun and mountServer == "remote": # В случае репликации перемонтируем ресурс профиль пользователя # на текущий сервер (в случае необходимости) textLine = self.umountSleepPath(defaultPath) if not textLine: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False textLine = self.mountSambaRes(userName,userPwd,uid,gid, "unix",defaultPath) if not (textLine == None): self.printERROR(_("Can not mount Samba resource [%s]")\ %"unix" + " ...") # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False if pathReplRun: if replErrorMount or replErrorSync: self.createUserFile(logOutFile,"ERROR", uid, gid) else: self.createUserFile(logOutFile,"SUCCESS", uid, gid) else: self.createUserFile(logOutFile,"SUCCESS", uid, gid) self.printSUCCESS(_("Mounted user resource of the domain")) self.printOK(_("Get a user profile from the domain") + " ...") return True def mountSleepRes(self,userName,userPwd,uid,gid,res,path): """Монтирует ресурс при неудаче задержка потом повторное монитрование""" textLine = self.mountSambaRes(userName,userPwd,uid,gid,res,path) if not (textLine == None): # Проверяем на монтирование директории if self.isMount(path, 'cifs'): textLineUmount = self.umountSleepPath(path) if not textLineUmount: return False i = 0 while (i/dev/null"%(srcFile,destFile)) != 0: self.printERROR(_("Can not move %s")%srcFile + " " +\ _("to %s")%destFile) flagError=True break if flagError: return False return True def upgradeTemplateClient(self, userName, userHome): """Переносит данные клиента в директорию .CLD Перед вызовом этого метода обязательно должен быть определен атрибут объекта self.clVars - объект переменных """ # Директория хранения старого профиля пользователя home = os.path.split(userHome)[0] pathOldTemplate = os.path.join(home, "." + userName) if os.path.exists(pathOldTemplate): osLinuxShort = self.clVars.Get("os_linux_shortname") # В случае пустой директории профиля пользователя if not os.listdir(pathOldTemplate): pathNewTemplate = os.path.join(pathOldTemplate, "." + osLinuxShort) # Создаем директорию для хранения профиля пользователя os.mkdir(pathNewTemplate) os.chmod(pathNewTemplate, 0700) return True skipDirs = [".CLD", ".CLDX", "." + osLinuxShort] # Если есть скрытые файлы кроме skipDir # а так-же нет файлов skipDir - делаем апгрейд if filter(lambda x: x[0]==".", list(set(os.listdir(pathOldTemplate))-set(skipDirs))) and\ len(filter(lambda x: not os.path.exists(os.path.join(pathOldTemplate,x)), skipDirs))==len(skipDirs): pathNewTemplate = os.path.join(pathOldTemplate,".CLD") # Копируем профиль пользователя в новое место try: self.copyTemplateDir(pathNewTemplate, pathOldTemplate) except: self.printERROR(_("Error updating user profile")) self.printERROR(_("path: %s")%pathNewTemplate) return False pathNewTemplate = os.path.join(pathOldTemplate, "." + osLinuxShort) if not os.path.exists(pathNewTemplate): # Создаем директорию для хранения профиля пользователя os.mkdir(pathNewTemplate) os.chmod(pathNewTemplate, 0700) return True def syncUser(self, userName, userHome, sync, uid, gid, progress=False,\ host="default"): """Синхронизация пользовательских настроек Перед вызовом этого метода обязательно должен быть определен атрибут объекта self.clVars - объект переменных """ home = os.path.split(userHome)[0] osLinuxShort = self.clVars.Get("os_linux_shortname") homeTemplate = os.path.join(home, "." + userName, osLinuxShort) if not os.path.exists(homeTemplate): homeTemplate = os.path.join(home, "." + userName, "." + osLinuxShort) flagError = False execStr = "" if sync == "login": # исключаемые пути при синхронизации # /.local/share/akonadi/db_data # хранит базу acanadi # /.mozilla/firefox/calculate.default/urlclassifier3.sqlite # хранит БД для firefox # /.local/share/mime/mime.cache # отключение ошибочного кэширования изображений # /.kde4/share/apps/nepomuk/repository/main/data # база nepomuk # /.VirtualBox # содержит данные о виртуальных машинах if os.path.exists(userHome) and\ os.path.exists(homeTemplate): execStr = '/usr/bin/rsync --delete-excluded --delete \ --exclude="/.googleearth" --exclude="/.kde4/share/config/phonondevicesrc" \ --exclude="*~" --exclude="/Home" --exclude="/Disks" --exclude="/FTP" \ --exclude="/.local/share/akonadi/db_data" --exclude="/.VirtualBox" \ --exclude="/.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ --exclude="/.local/share/mime/mime.cache" \ --exclude="/.kde4/share/apps/nepomuk/repository/main/data" \ --exclude="/.logout" \ --exclude="/.Xauthority" \ --filter="P /.googleearth" --filter="P /Home" --filter="P /Disks" \ --filter="P /.local/share/akonadi/db_data" --filter="P /.VirtualBox" \ --filter="P /.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ --filter="P /.local/share/mime/mime.cache" \ --filter="P /.kde4/share/apps/nepomuk/repository/main/data" \ --filter="P /.logout" \ --filter="P /.Xauthority" \ --filter="P /FTP" -a -x -v -v -v -v %s/ %s/' %(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 \ --exclude="/.googleearth" --exclude="/Home" --exclude="/Disks" --exclude="/FTP"\ --exclude="*~" --exclude="/.kde4/cache-*" --exclude="/.kde4/tmp-*" \ --exclude="/.local/share/akonadi/db_data" --exclude="/.VirtualBox" \ --exclude="/.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ --exclude="/.local/share/mime/mime.cache" \ --exclude="/.Xauthority" \ --exclude="/.kde4/share/apps/nepomuk/repository/main/data" \ --exclude="/.kde4/socket-*" --exclude="/.kde4/share/config/phonondevicesrc"\ -a -x -v -v -v -v %s/ %s/'%(userHome,homeTemplate) else: self.printERROR(_("Method syncUser: option sync=%s incorrect")\ %str(sync)) return False if execStr: host = "" + host +"" rsync = RsyncProgressBar(\ _("Receiving file list from %s") % host + " ...", _("Downloading the user profile from %s") % host \ + " ...", execStr) configFileName = os.path.join(homeTemplate,self.configFile) if sync == "login": # получить переменную files из секции Rsync файла # .calculate.ini try: numfiles = iniParser( \ configFileName).getVar('rsync','files') if numfiles == False: if os.path.exists(configFileName): os.remove(configFileName) numfiles = 1 else: numfiles = int(numfiles) except: numfiles = 1 rsync.maximum = numfiles if progress: rsync.run() else: rsync.runsilent() if sync == "logout": rsync.runsilent() try: if iniParser(configFileName).setVar('rsync', {'files':rsync.getFilesNum()}): os.chmod(configFileName, 0600) os.chown(configFileName,uid,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,uid,gid) except: pass self.printERROR(_("Can not execute rsync") + " " + str(sync) +\ " ...") flagError = True else: if sync == "login": if not (os.path.exists(userHome)): self.printERROR(_("Directory %s not exists")%userHome) else: self.printERROR(_("Directory %s not exists")%homeTemplate) elif sync == "logout": if not (os.path.exists(userHome)): self.printERROR(_("Directory %s is empty or not exists")\ %userHome) else: self.printERROR(_("Directory %s not exists")%homeTemplate) flagError = True if flagError: return False else: # Изменим если нужно права на директории fileObj = _file() # Домашняя директория и директория хранения профиля пользователя changeDirs = [userHome, homeTemplate] for changeDir in changeDirs: # Получаем права на директорию mode,uid,gid = fileObj.getModeFile(changeDir) # Если права не равны 0700 меняем их if mode != 0700: os.chmod(changeDir,0700) return True class tsOpt(opt): """Класс для обработки параметров и вывода help Параметры: helpObj объект-справка содержащий необходимые опции notOptError выдавать ошибку при отсутствии опций командной строки """ def __init__(self, helpObj, notOptError=False): # от cl_help получаем короткие и длинные опции shortOpt,longOpt = helpObj.getAllOpt('all', helpObj.relOptions['h']) # вызвать конструктор объекта, распознающего опции opt.__init__(self,shortOpt,longOpt) self.nameParams = ['user'] self.sysArgv = sys.argv[1:] self.helpObj = helpObj self.__iter = 0 self.opt = {} self.params = {} self.getopt() # Обработка help self.flagHelp = False # определяем есть ли среди опций опции, которые влияют на показ # опциональных разделов (метод пересечения множеств) helpopt = \ tuple(set(self.opt.keys()).intersection(helpObj.relOptions.keys())) #Если есть опции help if len(helpopt) > 0: print helpObj.getHelp(helpObj.relOptions[helpopt[0]]) self.flagHelp = True #Если нет хвостов #elif not self.params: #print helpObj.getHelp(helpObj.relOptions['h']) #self.flagHelp = True else: if self.params.has_key('user'): if len(self.nameParams) != self.__iter: self.handlerErrOpt() # В случае остсутствия опций командной строки и имени пользователя if notOptError: if not self.opt: self.printErrorNotOpt() self.flagHelp = True elif not self.opt and not self.params.has_key('user'): print helpObj.getHelp(helpObj.relOptions['h']) self.flagHelp = True def printErrorNotOpt(self): """Сообщение в случае отсутствия опций""" print _("Options are absent.") def handlerOpt(self,option,value): # Обработчик (опция значение) #print option, value shortOpt = self.helpObj.getShortOpt(option) if not shortOpt: shortOpt = option if not shortOpt in self.opt: self.opt[shortOpt] = value def handlerErrOpt(self): # Обработчик ошибок argv = " ".join(sys.argv[1:]) print _("Unrecognized option") + ' "' + argv + '"\n' + \ _("Try") + ' "' + sys.argv[0].split("/")[-1] + ' --help" ' +\ _("for more information.") def handlerParam(self,param): # Обработчик хвостов (значение) self.__iter += 1 if self.__iter<=len(self.nameParams): self.params[self.nameParams[self.__iter-1]] = param