|
|
#-*- 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
|
|
|
|