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

3025 lines
134 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#-*- 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
import cl_base
import cl_profile
import cl_utils2
import cl_utils
import ldap
import types
import getpass
import _cl_keys
import time
import stat
import subprocess
from encrypt import encrypt
from signal import SIGTERM
Version = "calculate-client 2.1.11"
tr = cl_base.lang()
tr.setLanguage(sys.modules[__name__])
pcs = cl_utils.prettyColumnStr
class printNoColor:
def colorPrint(self,attr,fg,bg,string):
sys.stdout.write(string)
class NoneProgressBar:
"""Abstract class of progress bar. It do nothing."""
def __init__(self,title,dialog=None):
pass
def openDialog(self,title,max=None):
"""Create dialog with progress bar, prepare date"""
pass
def shutdownDialog(self):
"""Destroy dialog"""
pass
def setValue(self,value):
"""Set value of progress bar relative of maximum"""
pass
def setMaximum(self,max):
"""Set maximum value of progress bar"""
pass
def setTitle(self,title):
"""Set title of progress dialog"""
pass
class GProgressBar(NoneProgressBar):
"""GProgressBar uses Xdialog program for display progressbar."""
def __init__(self,title,xdialog=None):
self.title = title
self.bgPID = 0
self.value = 0
self.pipein = None
if xdialog == None:
self.openDialog(self.title)
def openDialog(self,title,max=None):
if max != None:
self.setMaximum(max)
title = re.sub("<[^>]+>", "", title)
self.title = title
if os.system('which Xdialog &>/dev/null') == 0:
pipe = subprocess.Popen('/usr/bin/Xdialog --progress "%s" 5 80'\
%(self.title),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
env=os.environ, shell=True)
self.pipein = pipe.stdin
self._set(1)
pipe.stdout.close()
pipe.stderr.close()
def shutdownDialog(self):
'''Принудительно уничтожить процесс dialog'''
if self.pipein:
self._set(100)
time.sleep(0.1)
self.pipein.close()
self.pipein = None
def _set(self,value):
if self.pipein:
self.pipein.write("%02d\n"%value)
self.pipein.flush()
def setValue(self,value):
'''Установить текущее значения для прогресса'''
if self.pipein and value <= self.max:
progress_value = int(value / self.kmax)
if progress_value > int(self.value / self.kmax) and progress_value < 100:
self._set(progress_value)
self.value = value
def setMaximum(self,max):
'''Установить максимальное значения для прогресса'''
self.max = max
self.kmax = max / 100.0
def setTitle(self,title):
'''Установить описания прогресса'''
pass
class KProgressBar(NoneProgressBar):
"""KProgressBar uses kdialog program and dbus for display 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
self.value = 0
if kdialog is 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()
# wait for terminate kdialog, which say dbus dialog id
if pipe.poll() is None:
# ожидание в 5 сек
for t in range(500):
time.sleep(0.01)
if pipe.poll() != None:
break
# waiting is ok
if pipe.poll() == 0:
self.kdialog = pipe.stdout.readline().strip()
while not "org.kde.kdialog" in self.kdialog:
s = pipe.stdout.readline()
# if bad result of kdialog then shutdown dialog
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()
# waiting is failed
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)) + " "
oldperc = int(self.value / self.kmax)
newperc = int(value / self.kmax)
if newperc > oldperc:
os.system(env + '/usr/bin/qdbus %s %s value %d >/dev/null'\
%(self.kdialog,self.suffixSet, newperc))
self.value = value
def setMaximum(self,max):
'''Установить максимальное значения для прогресса'''
if self.kdialog and self.max == 0 and max != 0:
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, 100))
self.max = max
self.kmax = max / 100.0
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 ProgressBar(*args,**kwarg):
"""Return instance of object for progress bar"""
if os.system('which kdialog &>/dev/null') == 0:
return KProgressBar(*args,**kwarg)
elif os.system('which Xdialog &>/dev/null') == 0:
return GProgressBar(*args,**kwarg)
return NoneProgressBar(*args,**kwarg)
class ProgressProfile(cl_profile.profile):
def __init__(self, vars):
cl_profile.profile.__init__(self,vars)
self.progress = ProgressBar(_("Setting up user profile") + " ...")
def numberAllProfiles(self, number):
self.progress.setMaximum(number)
return True
def numberProcessProfiles(self,number):
self.progress.setValue(number)
return True
def close(self):
self.progress.shutdownDialog()
class RsyncProgressBar:
'''Объект запуска 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
self.progress = ProgressBar(title,1)
self.progress.setMaximum(self.maximum)
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.progress.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.progress.shutdownDialog()
self.progress.openDialog(self.secondtitle)
self.progress.setMaximum(self.maximum)
self.copyStarting = True
value = int(q.groups()[0])
self.progress.setValue(value)
def close(self):
self.progress.shutdownDialog()
# Импортированные классы в cl_ldap
# Запись ошибок
imp_cl_err = cl_profile._error
# Работа с XML
imp_cl_xml = cl_profile.xmlShare
# Обработка параметров командной строки
imp_cl_help = cl_utils2.cl_help
# Форматированный вывод
imp_cl_smcon = cl_utils2.cl_smartcon
class cl_client(imp_cl_err, imp_cl_xml, imp_cl_help, imp_cl_smcon, encrypt):
"""Основной класс для работы клиентских приложений"""
# Пути к профилям объединяемых с системными
# относительный путь при объединении '/'
rootProfilePaths=['/usr/lib/calculate/calculate-client/profiles',
'/var/calculate/remote/profiles',
'/var/calculate/profiles']
def __init__(self, cmdName):
# объект для форматированного вывода
imp_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,
'cl-passwd':3
}
# 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")
},
{
'progAccess':(3,),
'helpChapter':_("Usage"),
'help': cmdName
},
# 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")
},
{
'progAccess':(3,),
'helpChapter':"Function",
'help':_("Change user password")
},
# 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 )
},
{
'progAccess':(3,),
'helpChapter':_("Examples"),
'help':pcs(" cl-passwd", self.column_width,
"# "+_("change password of user for Samba and Unix services")+".",
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,3),
'longOption':"vars",
'optVal':_("TYPE_VAR"),
'helpChapter':_("Common options"),
'help':_("print variables (TYPE_VAR - all:full var)")
},
{'progAccess':(0,1,2,3),
'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 profiles 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'")
}
]
self._cl_help__setParamHelp()
# Удаляем ненужный аттрибут класса cl_profile.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
# файл с дополнительной информацией о профиле
# на данный момент только количество файлов для rsync
# используемое для прогрессбара
self.configFileDesktop = "desktop.env"
self.configFileServer = "server.env"
# Файл - список файлов профиля пользователя
self.listProfFile = ".calculate/files.txt"
# Путь относительно домашней директории в котором находятся
# конфигурационные файлы
self.pathConfig = ".calculate"
# При включениии репликации
# Временные задержки для монтирования в секундах
self.sleeps = [0.5, 2, 5]
# Название ветки хранения последнего посещенного пользователя
self.replBranchName = "Worked"
# Аттрибут ветки хранения последнего посещенного пользователя
self.replAttr = "ou"
# DN системной ветки
self.sysDN = self.addDN("ou=Replication","ou=LDAP", ServicesDN)
replBranch = self.replAttr + "=" + self.replBranchName
# DN хранения последнего посещенного пользователя
self.replHostsDN = self.addDN(replBranch,self.sysDN)
self.logOutFile = ".logout"
self.skipHomeFile = ["Home","Disks","FTP",
self.logOutFile,
os.path.join(self.pathConfig,self.configFileDesktop)]
# Если атрибут установлен то значит (ошибка и отмонтируются
# пользовательские ресурсы)
self.errorAndUnmountUserRes = False
# Имя пользователя
self.userName = ""
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 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
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, printError=True):
"""Определяет является ли пользователь root"""
if os.getuid() == 0 and os.getgid() == 0:
return True
else:
if printError:
self.printERROR(_("The user is not root"))
return False
def createClVars(self, clVars=False):
"""Создает объект Vars"""
if not clVars:
clVars = cl_base.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 = ldap.SCOPE_SUBTREE
searchScope = ldap.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 searchReplBranch(self):
"""Находит ветку создаваемую при включении репликации
В ветке находятся ((имя пользователя, имя системы, хост) ....)
"""
resSearch = self.searchLdapDN(self.replBranchName,
self.sysDN,
self.replAttr)
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 = cl_utils2.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 createUserDir(self, uid, gid, userDir, mode=0700):
"""Создание пользовательской директории"""
if not os.path.exists(userDir):
os.makedirs(userDir)
if mode:
os.chmod(userDir,mode)
os.chown(userDir,uid,gid)
return True
else:
self.printERROR(_("Path %s exists") %userDir)
return False
def applyProfilesFromUser(self,progress=False):
"""Применяем профили для пользователя"""
# Cоздаем объект профиль
if progress:
clProf = ProgressProfile(self.clVars)
else:
clProf = cl_profile.profile(self.clVars)
# Объединяем профили
dirsFiles = clProf.applyProfiles()
if progress:
clProf.close()
if clProf.getError():
self.printERROR(clProf.getError())
return False
else:
return dirsFiles
def applyProfilesFromSystem(self):
"""Применяем профили для cистемы"""
# Cоздаем объект профиль
clProf = cl_profile.profile(self.clVars)
# Объединяем профили
dirsFiles = clProf.applyProfiles()
if clProf.getError():
self.printERROR(clProf.getError())
return False
else:
return dirsFiles
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 = _("Password incorrect")
return False, errMessage
except ldap.LDAPError, e:
errMessage = e[0]['desc']
return False, errMessage
return True, errMessage
def getServerDataUser(self):
"""Получение имени LDAP сервера и DN пользователей под пользователем"""
fileName = "/etc/ldap.conf"
serverName = ""
usersDN = ""
strServer = ("host","HOST")
lenStrServer = len(strServer[0])
strDN = ("nss_base_passwd","NSS_BASE_PASSWD")
lenStrDN = len(strDN[0])
splList = (" ", "\t")
try:
for i in open(fileName):
if not serverName and\
filter(lambda x: i.startswith(x),strServer) and\
len(i)>lenStrServer:
spl = i[lenStrServer]
if spl in splList:
serverName = i.rpartition(spl)[2].strip()
if not usersDN and filter(lambda x: i.startswith(x), strDN) and\
len(i)>lenStrDN:
spl = i[lenStrDN]
if spl in splList:
usersDN = i.rpartition(spl)[2].partition('?')[0].strip()
if serverName and usersDN:
break
except:
self.printERROR(_("Can not open %s")%fileName)
return False
if serverName and usersDN:
return (serverName, usersDN)
else:
return ()
def setServerCommand(self, command, varsCommand, fileConfig,
uid=None, gid=None):
"""Установить команду для сервера"""
pathConfig = os.path.split(fileConfig)[0]
# Создаем директорию если ее нет
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 = cl_base.iniParser(fileConfig)
varsRun = {"run":"on"}
varsRun.update(varsCommand)
if not objConfig.setVar(["command"]+command, varsRun):
self.printERROR(_("Can not write variables in file %s")\
%fileConfig)
return False
if not uid is None and not gid is None:
os.chown(fileConfig, uid, gid)
return True
def getDataInConfig(self, section, listVars, objConfig):
"""Читаем переменные listVars из секции section конф. файла"""
varsConfig = {}
for varName in listVars:
varsConfig[varName] = objConfig.getVar(section,varName)
if objConfig.getError():
return False
return varsConfig
def setUserPasswordToServer(self, options):
"""Установка пароля пользователя на сервере"""
# Проверяем на root
if self.isRoot(False):
self.printERROR(_("The user is root"))
self.printWARNING(\
_("The program can be executed from a non-root user only"))
return False
# DNS имя хоста
data = self.getServerDataUser()
if not data:
self.printERROR(_("The computer is not in domain"))
self.printWARNING(_("Use passwd"))
return False
server, usersDN = data
# Получаем старый пароль пользователя
curPassword = self.getUserPassword(_("Enter current password"))
if not curPassword:
self.printERROR(_("Current password is empty"))
return False
userDN = self.addDN("uid=%s"%os.environ["USER"], usersDN)
# Проверяем в LDAP сервере текущий пароль пользователя
ret, err = self.checkUserPwdLDAP(server, userDN, curPassword)
if not ret:
self.printERROR(err)
return False
optPasswd = options
if not options:
optPasswd = {"p":""}
password = self.getUserPwd(optPasswd, "p", False)
if password is False:
return False
# Переменные для записи в env файл
varsConfig = {"unix_hash":self.getHashPasswd(password,"ssha"),
"samba_lm_hash":self.getHashPasswd(password,"lm"),
"samba_nt_hash":self.getHashPasswd(password,"nt"),
"samba_nt_hash_old":self.getHashPasswd(curPassword,"nt")}
if filter(lambda x: not x, varsConfig.values()):
return False
# ~/.calculate/server.env
fileConfig = os.path.join(os.environ["HOME"], self.pathConfig,
self.configFileServer)
if not self.setServerCommand(["passwd_samba"], varsConfig,
fileConfig):
return False
self.printOK(_("Changed password of user %s")%os.environ["USER"] + \
" ...")
self.printWARNING(_("Password will be changed when you logout from the \
X session"))
return True
def getUserPassword(self, pwDialog=False):
"""Получить пароль у пользователя
pwDialog - приглашение ввода пароля
"""
if not pwDialog:
pwDialog = _("Password")
userPwd = getpass.getpass(pwDialog+":")
return userPwd
def chownR(self, directory, uid, gid, dirsAndFiles=False):
"""изменяет владельца и группу
для всех файлов и директорий внутри directory
"""
if dirsAndFiles:
dirs, files = dirsAndFiles
# меняем владельца домашней директории
os.chown(directory, uid,gid)
# Меняем владельца директорий
for dirCh in dirs:
if os.path.exists(dirCh):
os.chown(dirCh, uid,gid)
# Меняем владельца файлов
for fileCh in files:
if os.path.exists(fileCh):
if os.path.islink(fileCh):
os.lchown(fileCh, uid, gid)
else:
os.chown(fileCh, uid,gid)
return True
else:
fileObj = cl_profile._file()
scanObjs = fileObj.scanDirs([directory])
# меняем владельца домашней директории
os.chown(directory, uid,gid)
# Меняем владельца директорий
for dirCh in scanObjs[0].dirs:
os.chown(dirCh, uid,gid)
# Меняем владельца файлов
for fileCh in scanObjs[0].files:
os.chown(fileCh, uid,gid)
# Меняем владельца ссылок
for linkCh in scanObjs[0].links:
os.lchown(linkCh[1], uid, gid)
return True
def execProg(self, cmdStrProg, inStr=False, retFull=True, envProg={}):
"""Выполняет внешнюю программу
Параметры:
cmdStrProg внешняя программа
inStr данные передаваемые программе на страндартный вход.
Возвращаемые параметры:
строка которую выведет внешняя программа или False в случае ошибки
"""
env_path = {"PATH":cl_utils.getpathenv()}
env = {}
env.update(os.environ.items() + env_path.items() + envProg.items())
retCode,programOut = cl_utils.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
# Создаем пользовательскую директорию
self.clVars.Set('cl_root_path',homeDir,True)
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 profiles") + " ...")
# Создаем домашнюю директорию
if not os.path.exists(homeDir):
self.createUserDir(uid, gid, homeDir)
# Записываем переменные
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.applyProfilesFromUser(progress)
if not dirsAndFiles:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Can not apply user profile"))
return False
self.chownR(homeDir, uid, gid, dirsAndFiles)
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 is 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 relevanceProfiles(self, hostAuth):
"""Определяем актуальность наложенных профилей
в зависимости от версии программы
Перед запуском обязательно должен быть определен объект переменных
self.clVars
Если актуальны - True, если нет False
"""
# 'local' или имя хоста
if hostAuth != self.clVars.Get("os_remote_auth"):
return False
clProf = cl_profile.profile(self.clVars)
# Текущая версия программы
currentVersion = self.clVars.Get("cl_ver")
# Версия программы которая ранее работала с профилями
previousVersion = self.clVars.Get("os_remote_client")
cVersion, pVersion = clProf._convertVers(currentVersion,previousVersion)
# Если версии программ не равны то профили не актуальные
if cVersion != pVersion:
return False
return True
def getPathProfiles(self, listPath):
"""Получаем список директорий хранения профилей"""
"""список накладываемых профилей при установке, наложении профилей"""
profpath = []
for profPath in listPath:
if os.path.isdir(profPath):
paths = os.listdir(profPath)
for path in paths:
ph = os.path.join(profPath,path)
filesAndDirs = os.listdir(ph)
if os.path.isdir(ph) and filesAndDirs:
profpath.append(ph)
return profpath
def applyRelevanceProfiles(self, hostAuth):
"""Накладывает релевантные профили
Перед запуском обязательно должен быть определен объект переменных
self.clVars
"""
if not self.relevanceProfiles(hostAuth):
# Обнуляем переменную удаленный хост
if hostAuth == "local":
self.clVars.Set("cl_remote_host","",True)
# Изменяем базовую директорию наложения профилей
self.clVars.Set("cl_root_path","/",True)
# Устанавливаем действие profiles_domain
self.clVars.Set("cl_pass_type","domain",True)
# Наложим профили profiles/domain
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
self.clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/domain' profiles"))
return False
if hostAuth == "local":
self.printOK(_("Set profiles of local mode"))
else:
self.printOK(_("Set profiles 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.applyRelevanceProfiles("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.applyRelevanceProfiles(domain):
return False
return True
flagLocalProfile = 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})
if not (textLine is None):
self.printWARNING(_("Can not mount Samba resource [%s]")%\
"remote" + " ...")
flagLocalProfile = True
# Если профили не актуальны накладываем новую версию профилей
if not self.applyRelevanceProfiles("local"):
return False
if foundMountHome:
umountStr = "umount /home"
textLine = self.execProg(umountStr)
if not (textLine is None):
self.printERROR(_("Can not unmount") + " /home")
return False
return True
self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\
" ...")
if (not foundMountHome) and (not flagLocalProfile):
if not os.path.exists(pathHome):
os.makedirs(pathHome)
mountStr = "mount -o bind %s /home" %pathHome
textLine = self.execProg(mountStr)
if not (textLine is None):
self.printERROR(_("Can not mount") + " " + str(pathHome) +\
" ...")
return False
self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\
" ...")
# Накладываем сетевые профили
if domain:
self.clVars.flIniFile()
if not self.applyRelevanceProfiles(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)
# Изменяем базовую директорию наложения профилей
self.clVars.Set("cl_root_path","/",True)
# Наложим профили profiles/domain
# Новые пути к профилям
# Устанавливаем действие profiles_domain
self.clVars.Set("cl_pass_type","domain",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
self.clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/domain' profiles"))
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)
# Изменяем базовую директорию наложения профилей
clVars.Set("cl_root_path","/",True)
# Устанавливаем действие profiles_client
clVars.Set("cl_pass_type","install",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/client' profiles"))
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)
# Изменяем базовую директорию наложения профилей
clVars.Set("cl_root_path","/",True)
# Устанавливаем действие profiles_client
clVars.Set("cl_pass_type","install",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/client' profiles"))
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 is 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 is 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
# Наложим профили profiles/client
if not self.installClient(self.clVars, False):
return False
# Наложим профили profiles/domain
# Устанавливаем действие profiles_client
self.clVars.Set("cl_pass_type","domain",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
self.clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/domain' profiles"))
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 removeNoiseFiles(self, userHome):
"""Удаление файлов, создающих помехи работе dm"""
noiseFiles = ['.dmrc']
for nsFile in noiseFiles:
rmFile = os.path.join(userHome, nsFile)
if os.path.exists(rmFile):
os.remove(rmFile)
return True
def removePrivateFiles(self, userHome):
"""Удаление приватных файлов"""
privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl',
os.path.join(self.pathConfig,
self.configFileServer)]
# файлы в .ssh
sshHome = ".ssh"
sshPath = os.path.join(userHome,sshHome)
if os.path.isdir(sshPath):
# .ssh файлы относительно домашней директории пользователя
privateFiles += map(lambda x:os.path.join(sshHome,x),\
filter(lambda x:\
os.path.isfile(os.path.join(sshPath,x)),\
os.listdir(sshPath)))
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 (i<len(self.sleeps) and textLine != None):
# Задержка перед следующей попыткой
time.sleep(self.sleeps[i])
# Отмонтируем Samba ресурс
if self.isMount(path, typeMount, secondPath):
textLine = self.execProg("umount %s"%path)
else:
textLine = None
break
i += 1
if textLine != None:
self.printERROR(_("Can not unmount path %s")%path + " ...")
return False
return True
def clearUserKey(self, userName):
"""Очищает пользовательский ключ ядра"""
# Ищем ключ в ядре
if _cl_keys.getKey(userName):
# Если нашли очищаем
ret = _cl_keys.clearKey(userName)
if ret == 0:
return True
else:
self.printERROR(_("Can not clear kernel key from user %s")\
%userName)
return False
return True
def umountUserRes(self, userName,progress=False):
"""Отмонтирование пользовательских ресурсов и синхронизация настроек"""
# Имя пользователя
self.userName = userName
# Проверяем на root
if not self.isRoot():
return False
self.createClVars()
# В случае компьютера вне домена
if not self.clVars.Get("cl_remote_host"):
self.printSUCCESS(_("To be used local profile."))
return True
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
# Если пользователь в X сессии тогда не будем отмонтировать ресурсы
if self.isSessionUser(userName):
self.printERROR(_("User %s is in X session")%userName)
self.printERROR(_("Can not unmount user %s resource")%userName)
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]
# Файл хранения настроек пакета
configFileName = os.path.join(homeDir, self.pathConfig,
self.configFileDesktop)
# Получаем монтируемые директории
names, dictRes = self.getUserMountResources(userName, homeDir, False)
if os.path.exists(homeDir):
self.moveHomeDir(homeDir)
else:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Directory %s not found") % homeDir)
return False
# Синхронизируем настройки
pathUnix = dictRes["profile"]["path"]
if pathUnix and self.isMount(pathUnix ,"cifs") and \
not(domain in self.isMount(pathUnix ,"cifs")):
# Убиваем rsync
if not self.killRsync():
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
textLine = self.umountSleepPath(pathUnix)
if not textLine:
self.printERROR(_("Can not unmount path %s")%pathUnix+ " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Монтируем ресурс Unix
# Получаем пароль пользователя из ключей ядра
userPwd = _cl_keys.getKey(userName)
if not userPwd:
self.printERROR(_("Not found user password"))
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
"unix",pathUnix)
if not (textLine is None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%"unix" + " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# проверяем произведен ли корректный вход в систему -
# в этом случае закачиваем профиль с локальной машины на сервер
# необходима ли синхронизация с локальным сервером
needSync = False
try:
# если актуальный профиль хранится не на локальном сервере
# то на локальный сервер закачиваем профиль
# так как даже если он будет поврежден на другом сервере
# остаётся правильная копия
if self.getNameRemoteServer(userName):
needSync = True
else:
exitStr = cl_base.iniParser(\
configFileName).getVar('main','status_sync')
# проверяем cтатус синхронизации: не содержит ли он "process"
if exitStr != "process":
needSync = True
except:
# Удаляем файлы, мешающие работе dm
self.removeNoiseFiles(homeDir)
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Очищаем ключ в ядре
self.clearUserKey(userName)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
osLinuxShort = self.clVars.Get("os_linux_shortname")
homeProfile = os.path.join(pathUnix, osLinuxShort)
if needSync and not self.syncUser(userName, homeDir,"logout",uid,gid,
homeProfile):
# Удаляем файлы, мешающие работе dm
self.removeNoiseFiles(homeDir)
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Очищаем ключ в ядре
self.clearUserKey(userName)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Удаляем файлы, мешающие работе dm
self.removeNoiseFiles(homeDir)
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Очищаем ключ в ядре
self.clearUserKey(userName)
flagError = False
# Отмонтируем директории
for name in names:
path = dictRes[name]["path"]
if self.isMount(path ,"cifs"):
textLine = self.umountSleepPath(path)
if not textLine:
flagError = True
break
if os.path.exists(path) and not os.listdir(path):
try:
os.rmdir(path)
except:
self.printERROR(_("Can not remove dir %s")% path)
flagError = True
break
if flagError:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Can not save a user profile in the domain"))
return False
self.printSUCCESS(_("Saved a user profile in the domain"))
self.printOK(_("Umounted user resource in domain") + " ...")
return True
def umountUserResNoSync(self, userName, printError=True, printSuccess=True,
isXsession=True, killRsync=False, progress=False):
"""Отмонтирование пользовательских ресурсов
без синхронизации настроек"""
# Имя пользователя
self.userName = userName
# Проверяем на root
if not self.isRoot():
return False
if killRsync:
self.killRsync()
self.createClVars(self.clVars)
# В случае компьютера вне домена
if not self.clVars.Get("cl_remote_host"):
if printSuccess:
self.printSUCCESS(_("To be used local profile."))
return True
# Подсоединяемся к серверу
domain = self.clVars.Get("cl_remote_host")
home = ""
homeDir = ""
if not self.getLdapObjBind(domain, printError):
home = "/home"
homeDir = os.path.join(home,userName)
# Если пользователь в X сессии тогда не будем отмонтировать ресурсы
if isXsession and self.isSessionUser(userName):
self.printERROR(_("User %s is in X session")%userName)
self.printERROR(_("Can not unmount user %s resource")%userName)
return False
if not homeDir:
# homeDir из LDAP
resLdap = self.getUserLdapInfo(userName, printError)
if not resLdap:
home = "/home"
homeDir = os.path.join(home,userName)
else:
homeDir = resLdap[5]
home = os.path.split(homeDir)[0]
# Получаем монтируемые директории
names, dictRes = self.getUserMountResources(userName, homeDir, True)
serverHomePath = dictRes["home"]["path"]
flagError = False
if self.isMount(serverHomePath, "cifs"):
self.moveHomeDir(homeDir)
for name in names:
path = dictRes[name]["path"]
if self.isMount(path ,"cifs"):
textLine = self.umountSleepPath(path)
if not textLine:
flagError = True
break
if os.path.exists(path) and not os.listdir(path):
try:
os.rmdir(path)
except:
if printError:
self.printERROR(_("Can not remove dir %s")% path)
flagError = True
break
if flagError:
if printError:
self.printERROR(_("Can not unmount user %s resource")%userName)
return False
if printSuccess:
self.printOK(_("Umounted user %s resources") %userName + " ...")
return True
def isSessionUser(self, userName):
"""Проверка на вход пользователя в X"""
xSession = False
reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
resWho = self.execProg("who",False,False)
if resWho and type(resWho) == types.ListType:
for string in resWho:
if reFoundUser.search(string):
xSession = True
break
return xSession
def isTwoSessionsUser(self, userName):
"""Проверка на повторный вход пользователя"""
xSession = 0
foundTwoSession = False
reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
resWho = self.execProg("who",False,False)
if resWho and type(resWho) == types.ListType:
for string in resWho:
if reFoundUser.search(string):
xSession +=1
if xSession>1:
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 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 removeDir(self, rmDirOrScanObjs):
"""Рекурсивное удаление директории
входной параметр директория или результат сканирования файлов (объект)
"""
rmDir = False
if type(rmDirOrScanObjs) == types.StringType:
rmDir = rmDirOrScanObjs
if not os.path.exists(rmDir):
self.printERROR(_("Not found remove dir %s") %rmDir)
return False
fileObj = cl_profile._file()
# Сканируем директорию
scanObjs = fileObj.scanDirs([rmDir])
else:
scanObjs = rmDirOrScanObjs
for socketRm in scanObjs[0].sockets:
# Удаляем сокеты
if os.path.exists(socketRm):
os.remove(socketRm)
for linkRm in scanObjs[0].links:
# Удаляем ссылки
os.unlink(linkRm[1])
for fileRm in scanObjs[0].files:
# Удаляем файлы
os.remove(fileRm)
scanObjs[0].dirs.sort(lambda x, y: cmp(len(y), len(x)))
for dirRm in scanObjs[0].dirs:
# Удаляем директории
os.rmdir(dirRm)
if rmDir:
os.rmdir(rmDir)
return True
def getNameRemoteServer(self,userName):
"""Если профиль на удаленном сервере, то выдать DNS имя этого сервера
"""
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 False
else:
return prevHost
def setDateAndVersionToConfig(self, configFileName, uid, gid, strTime):
"""Записывает текущую версию в файл конфигурации"""
# Текущая версия программы
currentVersion = self.clVars.Get("cl_ver")
if self.setVarToConfig("main", {"version":currentVersion,
"date":strTime},
configFileName, uid, gid):
return True
self.printERROR(_("can not write the version number in the file %s")\
%configFileName)
return False
def setVarToConfig(self, nameSection, varsDict, configFileName, uid, gid):
"""Записывает переменную в файл конфигурации"""
# Создаем директорию для конфигурационных файлов
pathConfig = os.path.split(configFileName)[0]
if not os.path.exists(pathConfig):
self.createUserDir(uid, gid, pathConfig, mode=False)
try:
if cl_base.iniParser(configFileName).setVar(nameSection, varsDict):
os.chmod(configFileName, 0600)
os.chown(configFileName,uid,gid)
except:
return False
return True
def clearHomeDir(self, homeDir):
"""Очистка домашней директории от файлов"""
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):
return False
elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]):
os.remove(delFile)
return True
def getUserMountResources(self, userName, homeDir, flagRemoteServer):
"""Получение монтируемых ресурсов для пользователя"""
home = os.path.split(homeDir)[0]
dictResources = {}
names = []
# Pесурс share
name = "share"
dictResources[name] = {"resource":"share",
"path":os.path.join(homeDir,"Disks")}
names.append(name)
# Ресурс профилей
name = "profile"
dictResources[name] = {"resource":"unix",
"path":os.path.join(home,"."+userName)}
names.append(name)
# Ресурс - домашняя директория (файлы пользователя на сервере)
name = "home"
dictResources[name] = {"resource":"homes",
"path":os.path.join(homeDir,"Home")}
names.append(name)
if self.clVars.Get("cl_remote_ftp"):
# Ресурс ftp
name = "ftp"
dictResources[name] = {"resource":"ftp",
"path":os.path.join(homeDir,"FTP")}
names.append(name)
if flagRemoteServer:
# Ресурс профилей на удаленном сервере
name = "remote_profile"
dictResources[name] = {"resource":"unix",
"path":os.path.join(home,"."+userName+"."+"remote")}
names.append(name)
return names, dictResources
def syncLoginProfile(self, userName, homeDir, homeProfile, uid, gid,
progress, flagClearHomeDir=True):
"""Синхронизация профиля пользователя с сервера
в переменной 'cl_remote_host' хост для прогрессбара
"""
home = os.path.split(homeDir)[0]
# Если на текущем сервере в ресурсе unix есть файлы
# то синхронируем настройки
if os.listdir(homeProfile):
if not self.syncUser(userName, homeDir, "login", uid, gid,\
homeProfile, progress=progress,
host=self.clVars.Get('cl_remote_host')):
return False
else:
if flagClearHomeDir:
# Удаляем ненужные файлы (очищаем домашнюю директорию)
if not self.clearHomeDir(homeDir):
return False
return True
def fileReader(self, fileName, stdin, progressObj=False):
"""Читает файл блоками в поток"""
fd = os.open(fileName, os.O_RDONLY)
if progressObj:
currSize = 0
# Размер файлового буфера в байтах
buffSize=131072
dataBlock = os.read(fd, buffSize)
while dataBlock:
if progressObj:
currSize += len(dataBlock)
progressObj.setValue(currSize)
stdin.write(dataBlock)
dataBlock = os.read(fd, buffSize)
stdin.close()
os.close(fd)
def unpackProfile(self, homeDir, archFile, progress=False,\
remoteServer=False):
"""Распаковка архива в домашнюю директорию пользователя"""
# Создаем прогрессбар
if progress:
title = _("Unpacking the archive from server")
if remoteServer:
title += " " + remoteServer
title += " ..."
progressObj = ProgressBar(title)
archiveSize = os.stat(archFile).st_size
progressObj.setMaximum(archiveSize)
execStr = "tar -C '%s' -xzf -" %homeDir
# Выполняем разархивацию
pipe = subprocess.Popen(execStr,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
env=os.environ, shell=True)
# Чтение файла в поток
if progress:
self.fileReader(archFile, pipe.stdin, progressObj)
else:
self.fileReader(archFile, pipe.stdin)
ret = pipe.wait()
pipe.stdout.close()
pipe.stderr.close()
if progress:
progressObj.shutdownDialog()
#ret = os.system(execStr)
if ret:
self.printERROR(_("Can not execute %s") %execStr)
self.printERROR(_("Can not unpack %s") %archFile)
return False
return True
def scanDirectory(self, scanDir, listFiles, skipPath=[], prefix=False,
flagDir=False):
"""Генерация списка файлов и директорий"""
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 getListFilesProfile(self, homeDir):
"""Генерация списка файлов в домашней директориии
Исключая монтируемые директории
"""
home = os.path.join(homeDir, "")
execStr = "mount"
textLines = self.execProg(execStr,False,False)
# Пропускаемые директории для сканирования
skipPaths = []
if textLines:
for line in textLines:
if home in line:
skipPath =\
line.partition(home)[2].rpartition(" type")[0]
skipPaths.append(skipPath)
# Список файлов в профиле пользователя
listFiles = []
if not skipPaths:
self.printERROR(_("Not found the mount point of server resources"))
return False
self.scanDirectory(homeDir, listFiles, skipPaths)
return listFiles
def removeFilesInProfile(self, homeDir, pathListFile):
"""Удаляем файлы которых нет в профиле пользователя"""
# Получаем файлы профиля на сервере
try:
filesProfileTxt = open(pathListFile).read()
except:
self.printERROR(_("Can not open %s")%pathListFile)
return False
listFilesProfile = filter(lambda x: x.strip(),
filesProfileTxt.split("\n"))
filesProfile = set(listFilesProfile)
# Получаем файлы в домашней директории
listFilesHome = self.getListFilesProfile(homeDir)
if listFilesHome is False:
return False
filesHome = set(listFilesHome)
filesRemove = list(filesHome - filesProfile)
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.isdir(rmPath):
os.rmdir(rmPath)
elif os.path.isfile(rmPath):
os.remove(rmPath)
elif os.path.islink(rmPath):
os.unlink(rmPath)
else:
os.remove(rmPath)
except:
self.printERROR(_("Can nor remove %s")%rmPath)
return False
return True
def foundArchFile(self,strCurrentTime,archPathProcess,
archPathSuccess, progress=False,
remoteServer=False):
"""Поиск архива профиля пользователя"""
# Ищем архив профиля
# Задержки при поиске файла архива .process
if progress:
title = _("Packing the archive at the server")
if remoteServer:
title += " " + remoteServer
title += " ..."
progressObj = ProgressBar(title, 0)
progressObj.openDialog(title, 0)
sleepsFindProcess = [0.1, 0.2, 0.5]
# Задержки при проверке размера архива
sleepsArch = [0.5, 1, 2]
lenFP = len(sleepsFindProcess)
lenA = len(sleepsArch)
i = 0
# Ждем появления файла архива
while(not (os.path.exists(archPathProcess) or\
os.path.exists(archPathSuccess)) and i<lenFP):
time.sleep(sleepsFindProcess[i])
i +=1
# Если нет архива применяем rsync синхронизацию
if not (os.path.exists(archPathProcess) or\
os.path.exists(archPathSuccess)):
# Архив не найден rsync схема синхронизации
if progress:
progressObj.shutdownDialog()
return False
# Ожидание создания архива
if os.path.exists(archPathProcess):
i = 0
while(os.path.exists(archPathProcess)):
if os.path.exists(archPathSuccess):
break
if i>=lenA:
# rsync схема синхронизации
if progress:
progressObj.shutdownDialog()
return False
startSize = os.stat(archPathProcess).st_size
time.sleep(sleepsArch[i])
if os.path.exists(archPathSuccess):
break
endSize = os.stat(archPathProcess).st_size
if startSize == endSize:
i += 1
if progress:
progressObj.shutdownDialog()
return True
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]
# Файл хранения настроек пакета
configFileName = os.path.join(homeDir, self.pathConfig,
self.configFileDesktop)
# При отсуствии создаем домашнюю директорию
if not os.path.exists(homeDir):
os.makedirs(homeDir)
os.chown(homeDir,uid,gid)
os.chmod(homeDir,0700)
# записываем в конфигурационный файл статус "в процессе"
self.setVarToConfig("main", {"status_sync":"process"},
configFileName, uid, gid)
# Получаем пароль пользователя из ключей ядра
userPwd = _cl_keys.getKey(userName)
if not userPwd:
self.printERROR(_("Not found user password"))
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Флаг ошибки
flagError = False
# Имя удаленного сервера
remoteServer = ""
# В случае включения репликации на сервере True
replOn = self.searchReplBranch()
if replOn:
remoteServer = self.getNameRemoteServer(userName)
# Получаем монтируемые директории
names, dictRes = self.getUserMountResources(userName, homeDir,
remoteServer)
# Путь к профилю пользователя по умолчанию
defaultPath = ""
# Хост пользователя по умолчанию
defaultHost = self.clVars.Get("cl_remote_host")
# Флаг - будет использован инкрементальный архив
flagIncrArch = True
# Ошибка при монтировании удаленного сервера
flagErrorMountRemote = False
# Ошибка при cинхронизации с удаленного сервера
flagErrorSyncRemote = False
# Статус синхронизации
syncStatus = True
osLinuxShort = self.clVars.Get("os_linux_shortname")
# Если профиль на удаленном сервере
if remoteServer and replOn:
# имя файла архива в процессе архивации
archPathProcess = ""
# имя файла архива - после архивации
archPathSuccess = ""
# Создаем инкрементальный архив в случае репликации
prevHost = ""
# Дата профиля на текущем сервере
dateDefaultProfile = ""
# Текущее время
currTime = str(float(time.time()))
# Создаваемые пути при монтировании
for name in names:
path = dictRes[name]["path"]
res = dictRes[name]["resource"]
# Создаем пользовательскую директории для профиля
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
# Монтируем Samba ресурс
if name == "remote_profile":
self.clVars.Set("cl_remote_host", remoteServer, True)
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
res,path)
if name == "remote_profile":
self.clVars.Set("cl_remote_host", defaultHost, True)
if not (textLine is None):
if name == "remote_profile":
flagErrorMountRemote = True
break
else:
self.printERROR(_("Can not mount Samba resource [%s]")\
%res + " ...")
flagError = True
break
if name == "profile" or name == "remote_profile":
# Находим директорию профиля
homeProfile = os.path.join(path, osLinuxShort)
if not os.path.exists(homeProfile):
homeProfile = os.path.join(path, "." + osLinuxShort)
# Примонтирована директория профиля с текущего сервера
if name == "profile":
# Перенос профиля на сервере в директорию без точки
if not self.upgradeProfileClient(userName, homeDir,
path):
flagError = True
break
fileConfig = os.path.join(homeProfile,
self.pathConfig,
self.configFileDesktop)
if os.path.exists(fileConfig):
objConfig = cl_base.iniParser(fileConfig)
data = self.getDataInConfig("main", ["status_sync",
"date"],
objConfig)
if data:
status = data.get("status_sync")
date = data.get("date")
if date and status=="success":
dateDefaultProfile = date
# Примонтирована директория профиля с удаленного сервера
# и есть дата профиля текущего сервера
if name == "remote_profile" and dateDefaultProfile:
# Даем команду на создание инкрементального архива проф.
fileConfig = os.path.join(homeProfile,
self.pathConfig,
self.configFileServer)
varsConfig = {"arch_date":dateDefaultProfile,
"curr_time":currTime}
self.setServerCommand(["pack"], varsConfig, fileConfig)
# Отмонтируем директорию профиля с удаленного сервера
textLine = self.umountSleepPath(path)
if not textLine:
flagError = True
break
pathProfileCurr = dictRes["profile"]["path"]
homeProfileCurr = os.path.join(pathProfileCurr,osLinuxShort)
# Синхронизация профиля с текущего сервера
if not self.syncLoginProfile(userName,homeDir,
homeProfileCurr,
uid, gid, progress):
flagError = True
break
# Монтируем директорию профиля с удаленного сервера
self.clVars.Set("cl_remote_host", remoteServer, True)
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
res,path)
self.clVars.Set("cl_remote_host", defaultHost, True)
if not (textLine is None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%res + " ...")
flagError = True
break
extSuccess = "gz"
extProcess = "process"
strCurrentTime = currTime.replace(".","_")
archPathTmp = os.path.join(path, "profile.%s.%s.tar"\
%(osLinuxShort, strCurrentTime))
# имя файла архива в процессе архивации
archPathSuccess = "%s.%s"%(archPathTmp,extSuccess)
# имя файла архива - после архивации
archPathProcess = "%s.%s"%(archPathTmp,extProcess)
# Ищем файл архива
if not self.foundArchFile(strCurrentTime, archPathProcess,
archPathSuccess, progress,
remoteServer):
flagIncrArch = False
break
# Если нет архива применяем rsync синхронизацию
if not os.path.exists(archPathSuccess):
flagIncrArch = False
break
else:
# Распаковываем архив в домашнюю директорию
if not self.unpackProfile(homeDir, archPathSuccess,
progress, remoteServer):
flagError = True
break
# Cканирование домашней директории и получение списка
# файлов
pathListFile = os.path.join(homeProfile,
self.listProfFile)
self.removeFilesInProfile(homeDir, pathListFile)
else:
if name == "remote_profile":
flagIncrArch = False
if archPathProcess or archPathSuccess:
# Удаление архивных файлов
rmFiles = [archPathProcess, archPathSuccess]
for rmFile in rmFiles:
if os.path.exists(rmFile):
# Удаляем файл архива
os.remove(archPathSuccess)
# Если не удалось замонтировать удаленный профиль
if flagErrorMountRemote:
syncStatus = False
# Если не удалось использовать инкрементальный архив
elif flagIncrArch is False:
# Синхронизируем удаленный профиль
homeProfile = os.path.join(dictRes["remote_profile"]["path"],
osLinuxShort)
if not self.upgradeProfileClient(userName, homeDir,
homeProfile):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Синхронизация профиля с удаленного сервера
self.clVars.Set("cl_remote_host", remoteServer, True)
ret = self.syncLoginProfile(userName, homeDir, homeProfile,
uid, gid, progress, False)
self.clVars.Set("cl_remote_host", defaultHost, True)
if not ret:
flagErrorSyncRemote = True
syncStatus = False
# Если не удалось синхронизировать удаленный синхронизируем
# текущий
homeProfile = os.path.join(dictRes["profile"]["path"],
osLinuxShort)
if not self.syncLoginProfile(userName, homeDir, homeProfile,
uid, gid, progress):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Отмонтируем директорию профиля пользователя на удаленном сервере
if "remote_profile" in dictRes:
umountPath = dictRes["remote_profile"]["path"]
textLine = self.umountSleepPath(umountPath)
if not textLine:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Удаляем директорию удаленного профиля у клиента
if os.path.exists(umountPath):
os.rmdir(umountPath)
# Если профиль на текущем сервере
elif not remoteServer and replOn or not replOn:
# Создаваемые пути при монтировании
for name in names:
path = dictRes[name]["path"]
res = dictRes[name]["resource"]
# Создаем пользовательскую директории для профиля
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
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
res, path)
if not (textLine is None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%res + " ...")
flagError = True
break
if name == "profile":
pathProfile = path
# Перенос профиля на сервере в директорию без точки
if not self.upgradeProfileClient(userName, homeDir,
pathProfile):
flagError = True
break
# Директория профиля
homeProfile = os.path.join(path, osLinuxShort)
# Синхронизируем
if not self.syncLoginProfile(userName, homeDir, homeProfile,
uid, gid, progress):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
if flagError:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
logOutFile = os.path.join(homeDir,self.logOutFile)
if syncStatus:
self.setVarToConfig("main", {"status_sync":"success"},
configFileName, uid, gid)
self.createUserFile(logOutFile,"SUCCESS", uid, gid)
else:
self.createUserFile(logOutFile,"ERROR", uid, gid)
self.setVarToConfig("main", {"status_sync":"error"},
configFileName, uid, gid)
self.printWARNING(_("Error in sync with the remote server %s")\
%remoteServer)
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 is None):
# Проверяем на монтирование директории
if self.isMount(path, 'cifs'):
textLineUmount = self.umountSleepPath(path)
if not textLineUmount:
return False
i = 0
while (i<len(self.sleeps) and not (textLine is None)):
# Задержка перед следующей попыткой
time.sleep(self.sleeps[i])
# Монтируем Samba ресурс
textLine = self.mountSambaRes(userName,userPwd,uid,
gid,res,path)
if not (textLine is None):
# Проверяем на монтирование директории
if self.isMount(path, 'cifs'):
textLineUmount = self.umountSleepPath(path)
if not textLineUmount:
return False
i += 1
return textLine
def copyFilesToMovie(self, userHome):
"""Переносим файлы пользователя в Home/Moved"""
# Находим директории и файлы в домащней директории
pathProg = os.getcwd()
os.chdir(userHome)
dirs = []
files = []
movedLink = os.path.join('Moved')
movedPath = os.path.join('Home',"Moved")
filesAndDir = filter(lambda x: not ('.' in x[0] or x in\
['Disks','Home','Moved','FTP','Desktop'] or os.path.islink(x)),
os.listdir('.'))
filesDir = []
for fd in filesAndDir:
if os.path.islink(fd):
os.unlink(fd)
else:
filesDir.append(fd)
# Нахождение файлов отличных от иконок и ссылок в Desktop
pathDesktop = './Desktop'
filesDirDesk = []
if os.path.exists(pathDesktop):
filesDirDesk = filter(lambda x: not os.path.islink(x) 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"):
# Удаляем пустую папку Moved
if os.path.exists(movedPath) and not os.listdir(movedPath):
os.rmdir(movedPath)
# Удалям ссылку на Moved в домашней директории
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 not (textLine is None):
self.printERROR(_("Can not exec") + " " + str(execStr) +\
" ...")
return False
execStr = "rm -rf '%s'" %fd
textLine = self.execProg(execStr)
if not (textLine is None):
self.printERROR(_("Can not exec") + " " + str(execStr) +\
" ...")
return False
os.chdir(pathProg)
return True
def moveHomeDir(self, userHome):
"""Переносим файлы пользователя в директорию Home/Moved если в домене"""
if self.isDomain():
return self.copyFilesToMovie(userHome)
return True
def createUserFile(self, fileName, fileTxt, uid, gid, mode=0644):
"""Создает пользовательский файл с содержимым
Если директория файла не существует то ошибка
"""
userDir = os.path.split(fileName)[0]
if not os.path.exists(userDir):
self.printERROR(_("Path %s not exists") %userDir)
return False
fd = os.open(fileName, os.O_CREAT|os.O_TRUNC)
os.close(fd)
os.chmod(fileName, mode)
os.chown(fileName,uid,gid)
FD = open(fileName, "r+")
FD.write(fileTxt)
FD.close()
return True
def copyProfileDir(self, srcDir, destDir):
"""Перенос директории srcDir в директорию destDir
При копировании сохраняются владелец, группа, права
"""
if os.system("mv %s %s &>/dev/null"%(srcDir, destDir)) != 0:
self.printERROR(_("Can not move %s")%srcDir + " " +\
_("to %s")%destDir)
return False
return True
def upgradeProfileClient(self, userName, userHome, pathProfiles):
"""Переносит данные клиента в директорию без точки
например: было /home/.user/.CLD стало /home/.user/CLD
Перед вызовом этого метода обязательно должен быть определен атрибут
объекта self.clVars - объект переменных
"""
# Директория хранения старых профилей
home = os.path.split(userHome)[0]
if os.path.exists(pathProfiles):
osLinuxShort = self.clVars.Get("os_linux_shortname")
pathNewProfile = os.path.join(pathProfiles, osLinuxShort)
pathOldProfile = os.path.join(pathProfiles, "."+osLinuxShort)
if not os.path.exists(pathNewProfile) and\
os.path.exists(pathOldProfile) and\
os.listdir(pathOldProfile):
# Переносим профиль
if not self.copyProfileDir(pathOldProfile,
pathNewProfile):
return False
if not os.path.exists(pathNewProfile):
# Создаем директорию для хранения профиля
os.mkdir(pathNewProfile)
os.chmod(pathNewProfile, 0700)
if os.path.exists(pathOldProfile) and\
not os.listdir(pathOldProfile):
os.rmdir(pathOldProfile)
return True
def syncUser(self, userName, userHome, sync, uid, gid, homeProfile, \
progress=False, host="default"):
"""Синхронизация пользовательских настроек
Перед вызовом этого метода обязательно должен быть определен атрибут
объекта self.clVars - объект переменных
"""
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(homeProfile):
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/' %(homeProfile,userHome)
elif sync == "logout":
if os.path.exists(userHome) and os.listdir(userHome) and\
os.path.exists(homeProfile):
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,homeProfile)
else:
self.printERROR(_("Method syncUser: option sync=%s incorrect")\
%str(sync))
return False
if execStr:
host = "<i>" + host +"</i>"
rsync = RsyncProgressBar(\
_("Receiving file list from %s") % host + " ...",
_("Downloading the user profile from %s") % host \
+ " ...", execStr)
pathConfig = os.path.join(homeProfile,
self.pathConfig)
# Удаляем предыдущий ini файл
prevIniFile = os.path.join(homeProfile,".calculate.ini")
if os.path.exists(prevIniFile):
os.remove(prevIniFile)
# Создаем директорию для конфигурационных файлов
if not os.path.exists(pathConfig):
self.createUserDir(uid, gid, pathConfig, mode=False)
configFileName = os.path.join(pathConfig, self.configFileDesktop)
if sync == "login":
# получить переменную files из секции Rsync файла
# .calculate.ini
try:
numfiles = cl_base.iniParser(\
configFileName).getVar('rsync','files')
if numfiles is 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 cl_base.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 cl_base.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")%homeProfile)
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")%homeProfile)
flagError = True
if flagError:
return False
else:
# Изменим если нужно права на директории
fileObj = cl_profile._file()
# Домашняя директория и директория хранения профиля
changeDirs = [userHome, homeProfile]
for changeDir in changeDirs:
# Получаем права на директорию
mode,uid,gid = fileObj.getModeFile(changeDir)
# Если права не равны 0700 меняем их
if mode != 0700:
os.chmod(changeDir,0700)
return True
class tsOpt(cl_base.opt):
"""Класс для обработки параметров и вывода help
Параметры:
helpObj объект-справка содержащий необходимые опции
notOptError выдавать ошибку при отсутствии опций командной строки
optUser проверять хвост командной строки на наличие пользователя
"""
def __init__(self, helpObj, notOptError=False, optUser=True):
# от cl_help получаем короткие и длинные опции
shortOpt,longOpt = helpObj.getAllOpt('all', helpObj.relOptions['h'])
# вызвать конструктор объекта, распознающего опции
cl_base.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
else:
if optUser and self.params.has_key('user'):
if len(self.nameParams) != self.__iter:
self.handlerErrOpt()
# В случае остсутствия опций командной строки и имени пользователя
if notOptError:
if not self.opt:
self.printErrorNotOpt()
self.flagHelp = True
if optUser and not self.params.has_key('user'):
print helpObj.getHelp(helpObj.relOptions['h'])
self.flagHelp = True
elif optUser and not self.opt and not self.params.has_key('user'):
print helpObj.getHelp(helpObj.relOptions['h'])
self.flagHelp = True
if not optUser and 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