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

2576 lines
115 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
import time
from encrypt import encrypt
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 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 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
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)
# Импортированные классы в 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.configFile = ".calculate.ini"
# При включениии репликации
# Временные задержки для монтирования в секундах
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.replLogoutFile = ".logout"
self.skipHomeFile = ["Home","Disks","FTP",
self.replLogoutFile]
# Если атрибут установлен то значит (ошибка и отмонтируются
# пользовательские ресурсы)
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") + ": " +\
_("password incorrect")+ ": " + _("try again"))
return False
userPwd = pwdA
return userPwd
def exit(self, exitCode):
"""Метод выхода при ошибке"""
self.errorExit()
sys.exit(exitCode)
def errorExit(self):
"""Отмонтирование пользовательских ресурсов при ошибке"""
# TODO: добавить удаление приватных файлов
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.progress.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:
if type(e.message) == dict and e.message.has_key('desc'):
errMessage = e.message['desc']
else:
errMessage = e
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
return (serverName, usersDN)
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"))
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 == False:
return False
# ~/.calculate
pathConfig = os.path.join(os.environ["HOME"],".calculate")
# Создаем директорию
if not os.path.exists(pathConfig):
os.makedirs(pathConfig)
# Переменные для записи в env файл
varsConfig = {"unix_hash":self.getHashPasswd(password,"ssha"),
"samba_lm_hash":self.getHashPasswd(password,"lm"),
"samba_nt_hash":self.getHashPasswd(password,"nt")}
if filter(lambda x: not x, varsConfig.values()):
return False
# ~/.calculate/server.env
fileConfig = os.path.join(pathConfig,"server.env")
txtConfig = cl_base.iniParser(fileConfig)
if not txtConfig.setVar(["passwd","samba"], varsConfig):
self.printERROR(_("Can not write password variable in file %s")\
%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 == 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 == 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 == 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 == 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 == 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
# Наложим профили 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 removePrivateFiles(self, userHome):
"""Удаление приватных файлов"""
privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl',
'.calculate/server.env']
# файлы в .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 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]
home = os.path.split(homeDir)[0]
pathRemote = []
# Удаленный ресурс профилей
pathRemote.append((os.path.join(home,"." + userName), "unix"))
# Удаленный ресурс home
pathRemote.append((os.path.join(homeDir,"Home"), "homes"))
# Удаленный ресурс ftp
if self.clVars.Get("cl_remote_ftp"):
pathRemote.append((os.path.join(homeDir,"FTP"), "ftp"))
# Удаленный ресурс share
pathRemote.append((os.path.join(homeDir,"Disks"), "share"))
if os.path.exists(homeDir):
self.moveHomeDir(homeDir)
else:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Directory %s not found") % homeDir)
return False
# Синхронизируем настройки
pathUnix = ""
for path, res in pathRemote:
if res == 'unix':
pathUnix = path
break
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 == None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%"unix" + " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# проверяем произведен ли корректный вход в систему -
# в этом случае закачиваем профиль с локальной машины на сервер
logOutFile = os.path.join(homeDir,self.replLogoutFile)
# необходима ли синхронизация с локальным сервером
needSync = False
try:
# если актуальный профиль хранится не на локальном сервере
# то на локальный сервер закачиваем профиль
# так как даже если он будет поврежден на другом сервере
# остаётся правильная копия
if not self.isCorrectProfileOnLocalServer(userName):
needSync = True
else:
FD = open(logOutFile)
exitStr = FD.read().strip()
FD.close()
# проверяем logOutFile: не содержит ли он "PROCESS"
if exitStr != "PROCESS":
needSync = True
except:
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
if needSync and not self.syncUser(userName,homeDir,"logout",uid,gid):
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
flagError = False
for path, res in pathRemote:
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]
pathRemote = []
# Удаленный ресурс профилей
pathRemote.append((os.path.join(home,"." + userName), "unix"))
# Удаленный ресурс home
pathRemote.append((os.path.join(homeDir,"Home"), "homes"))
# Удаленный ресурс ftp
if self.clVars.Get("cl_remote_ftp"):
pathRemote.append((os.path.join(homeDir,"FTP"), "ftp"))
# Удаленный ресурс share
pathRemote.append((os.path.join(homeDir,"Disks"), "share"))
flagError = False
if self.isMount(pathRemote[1][0] ,"cifs"):
self.moveHomeDir(homeDir)
for path, res in pathRemote:
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 isCorrectProfileOnLocalServer(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
# В случае включения репликации на сервере True
replOn = self.searchReplBranch()
# Путь к профилю пользователя по умолчанию
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 replOn 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 flagError:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
flagFirstSync = False
if replOn or sync:
# Ошибка при монтировании unix ресурса удаленного сервера при
# включенной репликации
replErrorMount = False
# Ошибка при cинхронизации unix ресурса удаленного сервера при
# включенной репликации
replErrorSync = False
# Синхронизируем настройки
if sync:
# Короткое имя системы
osLinuxShort = self.clVars.Get("os_linux_shortname")
# В случае репликации
prevHost = ""
# Монтирование по умолчанию (default - cвой сервер, remote - чужой)
mountServer = ""
if replOn:
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.upgradeProfileClient(userName, homeDir)
# Если на текущем сервере в ресурсе unix есть файлы
# то синхронируем настройки
# Получаем директорию хранения профиля на сервере
homeProfile = os.path.join(home, "." + userName, osLinuxShort)
if not os.path.exists(homeProfile):
homeProfile = os.path.join(home, "." + userName,
"." + osLinuxShort)
if os.listdir(homeProfile):
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 (replOn and mountServer == "default"):
# Переносим настройки пользователя в новую директорию
# .CLD
self.upgradeProfileClient(userName, homeDir)
# Получаем директорию хранения профиля на сервере
homeProfile = os.path.join(home, "." + userName, osLinuxShort)
if not os.path.exists(homeProfile):
homeProfile = os.path.join(home, "." + userName,
"." + osLinuxShort)
if os.listdir(homeProfile):
if not self.syncUser(userName, homeDir, "login", uid, gid,\
progress=progress,host=prevHost):
if replOn 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 replOn 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 replOn:
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<len(self.sleeps) and not (textLine == None)):
# Задержка перед следующей попыткой
time.sleep(self.sleeps[i])
# Монтируем Samba ресурс
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 += 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 == None):
self.printERROR(_("Can not exec") + " " + str(execStr) +\
" ...")
return False
execStr = "rm -rf '%s'" %fd
textLine = self.execProg(execStr)
if not (textLine == 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, destDir, srcDir):
"""Перенос файлов и директорий в директории srcDir в директорию destDir
При копировании сохраняются владелец, группа, права
"""
if not os.path.exists(srcDir):
self.printERROR(_("Not found directory %s")%srcDir)
return False
files = os.listdir(srcDir)
if os.path.exists(destDir):
if not os.listdir(destDir):
os.rmdir(destDir)
else:
# Файловый объект
fileObj = cl_profile._file()
os.makedirs(destDir)
mode,uid,gid = fileObj.getModeFile(srcDir)
os.chmod(destDir, 0700)
os.chown(destDir, uid,gid)
flagError=False
for nameFile in files:
srcFile = os.path.join(srcDir, nameFile)
destFile = os.path.join(destDir, nameFile)
# Создаем ссылки
if os.path.islink(srcFile):
dst = srcFile
src = os.readlink(srcFile)
os.symlink(src,destFile)
if os.path.exists(destFile):
mode,uid,gid = fileObj.getModeFile(destFile)
#Изменение прав на ссылки
os.lchown(destFile, uid, gid)
os.unlink(srcFile)
continue
# Удаляем сокеты
elif stat.S_ISSOCK(os.stat(srcFile)[stat.ST_MODE]):
os.remove(srcFile)
continue
# Переносим оставшиеся файлы и директории
if os.system("mv %s %s &>/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 upgradeProfileClient(self, userName, userHome):
"""Переносит данные клиента в директорию .CLD
Перед вызовом этого метода обязательно должен быть определен атрибут
объекта self.clVars - объект переменных
"""
# Директория хранения старых профилей
home = os.path.split(userHome)[0]
pathOldProfile = os.path.join(home, "." + userName)
if os.path.exists(pathOldProfile):
osLinuxShort = self.clVars.Get("os_linux_shortname")
# В случае пустой директории профиля
if not os.listdir(pathOldProfile):
pathNewProfile = os.path.join(pathOldProfile,
"." + osLinuxShort)
# Создаем директорию для хранения профиля
os.mkdir(pathNewProfile)
os.chmod(pathNewProfile, 0700)
return True
skipDirs = [".CLD", ".CLDX", "." + osLinuxShort]
# Если есть скрытые файлы кроме skipDir
# а так-же нет файлов skipDir - делаем апгрейд
if filter(lambda x: x[0]==".",
list(set(os.listdir(pathOldProfile))-set(skipDirs))) and\
len(filter(lambda x: not os.path.exists(os.path.join(pathOldProfile,x)),
skipDirs))==len(skipDirs):
pathNewProfile = os.path.join(pathOldProfile,".CLD")
# Копируем профиль в новое место
try:
self.copyProfileDir(pathNewProfile, pathOldProfile)
except:
self.printERROR(_("Error updating user profile"))
self.printERROR(_("path: %s")%pathNewProfile)
return False
pathNewProfile = os.path.join(pathOldProfile,
"." + osLinuxShort)
if not os.path.exists(pathNewProfile):
# Создаем директорию для хранения профиля
os.mkdir(pathNewProfile)
os.chmod(pathNewProfile, 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")
homeProfile = os.path.join(home, "." + userName, osLinuxShort)
if not os.path.exists(homeProfile):
homeProfile = 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(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)
configFileName = os.path.join(homeProfile,self.configFile)
if sync == "login":
# получить переменную files из секции Rsync файла
# .calculate.ini
try:
numfiles = cl_base.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 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