|
|
#-*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org
|
|
|
#
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
# You may obtain a copy of the License at
|
|
|
#
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
#
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
# See the License for the specific language governing permissions and
|
|
|
# limitations under the License.
|
|
|
|
|
|
__version__ = "2.2.0.0"
|
|
|
__app__ = "calculate-client"
|
|
|
|
|
|
import os
|
|
|
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
|
|
|
from _cl_keys import getKey, clearKey
|
|
|
from convertenv import convertEnv
|
|
|
from encrypt import encrypt
|
|
|
|
|
|
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, opts=["all"]):
|
|
|
"""Печать существующих переменных"""
|
|
|
if opts == ["all"]:
|
|
|
self.clVars.printVars()
|
|
|
else:
|
|
|
self.clVars.printVars(opts)
|
|
|
|
|
|
class RsyncProgressBar:
|
|
|
'''Объект запуска rsync для получения количества созданных файлов и
|
|
|
при необходимости вывода progressbar
|
|
|
'''
|
|
|
|
|
|
# получение номера передаваемого файла из инф потока rsync
|
|
|
senderre = re.compile("\[sender\] i=(\d+) ", re.S)
|
|
|
# получение номера получаемого файла из потока rsync
|
|
|
receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S)
|
|
|
pipe = None
|
|
|
maximum = 1
|
|
|
copyStarting = False
|
|
|
|
|
|
def __init__(self, title, secondtitle, rsyncstr, maximum=1):
|
|
|
self.title = title
|
|
|
self.secondtitle = secondtitle
|
|
|
self.maximum = maximum
|
|
|
self.rsyncstr = rsyncstr
|
|
|
self.progress = ProgressBar(title,1)
|
|
|
self.progress.setMaximum(self.maximum)
|
|
|
|
|
|
def getFilesNum(self):
|
|
|
'''Получить количество файлов созданных генератором'''
|
|
|
if self.pipe:
|
|
|
return self.value
|
|
|
|
|
|
def getExitCode(self):
|
|
|
'''Получить код выхода rsync'''
|
|
|
if self.pipe:
|
|
|
return os.WEXITSTATUS(self.pipe.wait())
|
|
|
|
|
|
def runsilent(self):
|
|
|
'''Запустить rsync без progressbar'''
|
|
|
self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE,
|
|
|
stdout=subprocess.PIPE,
|
|
|
stderr=subprocess.PIPE,
|
|
|
close_fds=True, shell=True)
|
|
|
while True:
|
|
|
s = self.pipe.stdout.readline()
|
|
|
if len(s) == 0:
|
|
|
break
|
|
|
q = self.receiverre.search(s)
|
|
|
if q:
|
|
|
self.value = int(q.groups()[0])
|
|
|
|
|
|
def run(self):
|
|
|
'''Запустить rsync с progressbar'''
|
|
|
self.progress.openDialog(self.title,0)
|
|
|
self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE,
|
|
|
stdout=subprocess.PIPE,
|
|
|
stderr=subprocess.PIPE,
|
|
|
close_fds=True, shell=True)
|
|
|
oldpercent = 0
|
|
|
while True:
|
|
|
s = self.pipe.stdout.readline()
|
|
|
if len(s) == 0:
|
|
|
break
|
|
|
q = self.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 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
|
|
|
# если местоположение актуального профиля найти не удалось
|
|
|
# или его местоположение не на локальном сервере
|
|
|
if not prevHost or prevHost == curHost:
|
|
|
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(_("Can not write variables in file %s")\
|
|
|
%fileConfig)
|
|
|
return False
|
|
|
if not uid is None and not gid is None:
|
|
|
os.chown(fileConfig, uid, gid)
|
|
|
return True
|
|
|
|
|
|
def checkUserPwdLDAP(self, server, userDN, password):
|
|
|
"""Проверка пароля Unix пользователя на сервере"""
|
|
|
ldapInit = ldap.initialize("ldap://%s"%server)
|
|
|
errMessage = ""
|
|
|
try:
|
|
|
ldapInit.bind_s(userDN, password)
|
|
|
except ldap.INVALID_CREDENTIALS:
|
|
|
errMessage = _("Password incorrect")
|
|
|
return False, errMessage
|
|
|
except ldap.LDAPError, e:
|
|
|
errMessage = e[0]['desc']
|
|
|
return False, errMessage
|
|
|
return True, errMessage
|
|
|
|
|
|
def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False):
|
|
|
"""Получить пароль у пользователя
|
|
|
|
|
|
options - полученные опции командной строки
|
|
|
optDialog - опция командной строки для вывода диалога для получения
|
|
|
пароля
|
|
|
optStdIn - опция командной строки для получения пароля из
|
|
|
стандартного ввода (stdin)
|
|
|
pwDialog - структура для вывода приглашения в режиме диалога
|
|
|
"""
|
|
|
userPwd = ""
|
|
|
if optStdIn and options.has_key(optStdIn):
|
|
|
pwdA = sys.stdin.readline().rstrip()
|
|
|
pwdB = sys.stdin.readline().rstrip()
|
|
|
elif optDialog and options.has_key(optDialog):
|
|
|
if not pwDialog:
|
|
|
pwDialog = [_("New password"),
|
|
|
_("Retype new password")]
|
|
|
pwdA = getpass.getpass(pwDialog[0]+":")
|
|
|
pwdB = getpass.getpass(pwDialog[1]+":")
|
|
|
if (optStdIn and options.has_key(optStdIn)) or\
|
|
|
(optDialog and options.has_key(optDialog)):
|
|
|
if not pwdA or not (pwdA == pwdB):
|
|
|
self.printERROR (_("ERROR") + ": " +\
|
|
|
_("passwords do not match"))
|
|
|
return False
|
|
|
userPwd = pwdA
|
|
|
return userPwd
|
|
|
|
|
|
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","FTP",logOutFile,configFileDesktop]
|
|
|
|
|
|
# словарь опций сервисов из /var/calculate/remote/server.env
|
|
|
optionsInfo = {}
|
|
|
|
|
|
# объект конвертирования из старого remote env файла
|
|
|
convObj = None
|
|
|
|
|
|
|
|
|
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",False,False)
|
|
|
if resWho and type(resWho) == types.ListType:
|
|
|
for string in resWho:
|
|
|
if reFoundUser.search(string):
|
|
|
xSession +=1
|
|
|
if xSession>1:
|
|
|
foundTwoSession = True
|
|
|
self.printERROR(\
|
|
|
_("Second X session for user %s can not be opened.")\
|
|
|
%userName)
|
|
|
break
|
|
|
return foundTwoSession
|
|
|
|
|
|
def isDomain(self):
|
|
|
"""Находится ли компьютер в домене"""
|
|
|
foundMountRemote = isMount("/var/calculate/remote")
|
|
|
foundMountHome = self.isMountDomainHome()
|
|
|
remoteHost = self.clVars.Get("cl_remote_host")
|
|
|
if remoteHost and foundMountRemote and foundMountHome:
|
|
|
return True
|
|
|
self.printERROR(_("The computer is not in 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,"Disks")}
|
|
|
names.append(name)
|
|
|
# Ресурс шаблонов
|
|
|
name = "profile"
|
|
|
dictResources[name] = {"resource":"unix",
|
|
|
"path":os.path.join(home,"."+userName)}
|
|
|
names.append(name)
|
|
|
# Ресурс - домашняя директория (файлы пользователя на сервере)
|
|
|
name = "home"
|
|
|
dictResources[name] = {"resource":"homes",
|
|
|
"path":os.path.join(homeDir,"Home")}
|
|
|
names.append(name)
|
|
|
if self.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, None, True, {"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 not (textLine is None):
|
|
|
# Проверяем на монтирование директории
|
|
|
if isMount(path):
|
|
|
textLineUmount = self.umountSleepPath(path)
|
|
|
if not textLineUmount:
|
|
|
return False
|
|
|
i = 0
|
|
|
sleeps = [0.5, 2, 5]
|
|
|
while (i<len(sleeps) and not (textLine is None)):
|
|
|
# Задержка перед следующей попыткой
|
|
|
time.sleep(sleeps[i])
|
|
|
# Монтируем Samba ресурс
|
|
|
textLine = self.mountSambaRes(host,userName,userPwd,uid,
|
|
|
gid,res,path)
|
|
|
if not (textLine is None):
|
|
|
# Проверяем на монтирование директории
|
|
|
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(_("Can not 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):
|
|
|
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(_("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":
|
|
|
# исключаемые пути при синхронизации
|
|
|
# /.local/share/akonadi/db_data
|
|
|
# хранит базу acanadi
|
|
|
# /.mozilla/firefox/calculate.default/urlclassifier3.sqlite
|
|
|
# хранит БД для firefox
|
|
|
# /.local/share/mime/mime.cache
|
|
|
# отключение ошибочного кэширования изображений
|
|
|
# /.kde4/share/apps/nepomuk/repository/main/data
|
|
|
# база nepomuk
|
|
|
# /.VirtualBox
|
|
|
# содержит данные о виртуальных машинах
|
|
|
if os.path.exists(userHome) and\
|
|
|
os.path.exists(homeTemplate):
|
|
|
filterPath = " ".join(map(lambda x: '--filter="P /%s"'\
|
|
|
%x.replace('"',"").replace("'",""),
|
|
|
skipPaths))
|
|
|
execStr = '/usr/bin/rsync --delete-excluded --delete %s %s \
|
|
|
-a -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 \
|
|
|
-a -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 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(_("Can not execute rsync") + " " + str(sync) +\
|
|
|
" ...")
|
|
|
flagError = True
|
|
|
else:
|
|
|
if sync == "login":
|
|
|
if not (os.path.exists(userHome)):
|
|
|
self.printERROR(_("Directory %s not exists")%userHome)
|
|
|
else:
|
|
|
self.printERROR(_("Directory %s not exists")%homeTemplate)
|
|
|
elif sync == "logout":
|
|
|
if not (os.path.exists(userHome)):
|
|
|
self.printERROR(_("Directory %s is empty or not exists")\
|
|
|
%userHome)
|
|
|
else:
|
|
|
self.printERROR(_("Directory %s not exists")%homeTemplate)
|
|
|
flagError = True
|
|
|
if flagError:
|
|
|
return False
|
|
|
else:
|
|
|
# Домашняя директория и директория хранения профиля
|
|
|
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 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 at the server")
|
|
|
if remoteServer:
|
|
|
title += " " + remoteServer
|
|
|
title += " ..."
|
|
|
progressObj = ProgressBar(title, 0)
|
|
|
progressObj.openDialog(title, 0)
|
|
|
sleepsFindProcess = [0.1, 0.2, 0.5]
|
|
|
# Задержки при проверке размера архива
|
|
|
sleepsArch = [0.5, 1, 2]
|
|
|
lenFP = len(sleepsFindProcess)
|
|
|
lenA = len(sleepsArch)
|
|
|
i = 0
|
|
|
# Ждем появления файла архива
|
|
|
while(not (os.path.exists(archPathProcess) or\
|
|
|
os.path.exists(archPathSuccess)) and i<lenFP):
|
|
|
time.sleep(sleepsFindProcess[i])
|
|
|
i +=1
|
|
|
# Если нет архива применяем rsync синхронизацию
|
|
|
if not (os.path.exists(archPathProcess) or\
|
|
|
os.path.exists(archPathSuccess)):
|
|
|
# Архив не найден rsync схема синхронизации
|
|
|
if progress:
|
|
|
progressObj.shutdownDialog()
|
|
|
return False
|
|
|
# Ожидание создания архива
|
|
|
if os.path.exists(archPathProcess):
|
|
|
i = 0
|
|
|
while(os.path.exists(archPathProcess)):
|
|
|
if os.path.exists(archPathSuccess):
|
|
|
break
|
|
|
if i>=lenA:
|
|
|
# rsync схема синхронизации
|
|
|
if progress:
|
|
|
progressObj.shutdownDialog()
|
|
|
return False
|
|
|
startSize = os.stat(archPathProcess).st_size
|
|
|
time.sleep(sleepsArch[i])
|
|
|
if os.path.exists(archPathSuccess):
|
|
|
break
|
|
|
endSize = os.stat(archPathProcess).st_size
|
|
|
if startSize == endSize:
|
|
|
i += 1
|
|
|
if progress:
|
|
|
progressObj.shutdownDialog()
|
|
|
return True
|
|
|
|
|
|
def 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 unpackTemplate(self, homeDir, archFile, progress=False,\
|
|
|
remoteServer=False):
|
|
|
"""Распаковка архива в домашнюю директорию пользователя"""
|
|
|
# Создаем прогрессбар
|
|
|
if progress:
|
|
|
title = _("Unpacking the archive from server")
|
|
|
if remoteServer:
|
|
|
title += " " + remoteServer
|
|
|
title += " ..."
|
|
|
progressObj = ProgressBar(title)
|
|
|
archiveSize = os.stat(archFile).st_size
|
|
|
progressObj.setMaximum(archiveSize)
|
|
|
execStr = "tar -C '%s' -xzf -" %homeDir
|
|
|
# Выполняем разархивацию
|
|
|
pipe = subprocess.Popen(execStr,
|
|
|
stdin=subprocess.PIPE,
|
|
|
stdout=subprocess.PIPE,
|
|
|
stderr=subprocess.PIPE,
|
|
|
close_fds=True,
|
|
|
env=os.environ, shell=True)
|
|
|
# Чтение файла в поток
|
|
|
if progress:
|
|
|
self.fileReader(archFile, pipe.stdin, progressObj)
|
|
|
else:
|
|
|
self.fileReader(archFile, pipe.stdin)
|
|
|
ret = pipe.wait()
|
|
|
pipe.stdout.close()
|
|
|
pipe.stderr.close()
|
|
|
if progress:
|
|
|
progressObj.shutdownDialog()
|
|
|
if ret:
|
|
|
self.printERROR(_("Can not execute %s") %execStr)
|
|
|
self.printERROR(_("Can not 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 not exists") %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 mountUserResAndSync(self, userName, sync=True, progress=False):
|
|
|
"""Монтирование пользовательских ресурсов и синхронизация настроек"""
|
|
|
# Проверяем на root
|
|
|
if not self.isRoot():
|
|
|
return False
|
|
|
# Проверка на повторный вход пользователя
|
|
|
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(_("To be used local profile"))
|
|
|
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['uid'])
|
|
|
homeDir = userLdapInfo['home']
|
|
|
else:
|
|
|
self.printERROR(_("Can not found user %s in LDAP")%userName)
|
|
|
self.umountUserRes()
|
|
|
return False
|
|
|
rootPath = self.clVars.Get('cl_root_path')
|
|
|
# Реальный путь к домашней директории
|
|
|
homeDir = os.path.join(rootPath, homeDir[1:])
|
|
|
# Файл хранения настроек пакета
|
|
|
configFileName = os.path.join(homeDir, self.configFileDesktop)
|
|
|
# При отсуствии создаем домашнюю директорию
|
|
|
if not os.path.exists(homeDir):
|
|
|
os.makedirs(homeDir)
|
|
|
os.chown(homeDir,uid,gid)
|
|
|
os.chmod(homeDir,0700)
|
|
|
# записываем в конфигурационный файл статус "в процессе"
|
|
|
self.setVarToConfig("main", {"status_sync":"process"},
|
|
|
configFileName, uid, gid)
|
|
|
# Получаем пароль пользователя из ключей ядра
|
|
|
userPwd = getKey(userName)
|
|
|
if not userPwd:
|
|
|
self.printERROR(_("Not found user password"))
|
|
|
# Отмонтируем пользовательские ресурсы в случае ошибки
|
|
|
self.umountUserRes(homeDir)
|
|
|
return False
|
|
|
# Флаг ошибки
|
|
|
flagError = False
|
|
|
# Имя удаленного сервера
|
|
|
remoteServer = ""
|
|
|
# В случае включения репликации на сервере True
|
|
|
replOn = self.ldapDataObj.isRepl()
|
|
|
if replOn:
|
|
|
osLinuxShort = self.clVars.Get("os_linux_shortname")
|
|
|
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
|
|
|
osLinuxShort = self.clVars.Get("os_linux_shortname")
|
|
|
|
|
|
# Если профиль на удаленном сервере
|
|
|
if remoteServer and replOn:
|
|
|
# имя файла архива в процессе архивации
|
|
|
archPathProcess = ""
|
|
|
# имя файла архива - после архивации
|
|
|
archPathSuccess = ""
|
|
|
# Создаем инкрементальный архив в случае репликации
|
|
|
prevHost = ""
|
|
|
# Дата профиля на текущем сервере
|
|
|
dateDefaultTemplate = ""
|
|
|
# Текущее время
|
|
|
currTime = str(float(time.time()))
|
|
|
# Создаваемые пути при монтировании
|
|
|
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 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 not (textLine is None):
|
|
|
if name == "remote_profile":
|
|
|
flagErrorMountRemote = True
|
|
|
break
|
|
|
else:
|
|
|
self.printERROR(_("Can not mount Samba resource [%s]")\
|
|
|
%res + " ...")
|
|
|
flagError = True
|
|
|
break
|
|
|
# Если нет синхронизации отменяем обработку
|
|
|
if 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 == "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 not (textLine is None):
|
|
|
self.printERROR(_("Can not mount Samba resource [%s]")\
|
|
|
%res + " ...")
|
|
|
flagError = True
|
|
|
break
|
|
|
extSuccess = "gz"
|
|
|
extProcess = "process"
|
|
|
strCurrentTime = currTime.replace(".","_")
|
|
|
archPathTmp = os.path.join(path, "profile.%s.%s.tar"\
|
|
|
%(osLinuxShort, strCurrentTime))
|
|
|
# имя файла архива в процессе архивации
|
|
|
archPathSuccess = "%s.%s"%(archPathTmp,extSuccess)
|
|
|
# имя файла архива - после архивации
|
|
|
archPathProcess = "%s.%s"%(archPathTmp,extProcess)
|
|
|
# Ищем файл архива
|
|
|
if not self.foundArchFile(strCurrentTime, archPathProcess,
|
|
|
archPathSuccess, progress,
|
|
|
remoteServer):
|
|
|
flagIncrArch = False
|
|
|
break
|
|
|
# Если нет архива применяем rsync синхронизацию
|
|
|
if not os.path.exists(archPathSuccess):
|
|
|
flagIncrArch = False
|
|
|
break
|
|
|
else:
|
|
|
# Распаковываем архив в домашнюю директорию
|
|
|
if not self.unpackTemplate(homeDir, archPathSuccess,
|
|
|
progress, remoteServer):
|
|
|
flagError = True
|
|
|
if archPathSuccess:
|
|
|
# Удаление архивного файла
|
|
|
if os.path.exists(archPathSuccess):
|
|
|
# Удаляем файл архива
|
|
|
os.remove(archPathSuccess)
|
|
|
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:
|
|
|
# Удаление архивных файлов
|
|
|
rmFiles = [archPathProcess, archPathSuccess]
|
|
|
for rmFile in rmFiles:
|
|
|
if os.path.exists(rmFile):
|
|
|
# Удаляем файл архива
|
|
|
os.remove(archPathSuccess)
|
|
|
# Если не удалось замонтировать удаленный профиль
|
|
|
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 sync and "remote_profile" in dictRes:
|
|
|
umountPath = dictRes["remote_profile"]["path"]
|
|
|
textLine = self.umountSleepPath(umountPath)
|
|
|
if not textLine:
|
|
|
# Отмонтируем пользовательские ресурсы в случае ошибки
|
|
|
self.umountUserRes(homeDir)
|
|
|
return False
|
|
|
# Удаляем директорию удаленного профиля у клиента
|
|
|
if os.path.exists(umountPath):
|
|
|
os.rmdir(umountPath)
|
|
|
|
|
|
# Если профиль на текущем сервере
|
|
|
elif not remoteServer and replOn or not replOn:
|
|
|
# Создаваемые пути при монтировании
|
|
|
for name in names:
|
|
|
path = dictRes[name]["path"]
|
|
|
res = dictRes[name]["resource"]
|
|
|
# Создаем пользовательскую директории для профиля
|
|
|
if not os.path.exists(path):
|
|
|
try:
|
|
|
os.mkdir(path)
|
|
|
os.chown(path, uid, gid)
|
|
|
os.chmod(path,0700)
|
|
|
except OSError:
|
|
|
self.printERROR(_("Error creating directory"))
|
|
|
self.printERROR(_("Permission denied: '%s'")%path)
|
|
|
flagError = True
|
|
|
break
|
|
|
# Проверяем на монтирование директории
|
|
|
if isMount(path):
|
|
|
continue
|
|
|
# Имя хоста для монтирования ресурса
|
|
|
hostConnect = defaultHost
|
|
|
textLine = self.mountSleepRes(hostConnect, userName, userPwd,
|
|
|
uid, gid, res, path)
|
|
|
if not (textLine is None):
|
|
|
self.printERROR(_("Can not 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)
|
|
|
# Синхронизация профиля пользователя с текущего сервера
|
|
|
hostConnect = defaultHost
|
|
|
if not self.syncLoginTemplate(hostConnect, userName, homeDir,
|
|
|
homeTemplate, uid, gid,
|
|
|
progress):
|
|
|
# Отмонтируем пользовательские ресурсы в случае ошибки
|
|
|
self.umountUserRes(homeDir)
|
|
|
return False
|
|
|
|
|
|
if flagError:
|
|
|
# Отмонтируем пользовательские ресурсы в случае ошибки
|
|
|
self.umountUserRes(homeDir)
|
|
|
return False
|
|
|
|
|
|
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 in sync 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",False,False)
|
|
|
if resWho and type(resWho) == types.ListType:
|
|
|
for string in resWho:
|
|
|
if reFoundUser.search(string):
|
|
|
xSession = True
|
|
|
break
|
|
|
return xSession
|
|
|
|
|
|
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','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 not (textLine is None):
|
|
|
self.printERROR(_("Can not exec") + " " + str(execStr) +\
|
|
|
" ...")
|
|
|
return False
|
|
|
execStr = "rm -rf '%s'" %fd
|
|
|
textLine = self.execProg(execStr)
|
|
|
if not (textLine is None):
|
|
|
self.printERROR(_("Can not exec") + " " + str(execStr) +\
|
|
|
" ...")
|
|
|
return False
|
|
|
os.chdir(pathProg)
|
|
|
return True
|
|
|
|
|
|
def removeNoiseFiles(self, userHome):
|
|
|
"""Удаление файлов, создающих помехи работе dm"""
|
|
|
noiseFiles = ['.dmrc']
|
|
|
for nsFile in noiseFiles:
|
|
|
rmFile = os.path.join(userHome, nsFile)
|
|
|
if os.path.exists(rmFile):
|
|
|
os.remove(rmFile)
|
|
|
return True
|
|
|
|
|
|
def removePrivateFiles(self, userHome):
|
|
|
"""Удаление приватных файлов"""
|
|
|
privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl',
|
|
|
self.configFileServer]
|
|
|
# файлы в .ssh
|
|
|
sshHome = ".ssh"
|
|
|
sshPath = os.path.join(userHome,sshHome)
|
|
|
if os.path.isdir(sshPath):
|
|
|
# .ssh файлы относительно домашней директории пользователя
|
|
|
privateFiles += map(lambda x:os.path.join(sshHome,x),\
|
|
|
filter(lambda x:\
|
|
|
os.path.isfile(os.path.join(sshPath,x)),\
|
|
|
os.listdir(sshPath)))
|
|
|
for prFile in privateFiles:
|
|
|
rmFile = os.path.join(userHome, prFile)
|
|
|
if os.path.exists(rmFile):
|
|
|
os.remove(rmFile)
|
|
|
return True
|
|
|
|
|
|
def clearUserKey(self, userName):
|
|
|
"""Очищает пользовательский ключ ядра"""
|
|
|
# Ищем ключ в ядре
|
|
|
if getKey(userName):
|
|
|
# Если нашли очищаем
|
|
|
ret = clearKey(userName)
|
|
|
if ret == 0:
|
|
|
return True
|
|
|
else:
|
|
|
self.printERROR(_("Can not clear kernel key from user %s")\
|
|
|
%userName)
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def umountUserResAndSync(self, userName, progress=False, sync=True):
|
|
|
"""Отмонтирование пользовательских ресурсов и синхронизация настроек"""
|
|
|
# Проверяем на root
|
|
|
if not self.isRoot():
|
|
|
return False
|
|
|
domain = self.clVars.Get("cl_remote_host")
|
|
|
hostAuth = self.clVars.Get("os_remote_auth")
|
|
|
# В случае компьютера вне домена
|
|
|
if not hostAuth or not domain:
|
|
|
self.printSUCCESS(_("To be used local profile"))
|
|
|
return True
|
|
|
connectDomain = self.isDomain()
|
|
|
if not connectDomain:
|
|
|
# Отмонтируем пользовательские ресурсы в случае ошибки
|
|
|
self.umountUserRes()
|
|
|
return False
|
|
|
# Если пользователь в X сессии тогда не будем отмонтировать ресурсы
|
|
|
if self.isSessionUser(userName):
|
|
|
self.printERROR(_("User %s is in X session")%userName)
|
|
|
self.printERROR(_("Can not unmount user %s resource")%userName)
|
|
|
return False
|
|
|
# Информация о пользователе из LDAP
|
|
|
userLdapInfo = self.ldapDataObj.getUserLdapInfo(userName)
|
|
|
if userLdapInfo:
|
|
|
uid = int(userLdapInfo['uid'])
|
|
|
gid = int(userLdapInfo['gid'])
|
|
|
homeDir = userLdapInfo['home']
|
|
|
else:
|
|
|
self.printERROR(_("Can not found user %s in LDAP")%userName)
|
|
|
self.umountUserRes()
|
|
|
return False
|
|
|
# Файл хранения настроек пакета
|
|
|
configFileName = os.path.join(homeDir, self.configFileDesktop)
|
|
|
if os.path.exists(homeDir):
|
|
|
self.moveHomeDir(homeDir)
|
|
|
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
|
|
|
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(_("Can not remove dir %s")% path)
|
|
|
return False
|
|
|
if needSync:
|
|
|
self.printSUCCESS(_("Saved a user profile in the domain"))
|
|
|
self.printOK(_("Umounted user resource in domain") + " ...")
|
|
|
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, retFull=True, envProg={}):
|
|
|
"""Выполняет внешнюю программу
|
|
|
|
|
|
Параметры:
|
|
|
cmdStrProg внешняя программа
|
|
|
inStr данные передаваемые программе на страндартный вход.
|
|
|
Возвращаемые параметры:
|
|
|
строка которую выведет внешняя программа или False в случае ошибки
|
|
|
"""
|
|
|
env_path = {"PATH":getpathenv()}
|
|
|
env = {}
|
|
|
env.update(os.environ.items() + env_path.items() + envProg.items())
|
|
|
retCode,programOut = runOsCommand(cmdStrProg,inStr,retFull,env)
|
|
|
if not retCode:
|
|
|
return programOut
|
|
|
return False
|
|
|
|
|
|
def umountSleepPath(self, path):
|
|
|
"""Отмонтирует путь при неудаче задержка потом повтор"""
|
|
|
# Задержки при отмонтированиии директории
|
|
|
sleeps = [0.5, 2, 5]
|
|
|
# Проверяем на монтирование директорию
|
|
|
if isMount(path):
|
|
|
textLine = self.execProg("umount %s"%path)
|
|
|
if textLine is False:
|
|
|
i = 0
|
|
|
flagError = False
|
|
|
while (i<len(sleeps) and textLine is False):
|
|
|
# Задержка перед следующей попыткой
|
|
|
time.sleep(sleeps[i])
|
|
|
# Отмонтируем Samba ресурс
|
|
|
if isMount(path):
|
|
|
textLine = self.execProg("umount %s"%path)
|
|
|
else:
|
|
|
textLine = None
|
|
|
break
|
|
|
i += 1
|
|
|
if textLine != None:
|
|
|
self.printERROR(_("Can not unmount path %s")%path + " ...")
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def umountUserRes(self, homeDir=False):
|
|
|
"""Отмонтируем пользовательские директории если они есть"""
|
|
|
umountPaths = self.getMountUserPaths(homeDir)
|
|
|
ret = True
|
|
|
for umountPath in umountPaths:
|
|
|
if not self.umountSleepPath(umountPath):
|
|
|
ret = False
|
|
|
break
|
|
|
return ret
|
|
|
|
|
|
def installClient(self):
|
|
|
"""Наложение шаблонов на систему при инсталяции"""
|
|
|
domain = self.clVars.Get("cl_remote_host")
|
|
|
if domain:
|
|
|
if not self.addDaemonAutostart("client"):
|
|
|
return False
|
|
|
if not self.mountRemote():
|
|
|
return False
|
|
|
# Записываем действие, введен в домен
|
|
|
self.clVars.Write("cl_pass_action", "domain", True)
|
|
|
self.printOK(_("To be used domain profile") + " ...")
|
|
|
else:
|
|
|
self.printOK(_("To be used local profile") + " ...")
|
|
|
return True
|
|
|
|
|
|
def getDefaultRunlevelDaemons(self):
|
|
|
"""Получаем всех демонов в default уровне"""
|
|
|
execStr = "rc-update show"
|
|
|
textLine = self.execProg(execStr, None, False)
|
|
|
if textLine == 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 uninstallClient(self):
|
|
|
"""Наложение шаблонов на систему при деинсталяции"""
|
|
|
# Проверяем на root
|
|
|
if not self.isRoot():
|
|
|
return False
|
|
|
# Действие выход из домена
|
|
|
self.clVars.Set("cl_pass_action", "undomain", True)
|
|
|
# Удаляем переменные из env файлов
|
|
|
self.removeVars()
|
|
|
if not self.applyTemplatesFromSystem():
|
|
|
self.printERROR(_("Can not apply uninstall templates"))
|
|
|
return False
|
|
|
if not self.delDaemonAutostart("client"):
|
|
|
return False
|
|
|
# Записываем действие, выведен из домена
|
|
|
self.clVars.Write("cl_pass_action", "undomain", True)
|
|
|
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:
|
|
|
return True
|
|
|
else:
|
|
|
self.printERROR(_("ERROR") + ": " + execStr)
|
|
|
self.printERROR(_("Can not delete from default runlevel"))
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def delDomain(self):
|
|
|
"""выводим из домена"""
|
|
|
# Проверяем на root
|
|
|
if not self.isRoot():
|
|
|
return False
|
|
|
pathRemote = "/var/calculate/remote"
|
|
|
pathHome = "/home"
|
|
|
foundMountRemote = isMount(pathRemote)
|
|
|
foundMountHome = isMount(pathHome)
|
|
|
domain = self.clVars.Get("cl_remote_host")
|
|
|
if not domain:
|
|
|
self.printWARNING(_("The computer is not in domain"))
|
|
|
return True
|
|
|
if foundMountRemote:
|
|
|
textLineUmount = self.umountSleepPath(pathRemote)
|
|
|
if not textLineUmount:
|
|
|
return False
|
|
|
if foundMountHome:
|
|
|
textLineUmount = self.umountSleepPath(pathHome)
|
|
|
if not textLineUmount:
|
|
|
return False
|
|
|
# Удаляем переменные из env файлов
|
|
|
self.removeVars()
|
|
|
# Устанавливаем действие выход из домена
|
|
|
self.clVars.Set("cl_pass_action", "undomain", True)
|
|
|
if not self.applyTemplatesFromSystem():
|
|
|
self.printERROR(_("Can not apply undomain templates"))
|
|
|
return False
|
|
|
# Рестартуем dbus
|
|
|
self.restartDBus()
|
|
|
self.printSUCCESS(_("Apply undomain templates"))
|
|
|
if not self.delDaemonAutostart("client"):
|
|
|
return False
|
|
|
# Записываем действие, введен в домен
|
|
|
self.clVars.Write("cl_pass_action", "undomain", True)
|
|
|
self.printOK(_("Computer removed from domain %s")%domain + " ...")
|
|
|
return True
|
|
|
|
|
|
def getUserPassword(self, pwDialog=False):
|
|
|
"""Получить пароль у пользователя
|
|
|
|
|
|
pwDialog - приглашение ввода пароля
|
|
|
"""
|
|
|
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:
|
|
|
return True
|
|
|
else:
|
|
|
self.printERROR(_("ERROR") + ": " + execStr)
|
|
|
self.printERROR(_("Can not add at default runlevel"))
|
|
|
return False
|
|
|
|
|
|
def restartDBus(self):
|
|
|
"""Перезапускаем службу D-Bus"""
|
|
|
dbusDaemon = '/etc/init.d/dbus'
|
|
|
if os.path.exists(dbusDaemon):
|
|
|
if os.system(dbusDaemon + ' restart &>/dev/null') != 0:
|
|
|
self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...")
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def 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, False, False)
|
|
|
if not resPing:
|
|
|
self.printERROR(_('Can not execute "%s"')%execStr)
|
|
|
return False
|
|
|
foudHost = False
|
|
|
foudHostSamba = False
|
|
|
foundMountRemote = False
|
|
|
reFoundHost = re.compile("(\d+)\% packet loss")
|
|
|
if resPing and type(resPing) == types.ListType and len(resPing)>=2:
|
|
|
pingStr = resPing[-2].strip()
|
|
|
reSearch = reFoundHost.search(pingStr)
|
|
|
if reSearch and reSearch.group(1) == "0":
|
|
|
foudHost = True
|
|
|
if not foudHost:
|
|
|
self.printERROR(_("Not found domain %s")%domain)
|
|
|
return False
|
|
|
reFoundHostSamba = re.compile("Server=\[Samba.+\]")
|
|
|
resSmbClient = self.execProg("smbclient -N -L %s" %domain,
|
|
|
False,False)
|
|
|
if resSmbClient and type(resSmbClient) == types.ListType:
|
|
|
for string in resSmbClient:
|
|
|
if reFoundHostSamba.search(string):
|
|
|
foudHostSamba = True
|
|
|
break
|
|
|
if not foudHostSamba:
|
|
|
self.printERROR(_("Not found Samba server in %s")%domain)
|
|
|
return False
|
|
|
pwd = False
|
|
|
if self.clVars.Get("cl_remote_host") and \
|
|
|
self.clVars.Get("cl_remote_host") != domain:
|
|
|
if not self.delDomain():
|
|
|
return False
|
|
|
elif self.clVars.Get("cl_remote_host") and \
|
|
|
self.clVars.Get("cl_remote_host") == domain:
|
|
|
pwd = self.clVars.Get("cl_remote_pw")
|
|
|
foundMountRemote = isMount("/var/calculate/remote")
|
|
|
foundMountHome = self.isMountDomainHome()
|
|
|
if foundMountRemote:
|
|
|
self.printWARNING(_("Samba resource [%s] is mount")%\
|
|
|
"remote" + " ...")
|
|
|
else:
|
|
|
if pwd:
|
|
|
userPwd = pwd
|
|
|
else:
|
|
|
userPwd=self.getUserPassword(\
|
|
|
_("Domain password for the desktop"))
|
|
|
pathRemote = "/var/calculate/remote"
|
|
|
pwdRemote = userPwd
|
|
|
if not os.path.exists(pathRemote):
|
|
|
os.makedirs(pathRemote)
|
|
|
mountStr = "mount -t cifs -o user=client //%s/remote %s"\
|
|
|
%(domain,pathRemote)
|
|
|
textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote})
|
|
|
if not (textLine is None):
|
|
|
self.printERROR(_("Can not mount Samba resource [%s]")%\
|
|
|
"remote" + " ...")
|
|
|
return False
|
|
|
else:
|
|
|
self.printSUCCESS(_("Mount Samba resource [%s]")%"remote" + \
|
|
|
" ...")
|
|
|
self.clVars.Write("cl_remote_host", domain, True, "local")
|
|
|
self.clVars.Write("cl_remote_pw", userPwd, True, "local")
|
|
|
pathHome = "/var/calculate/client-home"
|
|
|
if foundMountHome:
|
|
|
self.printWARNING(str(pathHome)+ " " +_("is mount")+
|
|
|
" ...")
|
|
|
else:
|
|
|
if not os.path.exists(pathHome):
|
|
|
os.makedirs(pathHome)
|
|
|
mountStr = "mount -o bind %s /home" %pathHome
|
|
|
textLine = self.execProg(mountStr)
|
|
|
if not (textLine is None):
|
|
|
self.printERROR(_("Can not mount") + " " + str(pathHome) +\
|
|
|
" ...")
|
|
|
return False
|
|
|
self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\
|
|
|
" ...")
|
|
|
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(_("Not found server info") + ": " +\
|
|
|
_("services DN or unix DN or bind DN or bind password"))
|
|
|
return False
|
|
|
# Наложим шаблоны - domain
|
|
|
# Действие - ввод в домен
|
|
|
self.clVars.Set("cl_pass_action", "domain", True)
|
|
|
# Доменная авторизация
|
|
|
self.clVars.Set("os_remote_auth", domain)
|
|
|
if not self.applyTemplatesFromSystem():
|
|
|
self.printERROR(_("Can not apply install 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.clVars.Write("cl_pass_action", "domain", 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_pass_action","domain",True)
|
|
|
else:
|
|
|
# Устанавливаем действие выход из домена
|
|
|
self.clVars.Set("cl_pass_action","undomain",True)
|
|
|
self.clVars.Set("os_remote_auth", hostAuth)
|
|
|
# Наложим шаблоны
|
|
|
dirsAndFiles = self.applyTemplatesFromSystem()
|
|
|
if not dirsAndFiles:
|
|
|
if hostAuth:
|
|
|
self.printERROR(_("Can not apply domain templates"))
|
|
|
else:
|
|
|
self.printERROR(_("Can not apply undomain templates"))
|
|
|
return False
|
|
|
if hostAuth:
|
|
|
self.printOK(_("Set templates of 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(_("Set templates of local mode"))
|
|
|
self.clVars.Delete("os_remote_auth")
|
|
|
self.clVars.Delete("os_remote_client")
|
|
|
return True
|
|
|
|
|
|
def isMountDomainHome(self):
|
|
|
"""Примонтирована ли директория /home для ввода в домен"""
|
|
|
return isMount("/home") == "/var/calculate/client-home"
|
|
|
|
|
|
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")
|
|
|
foundMountHome = self.isMountDomainHome()
|
|
|
else:
|
|
|
self.printWARNING(_("This computer is not in domain"))
|
|
|
# Если шаблоны не актуальны накладываем новую версию шаблонов
|
|
|
if not self.applyRelevanceTemplates():
|
|
|
return False
|
|
|
return True
|
|
|
pathHome = "/var/calculate/client-home"
|
|
|
if foundMountRemote:
|
|
|
self.printWARNING(_("Samba resource [%s] is mount")%"remote" + \
|
|
|
" ...")
|
|
|
if foundMountHome:
|
|
|
self.printWARNING(str(pathHome) + " " +_("is mount")+
|
|
|
" ...")
|
|
|
if foundMountHome and foundMountRemote:
|
|
|
# Накладываем сетевые шаблоны
|
|
|
if domain:
|
|
|
self.clVars.flIniFile()
|
|
|
if not self.applyRelevanceTemplates(domain):
|
|
|
return False
|
|
|
return True
|
|
|
flagLocalTemplate = False
|
|
|
if not foundMountRemote:
|
|
|
pathRemote = "/var/calculate/remote"
|
|
|
pwdRemote = self.clVars.Get("cl_remote_pw")
|
|
|
if not (domain and pwdRemote):
|
|
|
self.printERROR(_("Not found variable")+\
|
|
|
": cl_remote_pw ...")
|
|
|
return False
|
|
|
if not os.path.exists(pathRemote):
|
|
|
os.makedirs(pathRemote)
|
|
|
mountStr = "mount -t cifs -o user=client //%s/remote %s"\
|
|
|
%(domain,pathRemote)
|
|
|
textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote})
|
|
|
if not (textLine is None):
|
|
|
self.printWARNING(_("Can not mount Samba resource [%s]")%\
|
|
|
"remote" + " ...")
|
|
|
flagLocalTemplate = True
|
|
|
beforeRemoteAuth = self.clVars.Get('os_remote_auth')
|
|
|
# Если шаблоны не актуальны накладываем новую версию шаблонов
|
|
|
if not self.applyRelevanceTemplates():
|
|
|
return False
|
|
|
if foundMountHome:
|
|
|
umountStr = "umount /home"
|
|
|
textLine = self.execProg(umountStr)
|
|
|
if not (textLine is None):
|
|
|
self.printERROR(_("Can not unmount") + " /home")
|
|
|
return False
|
|
|
if beforeRemoteAuth != self.clVars.Get('os_remote_auth'):
|
|
|
self.restartDBus()
|
|
|
return True
|
|
|
self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\
|
|
|
" ...")
|
|
|
if not foundMountHome and not flagLocalTemplate:
|
|
|
if not os.path.exists(pathHome):
|
|
|
os.makedirs(pathHome)
|
|
|
mountStr = "mount -o bind %s /home" %pathHome
|
|
|
textLine = self.execProg(mountStr)
|
|
|
if not (textLine is None):
|
|
|
self.printERROR(_("Can not mount") + " " + str(pathHome) +\
|
|
|
" ...")
|
|
|
return False
|
|
|
self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\
|
|
|
" ...")
|
|
|
# Накладываем сетевые шаблоны
|
|
|
if domain:
|
|
|
self.clVars.flIniFile()
|
|
|
beforeRemoteAuth = self.clVars.Get('os_remote_auth')
|
|
|
if not self.applyRelevanceTemplates(domain):
|
|
|
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(_("Update env files") + " ...")
|
|
|
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 from 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 domain"))
|
|
|
self.printWARNING(_("Use passwd"))
|
|
|
return False
|
|
|
count = 2
|
|
|
# Получаем старый пароль пользователя
|
|
|
curPassword = self.getUserPassword(_("Enter current password"))
|
|
|
if not curPassword:
|
|
|
self.printERROR(_("Current password is empty"))
|
|
|
for i in range(count):
|
|
|
count -= 1
|
|
|
# Получаем старый пароль пользователя
|
|
|
curPassword = self.getUserPassword(_("Enter current password"))
|
|
|
if curPassword:
|
|
|
break
|
|
|
self.printERROR(_("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 current password"))
|
|
|
if not curPassword:
|
|
|
self.printERROR(_("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(_("Changed password of user %s")%os.environ["USER"] + \
|
|
|
" ...")
|
|
|
self.printWARNING(_("Password will be changed when you logout from the \
|
|
|
X session"))
|
|
|
return True
|
|
|
|