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.2-client/pym/cl_client.py

2444 lines
112 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 2010 Calculate Ltd. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
__version__ = "2.2.30"
__app__ = "calculate-client"
import os
import stat
import re
import sys
import pwd
import subprocess
import ldap
import time
import types
import getpass
from cl_lang import lang
from cl_template import template, iniParser
from cl_datavars import DataVars
from cl_print import color_print
from cl_ldap import ldapUser
from client.progressbar import ProgressBar
from cl_utils import runOsCommand, getpathenv, getModeFile, removeDir, isMount,\
appendProgramToEnvFile, removeProgramToEnvFile, pathJoin, \
tarLinks
from _cl_keys import getKey, clearKey
from convertenv import convertEnv
from encrypt import encrypt
from cl_client_cache import userCache
from shutil import copy2
from socket import gethostbyname
import tarfile
lang().setLanguage(sys.modules[__name__])
class DataVarsClient(DataVars):
"""Хранение переменных"""
def importClient(self, **args):
'''Импорт переменных для клиента'''
# Имя секции в calculate2.env
envSection = "client"
# импорт переменных
self.importData(envSection, ('cl_vars_client','cl_fill_client'))
class share(color_print):
"""Общие методы"""
# Объект хранения переменных
clVars = False
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 = DataVarsClient()
# Импортируем переменные
clVars.importClient()
# Заменяем значения переменных переменными из env файлов
clVars.flIniFile()
# Устанавливаем у объекта атрибут объект переменных
self.clVars = clVars
return True
def applyTemplatesFromSystem(self):
"""Применяем шаблоны для cистемы"""
# Cоздаем объект обработки шаблонов
clTempl = template(self.clVars)
# Объединяем шаблоны
dirsFiles = clTempl.applyTemplates()
if clTempl.getError():
self.printERROR(clTempl.getError().strip())
return False
else:
return dirsFiles
def printVars(self, *arg, **argv):
"""Печать существующих переменных"""
self.clVars.printVars(*arg, **argv)
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 self.pipe.wait()
return 255
def getErrMessage(self):
'''Сообщение ошибки rsync при синхронизации'''
if self.pipe:
return self.pipe.stderr.read()
return _("RsyncProgressBar: Wrong pipe")
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.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()
class ldapData(ldapUser):
"""Методы для LDAP"""
def addDN(self, *arg):
"""Складывает текстовые элементы DN"""
DNs = []
for dn in arg:
if dn:
DNs.append(dn)
return ','.join(DNs)
def getReplDN(self):
"""Получаем DN ветки хранения последнего посещенного сервера
В ветке находятся ((имя пользователя, имя системы, хост) ....)
"""
usersDN = self.getUsersDN()
if usersDN:
partDN = "ou=Worked,ou=Replication,ou=LDAP"
servicesDN = "ou=Services"
baseDN = usersDN.rpartition(servicesDN+",")[2]
replDN = self.addDN(partDN, servicesDN, baseDN)
return replDN
return False
def searchPrevHost(self, userName, osLinuxShort):
"""Находит сервер к которому был подключен пользователь"""
connectData = self.getBindConnectData()
if connectData:
bindDn, bindPw, host = connectData
replDN = self.getReplDN()
# cтрока для поиска в служебной ветке репликации
userAndOsName = "%s@%s"%(userName,osLinuxShort)
findAttr = "uid=%s"%userAndOsName
# Соединяемся с LDAP
if not self.ldapConnect(bindDn, bindPw, host):
return False
resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL,
findAttr, ["host"])
return resSearch
return False
def _gethostbyname(self,hostname):
try:
return gethostbyname(hostname)
except:
pass
return None
def getNameRemoteServer(self,userName, osLinuxShort, curHost):
"""Если профиль на удаленном сервере, то выдать DNS имя этого сервера
"""
searchPrevHost = self.searchPrevHost(userName, osLinuxShort)
if searchPrevHost and searchPrevHost[0][0][1].has_key('host'):
prevHost = searchPrevHost[0][0][1]['host'][0]
else:
prevHost = None
# get ip address of previous server and current server
prevIp = self._gethostbyname(prevHost)
curIp = self._gethostbyname(curHost)
# если местоположение актуального профиля найти не удалось
# или его местоположение не на локальном сервере
if not prevHost or curIp and prevIp == curIp:
return False
else:
return prevHost
def isRepl(self):
"""Включена ли репликация на сервере"""
connectData = self.getBindConnectData()
if connectData:
bindDn, bindPw, host = connectData
usersDN = self.getUsersDN()
partDN = "ou=Replication,ou=LDAP"
servicesDN = "ou=Services"
baseDN = usersDN.rpartition(servicesDN+",")[2]
replDN = self.addDN(partDN, servicesDN, baseDN)
findAttr = "ou=Worked"
# Соединяемся с LDAP
if not self.ldapConnect(bindDn, bindPw, host):
return False
resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL,
findAttr,
[findAttr.partition("=")[0]])
if resSearch:
return True
return False
class commandServer(color_print):
"""Отправка команд на сервер (изменение пароля пользователя на сервере)"""
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 = iniParser(fileConfig)
varsRun = {"run":"on"}
varsRun.update(varsCommand)
if not objConfig.setVar(["command"]+command, varsRun):
self.printERROR(_("Failed to 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 checkUserPwdLDAP(self, server, userDN, password):
"""Проверка пароля Unix пользователя на сервере"""
ldapInit = ldap.initialize("ldap://%s"%server)
errMessage = ""
try:
ldapInit.bind_s(userDN, password)
except ldap.INVALID_CREDENTIALS:
errMessage = _("Wrong password")
return False, errMessage
except ldap.LDAPError, e:
errMessage = e[0]['desc']
return False, errMessage
return True, errMessage
def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False):
"""Получить пароль у пользователя
options - полученные опции командной строки
optDialog - опция командной строки для вывода диалога для получения
пароля
optStdIn - опция командной строки для получения пароля из
стандартного ввода (stdin)
pwDialog - структура для вывода приглашения в режиме диалога
"""
userPwd = ""
if optStdIn and options.has_key(optStdIn):
pwdA = sys.stdin.readline().rstrip()
pwdB = sys.stdin.readline().rstrip()
elif optDialog and options.has_key(optDialog):
if not pwDialog:
pwDialog = [_("New password"),
_("Retype the new password")]
pwdA = getpass.getpass(pwDialog[0]+":")
pwdB = getpass.getpass(pwDialog[1]+":")
if (optStdIn and options.has_key(optStdIn)) or\
(optDialog and options.has_key(optDialog)):
if not pwdA or not (pwdA == pwdB):
self.printERROR (_("ERROR") + ": " +\
_("passwords do not match"))
return False
userPwd = pwdA
return userPwd
class client(share, commandServer, encrypt):
"""Методы работы для подключения пользователя к серверу и синхронизации"""
# Объект для поиска пользовательских данных в LDAP
ldapDataObj = ldapData()
# Путь относительно домашней директории в котором находятся
# конфигурационные файлы
pathConfig = ".calculate"
# Конфигурационный файл для клиента
configFileDesktop = os.path.join(pathConfig, "desktop.env")
# Конфигурационный файл для сервера
configFileServer = os.path.join(pathConfig, "server.env")
# Файл - список файлов профиля пользователя
listTemplFile = os.path.join(pathConfig, "files.txt")
logOutFile = ".logout"
# Файлы котороые не удаляются при очистке домашней директории
skipHomeFile = ["Home","Disks","Share","FTP",logOutFile,configFileDesktop]
# словарь опций сервисов из /var/calculate/remote/server.env
optionsInfo = {}
# объект конвертирования из старого remote env файла
convObj = None
# Приватный файлы пользователя
privateFiles = [configFileServer]
# Приватные директории пользователя
privateDirs = []
def removeVars(self):
"""Удаление переменных шаблонов
при удалении пакета и выходе из домена"""
self.clVars.Delete("cl_remote_host", "local")
self.clVars.Delete("cl_remote_pw", "local")
self.clVars.Delete("os_remote_auth")
self.clVars.Delete("os_remote_client")
self.clVars.Set("cl_remote_host", "", True)
self.clVars.Set("cl_remote_pw", "", True)
self.clVars.Set("os_remote_auth", "", True)
self.clVars.Set("os_remote_client", "", True)
def isTwoSessionsUser(self, userName):
"""Проверка на повторный вход пользователя"""
xSession = 0
foundTwoSession = False
reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
resWho = self.execProg("who")
if resWho:
for string in resWho:
if reFoundUser.search(string):
xSession +=1
if xSession>1:
foundTwoSession = True
self.printERROR(\
_("Second X session for user %s cannot be opened.")\
%userName)
break
return foundTwoSession
def isDomain(self):
"""Находится ли компьютер в домене"""
foundMountRemote = isMount("/var/calculate/remote")
remoteHost = self.clVars.Get("cl_remote_host")
if remoteHost and foundMountRemote:
return True
self.printERROR(_("The computer is not in the domain"))
return False
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,"Share")}
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.getInfoService("ftp", "host"):
# Ресурс 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 mountSambaRes(self,host,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" %(host, res, path)
else:
# Монтируем директории
mountStr = "mount -t cifs -o user=%s"%(userName)+\
" //%s/%s %s" %(host, res, path)
textLine = self.execProg(mountStr, envProg={"PASSWD":userPwd})
return textLine
def mountSleepRes(self,host,userName,userPwd,uid,gid,res,path):
"""Монтирует ресурс при неудаче задержка потом повторное монитрование"""
textLine = self.mountSambaRes(host,userName,userPwd,uid,gid,res,path)
if textLine is False:
# Проверяем на монтирование директории
if isMount(path):
textLineUmount = self.umountSleepPath(path)
if not textLineUmount:
return False
i = 0
sleeps = [0.5, 2, 5]
while (i<len(sleeps) and textLine is False):
# Задержка перед следующей попыткой
time.sleep(sleeps[i])
# Монтируем Samba ресурс
textLine = self.mountSambaRes(host,userName,userPwd,uid,
gid,res,path)
if textLine is False:
# Проверяем на монтирование директории
if isMount(path):
textLineUmount = self.umountSleepPath(path)
if not textLineUmount:
return False
i += 1
return textLine
def copyTemplateDir(self, srcDir, destDir):
"""Перенос директории srcDir в директорию destDir
При копировании сохраняются владелец, группа, права
"""
if os.system("mv %s %s &>/dev/null"%(srcDir, destDir)) != 0:
self.printERROR(_("Failed to move %s")%srcDir + " " +\
_("to %s")%destDir)
return False
return True
def upgradeTemplateClient(self, userName, userHome, pathTemplates):
"""Переносит данные клиента в директорию без точки
например: было /home/.user/.CLD стало /home/.user/CLD
Перед вызовом этого метода обязательно должен быть определен атрибут
объекта self.clVars - объект переменных
"""
# Директория хранения старых шаблонов
home = os.path.split(userHome)[0]
if os.path.exists(pathTemplates):
if self.clVars.Get("cl_profile_all_set") == "on":
osLinuxShort = "all"
else:
osLinuxShort = self.clVars.Get("os_linux_shortname")
pathNewTemplate = os.path.join(pathTemplates, osLinuxShort)
pathOldTemplate = os.path.join(pathTemplates, "."+osLinuxShort)
if not os.path.exists(pathNewTemplate) and\
os.path.exists(pathOldTemplate) and\
os.listdir(pathOldTemplate):
# Переносим профиль
if not self.copyTemplateDir(pathOldTemplate,
pathNewTemplate):
return False
if not os.path.exists(pathNewTemplate):
# Создаем директорию для хранения профиля
os.mkdir(pathNewTemplate)
os.chmod(pathNewTemplate, 0700)
if os.path.exists(pathOldTemplate) and\
not os.listdir(pathOldTemplate):
os.rmdir(pathOldTemplate)
return True
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 syncUser(self, userName, userHome, sync, uid, gid, homeTemplate, \
progress=False, host="default"):
"""Синхронизация пользовательских настроек
Перед вызовом этого метода обязательно должен быть определен атрибут
объекта self.clVars - объект переменных
"""
flagError = False
execStr = ""
skipPaths = self.clVars.Get("cl_sync_skip_path")
if not skipPaths:
self.printERROR(
_("The variable 'cl_sync_skip_path' is empty")%userHome)
return False
deletePaths = self.clVars.Get("cl_sync_del_path")
if not deletePaths:
deletePaths = []
excludePaths = " ".join(map(lambda x: '--exclude="/%s"'\
%x.replace('"',"").replace("'",""),
skipPaths + deletePaths))
if sync == "login":
if os.path.exists(userHome) and\
os.path.exists(homeTemplate):
filterPath = " ".join(map(lambda x: '--filter="P /%s"'\
%x.replace('"',"").replace("'",""),
skipPaths))
execStr = '/usr/bin/rsync --delete-excluded --delete %s %s '\
'-rlptgo -x -v -v -v %s/ %s/' \
%(excludePaths, filterPath, homeTemplate, userHome)
elif sync == "logout":
if os.path.exists(userHome) and os.listdir(userHome) and\
os.path.exists(homeTemplate):
execStr = '/usr/bin/rsync --delete-excluded --delete %s \
-rlptgo -x -v -v -v %s/ %s/'%(excludePaths, userHome, homeTemplate)
else:
self.printERROR(_("Method syncUser: option sync=%s incorrect")\
%str(sync))
return False
if execStr:
host = "<i>" + host +"</i>"
rsync = RsyncProgressBar(\
_("Receiving the file list from %s") % host + " ...",
_("Downloading the user profile from %s") % host +\
" ...", execStr)
pathConfig = os.path.join(homeTemplate,
self.pathConfig)
# Удаляем предыдущий ini файл
prevIniFile = os.path.join(homeTemplate,".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(homeTemplate, self.configFileDesktop)
if sync == "login":
# получить переменную files из секции Rsync файла
# .calculate.ini
try:
numfiles = iniParser(\
configFileName).getVar('rsync','files')
if numfiles is False:
if os.path.exists(configFileName):
os.remove(configFileName)
numfiles = 0
else:
numfiles = int(numfiles)
except:
numfiles = 0
rsync.maximum = numfiles
if progress:
rsync.run()
else:
rsync.runsilent()
if sync == "logout":
rsync.runsilent()
try:
if iniParser(configFileName).setVar('rsync',
{'files':rsync.getFilesNum()}):
os.chmod(configFileName, 0600)
os.chown(configFileName,uid,gid)
except:
pass
rsync.close()
if rsync.getExitCode() != 0:
try:
if iniParser(configFileName).setVar(\
'rsync',{'exitcode':rsync.getExitCode()}):
os.chmod(configFileName, 0600)
os.chown(configFileName,uid,gid)
except:
pass
self.printERROR(rsync.getErrMessage())
self.printERROR(_("Failed to execute rsync") + " " + \
str(sync) + " ...")
flagError = True
else:
if sync == "login":
if not (os.path.exists(userHome)):
self.printERROR(_("Directory %s does not exist")%userHome)
else:
self.printERROR(
_("Directory %s does not exist")%homeTemplate)
elif sync == "logout":
if not (os.path.exists(userHome)):
self.printERROR(
_("Directory %s is empty or does not exist")\
%userHome)
else:
self.printERROR(
_("Directory %s does not exist")%homeTemplate)
flagError = True
if flagError:
return False
else:
# Домашняя директория и директория хранения профиля
changeDirs = [userHome, homeTemplate]
for changeDir in changeDirs:
# Получаем права на директорию
mode = getModeFile(changeDir, mode="mode")
# Если права не равны 0700 меняем их
if mode != 0700:
os.chmod(changeDir,0700)
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 removeDir(delFile):
return False
elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]):
os.remove(delFile)
return True
def syncLoginTemplate(self, host, userName, homeDir, homeTemplate, uid, gid,
progress, flagClearHomeDir=True):
"""Синхронизация профиля пользователя с сервера
в переменной 'cl_remote_host' хост для прогрессбара
"""
home = os.path.split(homeDir)[0]
# Если на текущем сервере в ресурсе unix есть файлы
# то синхронируем настройки
if filter(lambda x: not x in ('.calculate',), os.listdir(homeTemplate)):
if not self.syncUser(userName, homeDir, "login", uid, gid,\
homeTemplate, progress=progress,
host=host):
return False
else:
if flagClearHomeDir:
# Удаляем ненужные файлы (очищаем домашнюю директорию)
if not self.clearHomeDir(homeDir):
return False
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 foundArchFile(self,strCurrentTime,archPathProcess,
archPathSuccess, progress=False,
remoteServer=False):
"""Поиск архива профиля пользователя"""
# Ищем архив профиля
# Задержки при поиске файла архива .process
if progress:
title = _("Packing the archive on 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 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 moveArch(self, srcArchFile, dstArchFile, progress=False,\
remoteServer=False, mode=0600):
"""Перенос архива из одной директории в другую"""
class copyTo(color_print):
def __init__(self, destFile):
self.FD = open(destFile, "w")
os.chmod(destFile, mode)
def write(self, data):
self.FD.write(data)
def close(self):
self.FD.close()
# Создаем прогрессбар
if progress:
title = _("Copying archive from the server")
if remoteServer:
title += " " + remoteServer
title += " ..."
progressObj = ProgressBar(title)
archiveSize = os.stat(srcArchFile).st_size
progressObj.setMaximum(archiveSize)
try:
copyToObj = copyTo(dstArchFile)
except:
self.printERROR(_("Failed to create file '%s'")%dstArchFile)
return False
# Копирование файла
try:
if progress:
self.fileReader(srcArchFile, copyToObj, progressObj)
else:
self.fileReader(srcArchFile, copyToObj)
except:
self.printERROR(_("Failed to copy '%(from)s' -> '%(to)s'")\
%{'from':srcArchFile,
'to':dstArchFile})
return False
if progress:
progressObj.shutdownDialog()
return True
def unpackArch(self, homeDir, archFile, progress=False,\
remoteServer=False):
"""Распаковка архива в домашнюю директорию пользователя"""
# Создаем прогрессбар
if progress:
title = _("Unpacking the archive from the 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()
if ret:
self.printERROR(_("Failed to execute %s") %execStr)
self.printERROR(_("Failed to unpack %s") %archFile)
return False
return True
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 iniParser(configFileName).setVar(nameSection, varsDict):
os.chmod(configFileName, 0600)
os.chown(configFileName,uid,gid)
except:
return False
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 does not exist") %userDir)
return False
FD = open(fileName, "w")
modeFile, uidFile, gidFile = getModeFile(fileName)
if mode != modeFile:
os.chmod(fileName, mode)
if (uidFile, gidFile) != (uid, gid):
os.chown(fileName,uid,gid)
if fileTxt:
FD.write(fileTxt)
FD.close()
return True
def getDateObjClientConf(self, fileConfig):
"""Возващает объект времени из конфигурационного файла
.calculate/desktop.env"""
if os.path.exists(fileConfig):
objConfig = iniParser(fileConfig)
data = self.getDataInConfig("main", ["date", "date_logout"],
objConfig)
timeLogout = data["date_logout"]
timeConfig = data["date"]
timeLogoutObj = False
if timeLogout:
try:
timeLogoutObj = time.strptime(timeLogout,
"%Y-%m-%d %H:%M:%S")
except:
pass
timeConfigObj = False
if timeConfig:
try:
timeConfigObj = time.strptime(timeConfig,
"%Y-%m-%d %H:%M:%S")
except:
pass
listDataObj = filter(lambda x: x, (timeLogoutObj, timeConfigObj))
if listDataObj:
return max(listDataObj)
return False
def mountUserResAndSync(self, userName, sync=True, progress=False):
"""Монтирование пользовательских ресурсов и синхронизация настроек"""
# Проверяем на root
if not self.isRoot():
return False
domain = self.clVars.Get("cl_remote_host")
if domain:
foundMountRemote = isMount("/var/calculate/remote")
if not foundMountRemote:
self.mountRemote();
# Проверка на повторный вход пользователя
if self.isTwoSessionsUser(userName):
return False
domain = self.clVars.Get("cl_remote_host")
hostAuth = self.clVars.Get("os_remote_auth")
# В случае компьютера вне домена
if not hostAuth or not domain:
self.printSUCCESS(_("The local profile will be used"))
return True
try:
passwdUsers = map(lambda x: x[0],
map(lambda x: x.split(':'),
map(lambda x: x.strip(),
open("/etc/passwd").readlines())))
except:
self.printERROR(_("Failed to open /etc/passwd"))
return False
if userName in passwdUsers:
try:
pwdObj = pwd.getpwnam(userName)
except:
self.printERROR(_("Failed to found user %s")%userName)
self.umountUserRes()
return False
self.printWARNING(_("User information from /etc/passwd is used"))
return True
# Проверим что компьютер в домене и смонтирован [remote]
connectDomain = self.isDomain()
if not connectDomain:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes()
return False
# Информация о пользователе из LDAP
userLdapInfo = self.ldapDataObj.getUserLdapInfo(userName)
if userLdapInfo:
uid = int(userLdapInfo['uid'])
gid = int(userLdapInfo['gid'])
homeDir = userLdapInfo['home']
else:
self.printERROR(_("Failed to found user %s in LDAP") %userName)
self.umountUserRes()
return False
# Файл хранения настроек пакета
configFileName = os.path.join(homeDir, self.configFileDesktop)
# объект времени для текущего профиля
currentTimeObj = self.getDateObjClientConf(configFileName)
# При отсуствии создаем домашнюю директорию
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 = getKey(userName)
if not userPwd:
self.printERROR(_("User password not found"))
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
# Кеширование пользователя
if not self.cAddUserToCache(userName, userPwd):
return False
# Флаг ошибки
flagError = False
# Имя удаленного сервера
remoteServer = ""
if self.clVars.Get("cl_profile_all_set") == "on":
osLinuxShort = "all"
else:
osLinuxShort = self.clVars.Get("os_linux_shortname")
# В случае включения репликации на сервере True
replOn = self.ldapDataObj.isRepl()
if replOn:
remoteServer = self.ldapDataObj.getNameRemoteServer(userName,
osLinuxShort,
domain)
# Получаем монтируемые директории
names, dictRes = self.getUserMountResources(userName, homeDir,
remoteServer)
# Путь к профилю пользователя по умолчанию
defaultPath = ""
# Хост пользователя по умолчанию
defaultHost = domain
# Флаг - будет использован инкрементальный архив
flagIncrArch = True
# Ошибка при монтировании удаленного сервера
flagErrorMountRemote = False
# Ошибка при cинхронизации с удаленного сервера
flagErrorSyncRemote = False
# Статус синхронизации
syncStatus = True
# Флаг копирования приватных файлов с сервера в случае
# устаревшей даты профиля
flagCopyPrivateFiles = False
# Если профиль на удаленном сервере
if remoteServer and replOn:
# имя файла архива в процессе архивации
archPathProcess = ""
# имя файла архива - после архивации
archPathSuccess = ""
# Создаем инкрементальный архив в случае репликации
prevHost = ""
# Дата профиля на текущем сервере
dateDefaultTemplate = ""
# Текущее время
currTime = str(float(time.time()))
archPathSrc = ""
archPathDst = ""
# Создаваемые пути при монтировании
for name in names:
# Пропускаем монтирование директории если нет синхронизации
if not sync and name == "remote_profile":
continue
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 the directory"))
self.printERROR(_("Permission denied: '%s'")%path)
flagError = True
break
#Проверяем на монтирование директории
if isMount(path):
continue
# Имя хоста для монтирования ресурса
hostConnect = defaultHost
if name == "remote_profile":
hostConnect = remoteServer
# Монтируем Samba ресурс
textLine = self.mountSleepRes(hostConnect, userName, userPwd,
uid, gid, res, path)
if textLine is False:
if name == "remote_profile":
flagErrorMountRemote = True
break
else:
self.printERROR(
_("Failed to mount Samba resource [%s]")\
%res + " ...")
flagError = True
break
# Если нет синхронизации отменяем обработку
if not sync:
continue
if name == "profile" or name == "remote_profile":
# Находим директорию профиля
homeTemplate = os.path.join(path, osLinuxShort)
if not os.path.exists(homeTemplate):
homeTemplate = os.path.join(path, "." + osLinuxShort)
# Проверка на дату профиля на удаленном сервере
if name == "remote_profile":
fileConfigRemote = os.path.join(homeTemplate,
self.configFileDesktop)
# объект времени для удаленного сервера
remoteTimeObj = self.getDateObjClientConf(fileConfigRemote)
if remoteTimeObj and currentTimeObj:
if currentTimeObj > remoteTimeObj:
sync = False
flagCopyPrivateFiles = True
# Если нет синхронизации отменяем обработку
if not sync:
continue
# Примонтирована директория профиля с текущего сервера
if name == "profile":
# Перенос профиля на сервере в директорию без точки
if not self.upgradeTemplateClient(userName, homeDir,
path):
flagError = True
break
fileConfig = os.path.join(homeTemplate,
self.configFileDesktop)
if os.path.exists(fileConfig):
objConfig = 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":
dateDefaultTemplate = date
# Примонтирована директория профиля с удаленного сервера
# и есть дата профиля текущего сервера
if name == "remote_profile" and dateDefaultTemplate:
# Даем команду на создание инкрементального архива проф.
fileConfig = \
os.path.join(homeTemplate,self.configFileServer)
varsConfig = {"arch_date":dateDefaultTemplate,
"curr_time":currTime}
self.setServerCommand(["pack"], varsConfig, fileConfig)
# Отмонтируем директорию профиля с удаленного сервера
textLine = self.umountSleepPath(path)
if not textLine:
flagError = True
break
pathTemplateCurr = dictRes["profile"]["path"]
homeTemplateCurr = os.path.join(pathTemplateCurr,
osLinuxShort)
# Синхронизация профиля с текущего сервера
hostConnect = defaultHost
if not self.syncLoginTemplate(hostConnect, userName,homeDir,
homeTemplateCurr, uid, gid,
progress):
flagError = True
break
# Монтируем директорию профиля с удаленного сервера
hostConnect = remoteServer
textLine = self.mountSleepRes(hostConnect,userName,userPwd,
uid,gid,res,path)
if textLine is False:
self.printERROR(
_("Failed to 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:
archPathSrc = archPathSuccess
archPathDst = ".".join(filter(lambda x: x!="/",
archPathSuccess.rpartition("/")))
# Копируем архив
if not self.moveArch(archPathSrc, archPathDst,
progress, remoteServer):
flagError = True
if archPathSrc:
# Удаление архивного файла
if os.path.exists(archPathSrc):
# Удаляем файл архива
os.remove(archPathSrc)
if archPathDst:
# Удаление архивного файла
if os.path.exists(archPathDst):
# Удаляем файл архива
os.remove(archPathDst)
break
# Распаковываем архив в домашнюю директорию
if not self.unpackArch(homeDir, archPathDst,
progress, remoteServer):
flagError = True
if archPathDst:
# Удаление архивного файла
if os.path.exists(archPathDst):
# Удаляем файл архива
os.remove(archPathDst)
break
# Cканирование домашней директории и получение списка
# файлов
pathListFile = os.path.join(homeTemplate,
self.listTemplFile)
if not self.removeFilesInTemplate(homeDir,pathListFile):
# Отмонтируем пользовательские ресурсы в
# случае ошибки
self.umountUserRes(homeDir)
return False
else:
if name == "remote_profile":
flagIncrArch = False
if archPathProcess or archPathSuccess or archPathSrc or archPathDst:
# Удаление архивных файлов
rmFiles = [archPathProcess, archPathSuccess, archPathSrc,
archPathDst]
rmFiles = filter(lambda x: x, list(set(rmFiles)))
for rmFile in rmFiles:
if os.path.exists(rmFile):
# Удаляем файл архива
os.remove(rmFile)
# Если не удалось замонтировать удаленный профиль
if flagErrorMountRemote:
syncStatus = False
# Если не удалось использовать инкрементальный архив
elif flagIncrArch is False:
# Синхронизируем удаленный профиль
pathTemplate = dictRes["remote_profile"]["path"]
homeTemplate = os.path.join(pathTemplate, osLinuxShort)
if not self.upgradeTemplateClient(userName, homeDir,
pathTemplate):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
# Синхронизация профиля с удаленного сервера
hostConnect = remoteServer
ret = self.syncLoginTemplate(hostConnect, userName, homeDir,
homeTemplate, uid, gid, progress,
False)
if not ret:
flagErrorSyncRemote = True
syncStatus = False
# Если не удалось синхронизировать удаленный синхронизируем
# текущий
homeTemplate = os.path.join(dictRes["profile"]["path"],
osLinuxShort)
# Синхронизация профиля пользователя с текущего сервера
hostConnect = defaultHost
if not self.syncLoginTemplate(hostConnect,userName,homeDir,
homeTemplate,uid,gid,progress):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
if not flagError and flagCopyPrivateFiles:
configFileName = os.path.join(homeDir, self.configFileDesktop)
# записываем в конфигурационный файл статус
# Write to the configuration file status
self.setVarToConfig("main", {"status_sync":"success"},
configFileName, uid, gid)
# Копируем приватные файлы с сервера
# Copy private files from the server
self.copyPrivateFiles(homeTemplate, homeDir)
# Отмонтируем директорию профиля пользователя на удаленном сервере
if sync and "remote_profile" in dictRes:
umountPath = dictRes["remote_profile"]["path"]
if isMount(umountPath):
textLine = self.umountSleepPath(umountPath)
if not textLine:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
# Удаляем директорию удаленного профиля у клиента
if os.path.exists(umountPath) and not os.listdir(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 the directory"))
self.printERROR(_("Permission denied: '%s'")%path)
flagError = True
break
# Проверяем на монтирование директории
if isMount(path):
continue
# Имя хоста для монтирования ресурса
hostConnect = defaultHost
textLine = self.mountSleepRes(hostConnect, userName, userPwd,
uid, gid, res, path)
if textLine is False:
self.printERROR(
_("Failed to mount Samba resource [%s]")
%res + " ...")
flagError = True
break
# Если нет синхронизации отменяем обработку
if not sync:
continue
if name == "profile":
pathTemplate = path
# Перенос профиля на сервере в директорию без точки
if not self.upgradeTemplateClient(userName, homeDir,
pathTemplate):
flagError = True
break
# Директория профиля
homeTemplate = os.path.join(path, osLinuxShort)
# Проверка на дату профиля на текущем сервере
fileConfigThis = os.path.join(homeTemplate,
self.configFileDesktop)
thisTimeObj = self.getDateObjClientConf(fileConfigThis)
if thisTimeObj and currentTimeObj:
if currentTimeObj > thisTimeObj:
sync = False
flagCopyPrivateFiles = True
# Если нет синхронизации отменяем обработку
if not sync:
continue
# Синхронизация профиля пользователя с текущего сервера
hostConnect = defaultHost
if not self.syncLoginTemplate(hostConnect, userName,homeDir,
homeTemplate, uid, gid,
progress):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
if not flagError and flagCopyPrivateFiles:
configFileName = os.path.join(homeDir, self.configFileDesktop)
# записываем в конфигурационный файл статус
# Write to the configuration file status
self.setVarToConfig("main", {"status_sync":"success"},
configFileName, uid, gid)
# Копируем приватные файлы с сервера
# Copy private files from the server
self.copyPrivateFiles(homeTemplate, homeDir)
if flagError:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
self.unpackLinks(homeDir)
if sync:
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 synchronizing with the remote server %s")
%remoteServer)
self.printSUCCESS(_("Mounted user resource of the domain"))
if sync:
self.printOK(_("Get a user profile from the domain") + " ...")
return True
def isSessionUser(self, userName):
"""Проверка на вход пользователя в X"""
xSession = False
reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
resWho = self.execProg("who")
if resWho:
for string in resWho:
if reFoundUser.search(string):
xSession = True
break
return xSession
def tarSymLinks(self,userHome,uid,gid):
"""Create tar archive of symlinks"""
linkArch = pathJoin(userHome,".calculate/links.tar.bz2")
try:
for filename in tarLinks(userHome,linkArch,
skip=self.clVars.Get("cl_sync_del_path")+
self.clVars.Get("cl_sync_skip_path")):
try:
os.unlink(filename)
except OSError:
self.printWARNING(_("Failed to remove %s")%filename)
except:
self.printWARNING(_("Failed to make links archive"))
try:
if os.path.exists(linkArch):
os.chown(linkArch,uid,gid)
except:
self.printWARNING(_("Failed to make links archive"))
def unpackLinks(self,userHome):
"""Unpack archive of symlinks"""
linksArch = pathJoin(userHome,".calculate/links.tar.bz2")
try:
if os.path.exists(linksArch):
tf = tarfile.open(linksArch)
tf.extractall(userHome)
tf.close()
except:
self.printWARNING(_("Failed to unpack links archive"))
def moveHomeDir(self, userHome):
"""Переносим файлы пользователя в Home/Moved"""
# Находим директории и файлы в домашней директории
pathProg = os.getcwd()
os.chdir(userHome)
dirs = []
files = []
movedLink = os.path.join('Moved')
movedPath = os.path.join('Home',"Moved")
skipPaths = self.clVars.Get("cl_moved_skip_path")
if not skipPaths:
skipPaths = ['Disks','Share','Home','Moved','FTP','Desktop']
filesAndDir = filter(lambda x: not ('.' in x[0] or x in\
skipPaths or os.path.islink(x)),
os.listdir('.'))
filesDir = []
for fd in filesAndDir:
if os.path.islink(fd):
os.unlink(fd)
else:
filesDir.append(fd)
# Нахождение файлов отличных ссылок и .* в Desktop
pathDesktop = './Desktop'
filesDirDesk = []
if os.path.exists(pathDesktop):
filesDirDesk = filter(lambda x: not os.path.islink(x) and\
not os.path.split(x)[1].startswith('.') and\
not x.rpartition('.')[2]=='desktop',
map(lambda x: os.path.join(pathDesktop,x),\
os.listdir(pathDesktop)))
movedPathDesk = os.path.join(movedPath, pathDesktop)
filesDir += filesDirDesk
if not filesDir or not os.path.exists("Home"):
# Удаляем пустую папку 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 textLine is False:
self.printERROR(_("Failed to execute") + " " + str(execStr) +\
" ...")
return False
execStr = "rm -rf '%s'" %fd
textLine = self.execProg(execStr)
if textLine is False:
self.printERROR(_("Failed to execute") + " " + str(execStr) +\
" ...")
return False
os.chdir(pathProg)
return True
def removeNoiseFiles(self, userHome):
"""Удаление файлов, создающих помехи работе dm"""
noiseFiles = []
for nsFile in noiseFiles:
rmFile = os.path.join(userHome, nsFile)
if os.path.exists(rmFile):
os.remove(rmFile)
return True
def getPrivateFiles(self, userHome):
"""Все приватные файлы пользователя относительно директории userHome"""
privateFiles = []
for privateHomeDir in self.privateDirs:
privateDir = os.path.join(userHome,privateHomeDir)
if os.path.isdir(privateDir):
# .ssh файлы относительно домашней директории пользователя
privateFiles += map(lambda x:os.path.join(privateHomeDir,x),
filter(lambda x:\
os.path.isfile(os.path.join(privateDir,x)),
os.listdir(privateDir)))
return self.privateFiles + privateFiles
def removePrivateFiles(self, userHome):
"""Удаление приватных файлов"""
privateFiles = self.getPrivateFiles(userHome)
for prFile in privateFiles:
rmFile = os.path.join(userHome, prFile)
if os.path.exists(rmFile):
os.remove(rmFile)
return True
def copyPrivateFiles(self, serverProfileDir, userHomeDir):
"""Копирование приватных файлов c сервера в домашнюю директорию"""
privateFiles = self.getPrivateFiles(serverProfileDir)
for prFile in privateFiles:
src = os.path.join(serverProfileDir, prFile)
dst = os.path.join(userHomeDir, prFile)
if os.path.exists(src):
dstPath = os.path.dirname(dst)
if not os.path.exists(dstPath):
listElPath = []
for el in filter(lambda x: x,
dstPath.partition(userHomeDir)[2].split("/")):
listElPath.append(el)
joinPath = "/".join(listElPath)
dPath = os.path.join(userHomeDir, joinPath)
if not os.path.exists(dPath):
sPath = os.path.join(serverProfileDir, joinPath)
sMode, sUid, sGid = getModeFile(sPath)
os.mkdir(dPath,sMode)
os.chown(dPath, sUid, sGid)
copy2(src, dst)
sUid, sGid = getModeFile(src, mode="owner")
os.chown(dst, sUid, sGid)
return True
def scanDirectory(self, scanDir, listFiles, skipPath=[], prefix=False,
flagDir=False):
"""Генерация списка файлов и директорий"""
if not prefix:
prefix = os.path.join(scanDir,"")
if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]):
for fileOrDir in os.listdir(scanDir):
absPath = os.path.join(scanDir,fileOrDir)
relPath = absPath.split(prefix)[1]
if relPath in skipPath:
continue
listFiles.append(relPath)
stInfo = os.lstat(absPath)
statInfo = stInfo[stat.ST_MODE]
if stat.S_ISDIR(statInfo):
self.scanDirectory(absPath, listFiles,
skipPath, prefix, True)
def getListFilesTemplate(self, homeDir):
"""Генерация списка файлов в домашней директориии
Исключая монтируемые директории
"""
home = os.path.join(homeDir, "")
execStr = "mount"
textLines = self.execProg(execStr)
# Пропускаемые директории для сканирования
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(_("Mounting point for server resources not found"))
return False
self.scanDirectory(homeDir, listFiles, skipPaths)
return listFiles
def removeFilesInTemplate(self, homeDir, pathListFile):
"""Удаляем файлы которых нет в профиле пользователя"""
# Получаем файлы профиля на сервере
try:
filesTemplateTxt = open(pathListFile).read()
except:
self.printERROR(_("Failed to open %s")%pathListFile)
return False
listFilesTemplate = filter(lambda x: x.strip(),
filesTemplateTxt.split("\n"))
filesTemplate = set(listFilesTemplate)
# Получаем файлы в домашней директории
listFilesHome = self.getListFilesTemplate(homeDir)
if listFilesHome is False:
return False
filesHome = set(listFilesHome)
filesRemove = list(filesHome - filesTemplate)
filesRemove.sort(lambda x, y: cmp(len(y), len(x)))
rmPath = ""
try:
for rmFile in filesRemove:
rmPath = os.path.join(homeDir, rmFile)
if os.path.islink(rmPath):
os.unlink(rmPath)
elif os.path.isfile(rmPath):
os.remove(rmPath)
elif os.path.isdir(rmPath):
os.rmdir(rmPath)
else:
os.remove(rmPath)
except:
self.printERROR(_("Failed to remove %s")%rmPath)
return False
return True
def getRunCommandsWithEnv(self):
"""List run program"""
def getCmd(procNum):
cmdLineFile = '/proc/%s/cmdline'%procNum
environFile = '/proc/%s/environ'%procNum
try:
if os.path.exists(cmdLineFile) and \
os.path.exists(environFile):
return (open(cmdLineFile,'r').read().strip(),
open(environFile,'r').read().strip())
except:
pass
return ("","")
if not os.access('/proc',os.R_OK):
return []
return map(getCmd,
filter(lambda x:x.isdigit(),
os.listdir('/proc')))
def clearUserKey(self, userName):
"""Очищает пользовательский ключ ядра"""
# Ищем ключ в ядре и проверяем не выполняет ли он повторный вход
if getKey(userName) and not \
filter(lambda x:"xdm/xdm\x00--login" in x[0] and \
("USER=%s"%userName) in x[1],self.getRunCommandsWithEnv()):
# очищаем
ret = clearKey(userName)
if ret == 0:
return True
else:
self.printERROR(_("Failed to clear the kernel key for user %s")\
%userName)
return False
return True
def umountUserResAndSync(self, userName, progress=False, sync=True):
"""Отмонтирование пользовательских ресурсов и синхронизация настроек"""
# Проверяем на root
if not self.isRoot():
return False
try:
passwdUsers = map(lambda x: x[0],
map(lambda x: x.split(':'),
map(lambda x: x.strip(),
open("/etc/passwd").readlines())))
except:
self.printERROR(_("Failed to open /etc/passwd"))
return False
currentDateStr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if userName in passwdUsers:
try:
pwdObj = pwd.getpwnam(userName)
except:
self.printERROR(_("Failed to found user %s")%userName)
self.umountUserRes()
return False
homeDir = pwdObj.pw_dir
uid = pwdObj.pw_uid
gid = pwdObj.pw_gid
configFileName = os.path.join(homeDir, self.configFileDesktop)
self.setVarToConfig("main", {"date_logout":currentDateStr},
configFileName, uid, gid)
self.printWARNING(_("User information from /etc/passwd is used"))
self.printSUCCESS(_("The local profile will be used"))
return True
domain = self.clVars.Get("cl_remote_host")
hostAuth = self.clVars.Get("os_remote_auth")
# В случае компьютера вне домена
if not hostAuth or not domain:
self.printSUCCESS(_("The local profile will be used"))
return True
connectDomain = self.isDomain()
if not connectDomain:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes()
return False
# Если пользователь в X сессии тогда не будем отмонтировать ресурсы
if self.isSessionUser(userName):
self.printERROR(_("User %s is already in X session")%userName)
self.printERROR(_("Failed to unmount user %s resource")%userName)
return False
# Информация о пользователе из LDAP
userLdapInfo = self.ldapDataObj.getUserLdapInfo(userName)
if userLdapInfo:
uid = int(userLdapInfo['uid'])
gid = int(userLdapInfo['gid'])
homeDir = userLdapInfo['home']
else:
self.printERROR(_("Failed to found user %s in LDAP")%userName)
self.umountUserRes()
return False
# Файл хранения настроек пакета
configFileName = os.path.join(homeDir, self.configFileDesktop)
self.setVarToConfig("main", {"date_logout":currentDateStr},
configFileName, uid, gid)
if os.path.exists(homeDir):
self.moveHomeDir(homeDir)
if sync:
self.tarSymLinks(homeDir,uid,gid)
else:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.printERROR(_("Directory %s not found") % homeDir)
self.umountUserRes(homeDir)
return False
# необходима ли синхронизация с локальным сервером
needSync = sync
# проверяем произведен ли корректный вход в систему -
# в этом случае закачиваем профиль с локальной машины на сервер
exitStr = iniParser(configFileName).getVar('main','status_sync')
# проверяем cтатус синхронизации: не содержит ли он "process"
if exitStr == "process" or exitStr == "error":
needSync = False
if self.clVars.Get("cl_profile_all_set") == "on":
osLinuxShort = "all"
else:
osLinuxShort = self.clVars.Get("os_linux_shortname")
# Получаем монтируемые директории
names, dictRes = self.getUserMountResources(userName, homeDir, False)
pathUnix = dictRes["profile"]["path"]
homeTemplate = os.path.join(pathUnix, osLinuxShort)
if needSync:
# Синхронизация профиля пользователя на сервер
if not self.syncUser(userName, homeDir, "logout", uid, gid,
homeTemplate):
# Удаляем файлы, мешающие работе dm
self.removeNoiseFiles(homeDir)
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Очищаем ключ в ядре
self.clearUserKey(userName)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.umountUserRes(homeDir)
return False
# Удаляем файлы, мешающие работе dm
self.removeNoiseFiles(homeDir)
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Очищаем ключ в ядре
self.clearUserKey(userName)
# Отмонтируем директории
if not self.umountUserRes(homeDir):
return False
# Удаляем пустые директории
for name in names:
path = dictRes[name]["path"]
if os.path.exists(path) and not os.listdir(path):
try:
os.rmdir(path)
except:
self.printERROR(_("Failed to remove dir %s")% path)
return False
# Удаляем Disks директорию если она пуста
pathDisks = os.path.join(homeDir,"Disks")
if os.path.exists(pathDisks) and not os.listdir(pathDisks):
try:
os.rmdir(pathDisks)
except:
self.printERROR(_("Failed to remove dir %s")% pathDisks)
return False
if needSync:
self.printSUCCESS(_("User profile saved in the domain"))
self.printOK(_("Domain user resource unmounted") + " ...")
return True
def getMountUserPaths(self, homeDir=False):
"""Находит пользовательские примонтированные пути"""
# Имя пользователя
if not homeDir:
userName = self.clVars.Get("ur_login")
try:
homeDir = pwd.getpwnam(userName).pw_dir
except:
homeDir = os.path.join("/home",userName)
dirStart, dirEnd = os.path.split(homeDir)
mountTemplateDir = os.path.join(dirStart, ".%s" %dirEnd)
mountRemoteTemplateDir = os.path.join(dirStart, ".%s.remote" %dirEnd)
return filter(lambda x: x.startswith(homeDir) or\
x.startswith(mountTemplateDir) or\
x.startswith(mountRemoteTemplateDir),
map(lambda x: x.split(" ")[1],\
open("/proc/mounts").readlines()))
def execProg(self, cmdStrProg, inStr=False, envProg={}):
"""Выполняет внешнюю программу
Параметры:
cmdStrProg внешняя программа
inStr данные передаваемые программе на страндартный вход.
Возвращаемые параметры:
строки которые выведет внешняя программа или False в случае ошибки
"""
env_path = {"PATH":getpathenv()}
env = {}
env.update(os.environ.items() + env_path.items() + envProg.items())
retCode,programOut = runOsCommand(cmdStrProg,in_str=inStr,env_dict=env)
if not retCode:
return programOut
return False
def umountSleepPath(self, path):
"""Отмонтирует путь при неудаче задержка потом повтор"""
# Задержки при отмонтированиии директории
sleeps = [0.5, 1, 2]
# Проверяем на монтирование директорию
if isMount(path):
textLines = self.execProg("umount %s"%path)
if textLines is False:
i = 0
flagError = False
while (i<len(sleeps) and textLines is False):
# Задержка перед следующей попыткой
time.sleep(sleeps[i])
# Отмонтируем Samba ресурс
if isMount(path):
textLines = self.execProg("umount %s"%path)
else:
textLines = True
break
i += 1
if textLines is False:
self.execProg("fuser -km %s"%path)
sleeps = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
i = 0
while (i<len(sleeps) and isMount(path)):
time.sleep(sleeps[i])
self.execProg("umount %s"%path)
i += 1
if isMount(path):
self.printERROR(_("Failed to unmount path %s")%path + " ...")
return False
return True
def umountUserRes(self, homeDir=False):
"""Отмонтируем пользовательские директории если они есть"""
umountPaths = self.getMountUserPaths(homeDir)
ret = True
for umountPath in umountPaths:
if not self.umountSleepPath(umountPath):
ret = False
break
return ret
def installProg(self,onlyEnv=False):
"""Наложение шаблонов на систему при инсталяции
onlyEnv - выполнить только добавление в calculate2.env
и client в автозапуск
"""
# Действие выход из домена
self.clVars.Set("cl_action", "install", True)
domain = self.clVars.Get("cl_remote_host")
domainLive = self.clVars.Get("cl_remote_host_live")
self.clVars.AppendToList("cl_merges", __app__, force=True)
# Добавление программы в инсталяционную переменную
if not appendProgramToEnvFile(__app__, self.clVars):
self.printERROR(_("Failed to save '%s'") %__app__ + " " +\
_("to %s") %self.clVars.Get("cl_env_path")[0])
return False
if domain or domainLive:
if not self.addDaemonAutostart("client"):
return False
if onlyEnv:
# apply templates (this appling client templates need
# for safety executing cl-install --startup for
# already domained computers
if self.clVars.Get("os_remote_auth") and \
not self.applyTemplatesFromSystem():
self.printERROR(_("Failed to apply install templates"))
return False
return True
if domain and not self.mountRemote():
return False
if not self.applyTemplatesFromSystem():
self.printERROR(_("Failed to apply install templates"))
return False
if domain:
self.printOK(_("The domain profile will be used") + " ...")
else:
self.printOK(_("The local profile will be used") + " ...")
return True
def getDefaultRunlevelDaemons(self):
"""Получаем всех демонов в default уровне"""
execStr = "rc-update show"
textLine = self.execProg(execStr)
if textLine is False:
self.printERROR(_("ERROR") + ": " + execStr)
return False
else:
splLines = filter(lambda x: len(x)==2 and "default" in x[1],\
map(lambda x: x.split("|"),textLine))
splLines = map(lambda x: x[0].strip(), splLines)
return splLines
def uninstallProg(self):
"""Наложение шаблонов на систему при деинсталяции"""
# Проверяем на root
if not self.isRoot():
return False
# Действие удаление
self.clVars.Set("cl_action", "uninstall", True)
# Удаляем переменные из env файлов
self.removeVars()
# Удаление программы из инсталяционной переменной
if not removeProgramToEnvFile(__app__, self.clVars):
self.printERROR(_("Failed to remove '%(app)s' from %(path)s")%
{'app':__app__,
'path':self.clVars.Get("cl_env_path")[0]})
return False
if not self.applyTemplatesFromSystem():
self.printERROR(_("Failed to apply uninstall templates"))
return False
if not self.delDaemonAutostart("client"):
return False
self.printOK(_("Apply uninstall templates"))
return True
def delDaemonAutostart(self, daemon):
"""Удаляет демона из автозагрузки"""
defaultDaemons = self.getDefaultRunlevelDaemons()
if defaultDaemons is False:
return False
if daemon in defaultDaemons:
execStr = "rc-update del %s default" %daemon
textLine = self.execProg(execStr)
if textLine is False:
self.printERROR(_("ERROR") + ": " + execStr)
self.printERROR(_("Failed to delete from the default runlevel"))
return False
else:
return True
return True
def delDomain(self):
"""выводим из домена"""
# Проверяем на root
if not self.isRoot():
return False
pathRemote = "/var/calculate/remote"
foundMountRemote = isMount(pathRemote)
domain = self.clVars.Get("cl_remote_host")
if foundMountRemote:
textLineUmount = self.umountSleepPath(pathRemote)
if not textLineUmount:
return False
if not domain:
self.printWARNING(_("The computer is not in the domain"))
return True
# Удаляем переменные из env файлов
self.removeVars()
# Устанавливаем действие выход из домена
self.clVars.Set("cl_action", "undomain", True)
if not self.applyTemplatesFromSystem():
self.printERROR(_("Failed to apply undomain templates"))
return False
# Delete LDAP users from system and clear cache
if not self.cDelLdapSysUsersAndClearCache():
return False
# Рестартуем dbus
self.restartDBus()
self.printSUCCESS(_("Apply undomain templates"))
if not self.delDaemonAutostart("client"):
return False
self.printOK(_("Computer removed from domain %s")%domain + " ...")
return True
def getUserPassword(self, pwDialog=False):
"""Получить пароль у пользователя
pwDialog - приглашение ввода пароля
"""
if os.readlink('/proc/self/fd/0') == '/dev/console':
os.system('chvt 1 &>/dev/null')
if not pwDialog:
pwDialog = _("Password")
userPwd = getpass.getpass(pwDialog+":")
return userPwd
def addDaemonAutostart(self, daemon):
"""Прописывает демона в автозагрузку"""
defaultDaemons = self.getDefaultRunlevelDaemons()
if defaultDaemons is False:
return False
if daemon in defaultDaemons:
return True
execStr = "rc-update add %s default" %daemon
textLine = self.execProg(execStr)
if textLine is False:
self.printERROR(_("ERROR") + ": " + execStr)
self.printERROR(_("Failed to add to the default runlevel"))
return False
else:
return True
def restartDBus(self):
"""Перезапускаем службу D-Bus"""
dbusDaemon = 'rc-service -i dbus'
existsDaemon = 'rc-service -e dbus'
# если установлена и запущена
if os.system(existsDaemon) == 0 and \
os.system(dbusDaemon + ' status &>/dev/null') == 0:
# пезапустить
if os.system(dbusDaemon + ' restart -- -s &>/dev/null') != 0:
self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...")
return False
return True
def getInfoService(self, service, option, envFile=False):
"""Получить параметр сервиса из env"""
if not envFile:
if self.convObj == None:
# файл /var/calculate/remote/server.env
envFile = self.clVars.Get("cl_env_server_path")
if os.access(envFile, os.R_OK):
self.convObj = False
elif os.access("/var/calculate/remote/calculate.env", os.R_OK):
self.convObj = convertEnv()
if self.convObj:
return self.convObj.getVar(service, option)
if not self.optionsInfo:
if not envFile:
# файл /var/calculate/remote/server.env
envFile = self.clVars.Get("cl_env_server_path")
optInfo = self.clVars.GetRemoteInfo(envFile)
if optInfo is False:
return False
if optInfo:
self.optionsInfo = optInfo
value = ''
if service in self.optionsInfo and option in self.optionsInfo[service]:
value = self.optionsInfo[service][option]
return value
def addDomain(self, domainName):
"""Вводим в домен"""
# Проверяем на root
if not self.isRoot():
return False
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)
if resPing is False:
self.printERROR(_("Failed to execute \"%s\"")%execStr)
return False
foudHost = False
foundHostSamba = False
foundMountRemote = False
reFoundHost = re.compile("(\d+)\% packet loss")
if resPing 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(_("Domain %s not found")%domain)
return False
reFoundHostSamba = re.compile("Server=\[Samba.+\]")
resSmbClient = self.execProg("smbclient -N -L %s" %domain)
if not resSmbClient is False:
for string in resSmbClient:
if reFoundHostSamba.search(string):
foundHostSamba = True
break
if not foundHostSamba:
self.printERROR(_("Samba server not found in %s")%domain)
return False
remoteHost = self.clVars.Get("cl_remote_host")
if remoteHost:
self.printERROR(_("The computer is already in the domain %s")\
%remoteHost)
self.printWARNING(_("Before joining the domain, "
"you need to remove it from the previous domain"))
self.printWARNING(_("Run 'cl-client -r'"))
return False
foundMountRemote = isMount("/var/calculate/remote")
if foundMountRemote:
self.printERROR(_("Samba resource [%s] mounted")%\
"remote" + " ...")
return False
else:
def passwdQueue():
remotePw = self.clVars.Get('cl_remote_pw')
if remotePw:
yield remotePw
yield self.getUserPassword(\
_("Domain password for the desktop"))
pathRemote = "/var/calculate/remote"
for pwdRemote in passwdQueue():
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, envProg={"PASSWD":pwdRemote})
if not textLine is False:
self.printSUCCESS(_("Samba resource [%s] mounted")%"remote" + \
" ...")
self.clVars.Write("cl_remote_host", domain, True, "local")
self.clVars.Write("cl_remote_pw", pwdRemote, True, "local")
break
else:
self.printERROR(_("Failed to mount Samba resource [%s]")%\
"remote" + " ...")
return False
servDn = self.getInfoService("ldap", "services_dn")
unixDn = self.getInfoService("unix", "dn")
bindDn = self.getInfoService("unix", "bind_dn")
bindPw = self.getInfoService("unix", "bind_pw")
# запишем их
if not (servDn and unixDn and bindDn and bindPw):
self.printERROR(_("Info not found on the server") + ": " +\
_("services DN or unix DN or bind DN or bind password"))
return False
# Наложим шаблоны - domain
# Действие - ввод в домен
self.clVars.Set("cl_action", "domain", True)
# Доменная авторизация
self.clVars.Set("os_remote_auth", domain)
if not self.applyTemplatesFromSystem():
self.printERROR(
_("Failed to apply install templates or domain templates"))
return False
# Рестартуем dbus
self.restartDBus()
if not self.addDaemonAutostart("client"):
return False
# Записываем переменную
self.clVars.Write("os_remote_auth", domain, True)
# Записываем текущую версию программы
currentVersion = self.clVars.Get("cl_ver")
self.clVars.Write("os_remote_client", currentVersion, True)
self.printOK(_("Computer added to domain %s")%domain + " ...")
return True
def relevanceTemplates(self, hostAuth):
"""Определяем актуальность наложенных шаблонов
в зависимости от версии программы
Перед запуском обязательно должен быть определен объект переменных
self.clVars
Если актуальны - True, если нет False
"""
# '' или имя хоста
if hostAuth != self.clVars.Get("os_remote_auth"):
return False
clTempl = template(self.clVars)
# Текущая версия программы
currentVersion = self.clVars.Get("cl_ver")
# Версия программы которая ранее работала с шаблонами
previousVersion = self.clVars.Get("os_remote_client")
cVersion,pVersion = clTempl._convertVers(currentVersion,previousVersion)
# Если версии программ не равны то шаблоны не актуальные
if cVersion != pVersion:
return False
return True
def applyRelevanceTemplates(self, hostAuth=""):
"""Накладывает релевантные шаблоны
Перед запуском обязательно должен быть определен объект переменных
self.clVars
"""
if not self.relevanceTemplates(hostAuth):
if hostAuth:
# Устанавливаем действие вход в домен
self.clVars.Set("cl_action","domain",True)
else:
# Устанавливаем действие выход из домена
self.clVars.Set("cl_action","undomain",True)
self.clVars.Set("os_remote_auth", hostAuth)
# Наложим шаблоны
dirsAndFiles = self.applyTemplatesFromSystem()
if not dirsAndFiles:
if hostAuth:
self.printERROR(_("Failed to apply domain templates"))
else:
self.printERROR(_("Failed to apply undomain templates"))
return False
if hostAuth:
self.printOK(_("Templates set for network mode"))
currentVersion = self.clVars.Get("cl_ver")
self.clVars.Write("os_remote_client", currentVersion, True)
self.clVars.Write("os_remote_auth", hostAuth, True)
else:
self.printOK(_("Templates set for local mode"))
self.clVars.Delete("os_remote_auth")
self.clVars.Delete("os_remote_client")
return True
def cDelLdapSysUsersAndSyncCache(self):
"""Delete LDAP users from system and synchronize cache"""
cacheObj = userCache()
if not cacheObj.deleteCacheUsersFromSystem():
return False
if not cacheObj.syncCacheToLdap():
return False
return True
def cAddCacheUsersFromSystem(self):
"""Add cache users from system"""
cacheObj = userCache()
if not cacheObj.addCacheUsersFromSystem():
return False
return True
def cAddUserToCache(self, userName, userPwd):
"""Add user to cache"""
cacheObj = userCache()
pwdHash = self.getHashPasswd(userPwd, "shadow_ssha256")
if not cacheObj.addUserToCache(userName, pwdHash):
return False
return True
def cDelLdapSysUsersAndClearCache(self):
"""Delete LDAP users from system and clear cache"""
cacheObj = userCache()
if not cacheObj.deleteCacheUsersFromSystem():
return False
if not cacheObj.clearCache():
return False
return True
def moveHomeDirs(self):
"""Move home dirs /var/calculate/client-home -> /home
if user in cache
"""
cacheObj = userCache()
loginUsersData = cacheObj.getLoginDomainUsers()
if loginUsersData is False:
return False
previousHome = "/var/calculate/client-home"
if isMount(previousHome):
return True
if os.path.exists(previousHome):
flagMovedUsers = False
for userName,x,uid,gid,gecos,directory,shell in loginUsersData:
homeDir = directory
pathUserList = filter(lambda x: x, directory.split('/'))
if not pathUserList:
continue
pathUser = "/".join(pathUserList[1:])
srcDir = pathJoin(previousHome, pathUser)
if os.path.exists(srcDir) and not os.path.exists(homeDir):
flagMovedUsers = True
destDir = os.path.dirname(homeDir)
self.printWARNING(_("Moved %(src)s to %(dest)s")\
%{"src":srcDir,"dest":homeDir})
if not self.copyTemplateDir(srcDir, destDir):
return False
if flagMovedUsers and not os.listdir(previousHome):
os.rmdir(previousHome)
return True
def mountRemote(self):
"""Монтирование remote если компьютер в домене
а так-же ввод в домен если найдено имя хоста и пароль для подключения
"""
# Проверяем на root
if not self.isRoot():
return False
domain = self.clVars.Get("cl_remote_host")
if domain:
foundMountRemote = isMount("/var/calculate/remote")
else:
self.printWARNING(_("This computer is not in the domain"))
# Если шаблоны не актуальны накладываем новую версию шаблонов
if not self.applyRelevanceTemplates():
return False
return True
if foundMountRemote:
self.printWARNING(_("Samba resource [%s] mounted")%"remote" + \
" ...")
# Накладываем сетевые шаблоны
if domain:
self.clVars.flIniFile()
if not self.applyRelevanceTemplates(domain):
return False
# Delete LDAP users from system and synchronize cache
if not self.cDelLdapSysUsersAndSyncCache():
return False
return True
else:
pathRemote = "/var/calculate/remote"
pwdRemote = self.clVars.Get("cl_remote_pw")
if not (domain and pwdRemote):
self.printERROR(_("Variable not found")+\
": cl_remote_pw ...")
return False
if not os.path.exists(pathRemote):
os.makedirs(pathRemote)
mountStr = "mount -t cifs -o user=client //%s/remote %s"\
%(domain,pathRemote)
textLine = self.execProg(mountStr, envProg={"PASSWD":pwdRemote})
if textLine is False:
self.printWARNING(_("Failed to mount Samba resource [%s]")%\
"remote" + " ...")
beforeRemoteAuth = self.clVars.Get('os_remote_auth')
# Если шаблоны не актуальны накладываем новую версию шаблонов
if not self.applyRelevanceTemplates():
return False
if not self.cAddCacheUsersFromSystem():
return False
if not self.moveHomeDirs():
return False
if beforeRemoteAuth != self.clVars.Get('os_remote_auth'):
self.restartDBus()
return True
self.printSUCCESS(_("Samba resource [%s] mounted") % "remote" +\
" ...")
# Накладываем сетевые шаблоны
if domain:
self.clVars.flIniFile()
beforeRemoteAuth = self.clVars.Get('os_remote_auth')
if not self.applyRelevanceTemplates(domain):
return False
# Delete LDAP users from system and synchronize cache
if not self.cDelLdapSysUsersAndSyncCache():
return False
if beforeRemoteAuth != self.clVars.Get('os_remote_auth'):
self.restartDBus()
return True
def updateEnvFiles(self):
"""Апдейт env файлов до версии 2.2"""
previousVersion = self.clVars.Get("os_remote_client")
flagUpdate = False
if not previousVersion:
# Апдейт
envFile="/var/calculate/calculate.env"
remoteHost = self.getInfoService("client", "cl_remote_host",
envFile=envFile)
remotePw = self.getInfoService("client", "cl_remote_pw",
envFile=envFile)
if remoteHost and remotePw:
self.clVars.Write("cl_remote_host", remoteHost, True, "local")
self.clVars.Write("cl_remote_pw", remotePw, True, "local")
envFile="/etc/calculate/calculate.env"
previousVersion = self.getInfoService("client", "os_remote_client",
envFile=envFile)
workHost = self.getInfoService("client", "os_remote_auth",
envFile=envFile)
if previousVersion and workHost:
self.clVars.Write("os_remote_client", previousVersion, True)
if workHost != "local":
self.clVars.Write("os_remote_auth", workHost, True)
self.printOK(_("Env files updated") + " ...")
flagUpdate = True
return flagUpdate
def setUserPasswordToServer(self):
"""Установка пароля пользователя на сервере"""
# Проверяем на root
if self.isRoot(False):
self.printERROR(_("The user is root"))
self.printWARNING(\
_("The program can be executed by a non-root user only"))
return False
# DNS имя хоста
server = self.ldapDataObj.getHost()
# DN пользователей
usersDN = self.ldapDataObj.getUsersDN()
if not (server and usersDN):
self.printERROR(_("The computer is not in the domain"))
self.printWARNING(_("Use passwd"))
return False
count = 2
# Получаем старый пароль пользователя
curPassword = self.getUserPassword(_("Enter the current password"))
if not curPassword:
self.printERROR(_("The current password is empty"))
for i in range(count):
count -= 1
# Получаем старый пароль пользователя
curPassword = self.getUserPassword(
_("Enter the current password"))
if curPassword:
break
self.printERROR(_("The current password is empty"))
if not curPassword:
return False
userDN = self.ldapDataObj.addDN("uid=%s"%os.environ["USER"], usersDN)
# Проверяем в LDAP сервере текущий пароль пользователя
ret, err = self.checkUserPwdLDAP(server, userDN, curPassword)
if not ret:
self.printERROR(err)
for i in range(count):
# Получаем старый пароль пользователя
curPassword = self.getUserPassword(
_("Enter the current password"))
if not curPassword:
self.printERROR(_("The current password is empty"))
continue
# Проверяем в LDAP сервере текущий пароль пользователя
ret, err = self.checkUserPwdLDAP(server, userDN, curPassword)
if ret:
break
self.printERROR(err)
if not ret:
return False
password = self.getUserPwd({"p":""}, "p", False)
if password is False:
for i in range(2):
password = self.getUserPwd({"p":""}, "p", False)
if password:
break
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.configFileServer)
if not self.setServerCommand(["passwd_samba"], varsConfig, fileConfig):
return False
self.printOK(_("%s's password changed")%os.environ["USER"] + \
" ...")
self.printWARNING(_("The password will be changed when you log "
"out from the X session"))
return True