You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2575 lines
115 KiB

#-*- coding: utf-8 -*-
# Copyright 2008-2010 Mir Calculate Ltd. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import re
import sys
import cl_base
import cl_profile
import cl_utils2
import cl_utils
import ldap
import types
import getpass
import _cl_keys
import time
import stat
import subprocess
import time
from encrypt import encrypt
Version = "calculate-client 2.1.11"
tr = cl_base.lang()
tr.setLanguage(sys.modules[__name__])
pcs = cl_utils.prettyColumnStr
class printNoColor:
def colorPrint(self,attr,fg,bg,string):
sys.stdout.write(string)
class ProgressBar:
suffixSet = 'org.freedesktop.DBus.Properties.Set \
org.kde.kdialog.ProgressDialog'
execenv = {"HOME":"/root"}
max = 100
kdialog = None
label = None
def __init__(self,title,kdialog=None):
self.title = title
if kdialog == None:
self.openDialog(self.title)
def openDialog(self,title,max=None):
if max != None:
self.max = max
self.title = title
if os.system('which kdialog >/dev/null') == 0:
self.label ="LOGINKDIALOG=%d" % os.getpid()
env = {}
env.update(os.environ.items() + self.execenv.items() +\
[tuple(self.label.split("="))])
pipe = subprocess.Popen('/usr/bin/kdialog --progressbar "%s" %d'\
%(" "*(len(title)+20), self.max),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
env=env, shell=True)
pipe.stdin.close()
if pipe.poll() == None:
# ожидание в 5 сек
for t in range(500):
time.sleep(0.01)
if pipe.poll() != None:
break
if pipe.poll() == 0:
self.kdialog = pipe.stdout.readline().strip()
while not "org.kde.kdialog" in self.kdialog:
s = pipe.stdout.readline()
if s == "":
pipe.stdout.close()
pipe.stderr.close()
self.shutdownDialog()
break
self.kdialog = s.strip()
self.setTitle(self.title)
pipe.stdout.close()
pipe.stderr.close()
else:
pipe.stdout.close()
pipe.stderr.close()
self.shutdownDialog()
def shutdownDialog(self):
'''Принудительно уничтожить процесс kdialog'''
self.kdialog = None
pipe = subprocess.Popen("/bin/ps axeo pid,cmd",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, shell=True)
if self.label != None:
for s in pipe.stdout.readlines():
if self.label in s:
try:
os.kill( int(s.split()[0]), 9 )
except (OSError,ValueError):
pass
self.label = None
def setValue(self,value):
'''Установить текущее значения для прогресса'''
if self.kdialog and value <= self.max:
env = ""
if self.execenv:
env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\
self.execenv)) + " "
os.system(env + '/usr/bin/qdbus %s %s value %d >/dev/null'\
%(self.kdialog,self.suffixSet, value))
def setMaximum(self,max):
'''Установить максимальное значения для прогресса'''
self.max = max
if self.kdialog:
env = ""
if self.execenv:
env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\
self.execenv)) + " "
os.system(env + '/usr/bin/qdbus %s %s maximum %d >/dev/null'\
%(self.kdialog,self.suffixSet, self.max))
def setTitle(self,title):
'''Установить описания прогресса'''
self.title = title
if self.kdialog:
env = ""
if self.execenv:
env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\
self.execenv)) + " "
os.system(env + '/usr/bin/qdbus %s setLabelText "%s" >/dev/null'\
%(self.kdialog,self.title))
def close(self):
'''Закрыть прогресс'''
self.shutdownDialog()
class ProgressProfile(cl_profile.profile):
def __init__(self, vars):
cl_profile.profile.__init__(self,vars)
self.progress = ProgressBar(_("Setting up user profile") + " ...")
def numberAllProfiles(self, number):
self.progress.setMaximum(number)
return True
def numberProcessProfiles(self,number):
self.progress.setValue(number)
return True
class RsyncProgressBar(ProgressBar):
'''Объект запуска rsync для получения количества созданных файлов и
при необходимости вывода progressbar
'''
# получение номера передаваемого файла из инф потока rsync
senderre = re.compile("\[sender\] i=(\d+) ", re.S)
# получение номера получаемого файла из потока rsync
receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S)
pipe = None
maximum = 1
copyStarting = False
def __init__(self, title, secondtitle, rsyncstr, maximum=1):
self.title = title
self.secondtitle = secondtitle
self.maximum = maximum
self.rsyncstr = rsyncstr
def getFilesNum(self):
'''Получить количество файлов созданных генератором'''
if self.pipe:
return self.value
def getExitCode(self):
'''Получить код выхода rsync'''
if self.pipe:
return os.WEXITSTATUS(self.pipe.wait())
def runsilent(self):
'''Запустить rsync без progressbar'''
self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, shell=True)
while True:
s = self.pipe.stdout.readline()
if len(s) == 0:
break
q = self.receiverre.search(s)
if q:
self.value = int(q.groups()[0])
def run(self):
'''Запустить rsync с progressbar'''
self.openDialog(self.title,0)
self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, shell=True)
oldpercent = 0
while True:
s = self.pipe.stdout.readline()
if len(s) == 0:
break
q = self.senderre.search(s)
if q:
maximum = int(q.groups()[0])
if self.maximum < maximum:
self.maximum = maximum
continue
q = self.receiverre.search(s)
if q:
if not self.copyStarting:
self.shutdownDialog()
self.max = 100
self.openDialog(self.secondtitle)
self.copyStarting = True
self.value = int(q.groups()[0])
newpercent = self.value * 100 / self.maximum
if oldpercent < newpercent:
oldpercent = newpercent
self.setValue(oldpercent)
# Импортированные классы в cl_ldap
# Запись ошибок
imp_cl_err = cl_profile._error
# Работа с XML
imp_cl_xml = cl_profile.xmlShare
# Обработка параметров командной строки
imp_cl_help = cl_utils2.cl_help
# Форматированный вывод
imp_cl_smcon = cl_utils2.cl_smartcon
class cl_client(imp_cl_err, imp_cl_xml, imp_cl_help, imp_cl_smcon, encrypt):
"""Основной класс для работы клиентских приложений"""
# Пути к профилям объединяемых с системными
# относительный путь при объединении '/'
rootProfilePaths=['/usr/lib/calculate/calculate-client/profiles',
'/var/calculate/remote/profiles',
'/var/calculate/profiles']
def __init__(self, cmdName):
# объект для форматированного вывода
imp_cl_help.__init__(self, cmdName)
servName = ""
if "user" in cmdName:
servName = _("user")
self.chapter = [\
# расположение разделов на странице
# имя раздела, видимый или невидимый, кол. "\n" после
# названия раздела, кол. "\n" после раздела
# тип раздела
("Copyright",False,0,2,""),
(_("Usage"),True,0,1,""),
("Function",False,0,2,""),
(_("Examples"),True,1,1,""),
(_("Common options"),True,1,0,"options"),
]
# имена используемых программ и их номера для доступа к переменным
# self.data
self.progName = { 'cl-client':0,
'cl-createhome':1,
'cl-sync':2,
'cl-passwd':3
}
# Cвязь длинных опций помощи и выводимых разделов помощи с опциями
self.relOptions = {"h":[_("Common options")],}
# список разделов, которые на наличие в ней информации
# используется для автоматического отображения/скрытия
# опций help-имя
# Пример: self.relOption =
# { "help-all":[_("Common options"], _("Unix service options"),
# _("Samba service options"), _("LDAP service options")}]
# self.relChapterPass = (_("Common options"),)
# это означается что опция будет активна, если только в разделах
# кроме Common options есть хоть одна доступная опция.
self.relChapterPass = (_("Common options"),)
self.data = [\
{
#Copyright
#'progAccess':(3,),
'helpChapter':"Copyright",
'help':Version
},
#Usage
{
'progAccess':(0,),
'helpChapter':_("Usage"),
'help': cmdName + " [" + _("options") + "] " + _("domain")
},
{
'progAccess':(1,),
'helpChapter':_("Usage"),
'help': cmdName + " " + _("user")
},
{
'progAccess':(2,),
'helpChapter':_("Usage"),
'help': cmdName + " [" + _("options") + "] " + _("user")
},
{
'progAccess':(3,),
'helpChapter':_("Usage"),
'help': cmdName
},
# Function
{
'progAccess':(0,),
'helpChapter':"Function",
'help':_("Changes settings for connecting to domain \
(calculate-server)")
},
{
'progAccess':(1,),
'helpChapter':"Function",
'help':_("Create home directory for the new user account")
},
{
'progAccess':(2,),
'helpChapter':"Function",
'help':_("Mounting resources and synchronize the user preferences")
},
{
'progAccess':(3,),
'helpChapter':"Function",
'help':_("Change user password")
},
# Examples
{
'progAccess':(0,),
'helpChapter':_("Examples"),
'help':pcs( " cl-client 192.168.0.1", self.column_width,
"# " + _("Adds settings for connecting to domain") + \
" (ip 192.168.0.1)",
self.consolewidth-self.column_width )
},
{
'progAccess':(3,),
'helpChapter':_("Examples"),
'help':pcs(" cl-passwd", self.column_width,
"# "+_("change password of user for Samba and Unix services")+".",
self.consolewidth-self.column_width)
},
# Options
{'shortOption':"h",
'longOption':"help",
'helpChapter':_("Common options"),
'help':_("display this help and exit")
},
{'progAccess':(0,),
'shortOption':"r",
'helpChapter':_("Common options"),
'help':_("remove the settings for connecting to a domain")
},
{'progAccess':(2,),
'longOption':'progress',
'helpChapter':_("Common options"),
'help':_("show progress bar for kde startup (works only with options \
--login)")
},
{'progAccess':(1,),
'longOption':'progress',
'helpChapter':_("Common options"),
'help':_("show progress bar for kde startup")
},
{'progAccess':(0,1,2,3),
'longOption':"vars",
'optVal':_("TYPE_VAR"),
'helpChapter':_("Common options"),
'help':_("print variables (TYPE_VAR - all:full var)")
},
{'progAccess':(0,1,2,3),
'longOption':"color",
'optVal':_("WHEN"),
'helpChapter':_("Common options"),
'help':_("control whether color is used. \
WHEN may be 'never', 'always', or 'auto'")
},
{'progAccess':(1,),
'shortOption':"f",
'longOption':"force",
'helpChapter':_("Common options"),
'help':_("always join the user profiles and preferences")
},
{'progAccess':(0,),
'longOption':"install",
'helpChapter':_("Common options"),
'help':_("add use of scripts this package for window manager")
},
{'progAccess':(0,),
'longOption':"uninstall",
'helpChapter':_("Common options"),
'help':_("remove use of scripts this package for window manager and, \
if necessary, removes from domain")
},
{'progAccess':(0,),
'longOption':"mount",
'helpChapter':_("Common options"),
'help':_("mount [remote] resource for Samba (calculate-server)")
},
{'progAccess':(2,),
'longOption':"login",
'helpChapter':_("Common options"),
'help':_("mount user resource")+ " " +\
_("and synchronize the user preferences")
},
{'progAccess':(2,),
'longOption':"logout",
'helpChapter':_("Common options"),
'help':_("synchronize the user preferences") + " " +\
_("and umount user resource")
},
{'progAccess':(2,),
'longOption':"nosync",
'helpChapter':_("Common options"),
'help':_("not synchronize the user preferences, is used in \
conjunction with the 'login' or 'logout'")
}
]
self._cl_help__setParamHelp()
# Удаляем ненужный аттрибут класса cl_profile.xmlShare
self._createElement = False
delattr(self, "_createElement")
# Переменная объект Vars
self.clVars = False
# Переменная объект ldapFunction
self.ldapObj = False
# Переменная соединение с LDAP сервером
self.conLdap = False
# Базовый DN LDAP сервера
self.baseDN = False
# DN сервисов относительно базового
ServicesDN = "ou=Services"
relGrDN = 'ou=Groups'
relUsDN = 'ou=Users'
relServDN = 'ou=Unix'
relDN = self.addDN(relServDN,ServicesDN)
# DN пользователей, относительно базового DN
self.relUsersDN = self.addDN(relUsDN, relDN)
# DN групп, относительно базового DN
self.relGroupsDN = self.addDN(relGrDN, relDN)
# DN Samba групп
self.relSambaGroupsDN = self.addDN(relGrDN,"ou=Samba",
ServicesDN)
# Объект хранения переменных
self.clVars = False
# файл с дополнительной информацией о профиле
# на данный момент только количество файлов для rsync
# используемое для прогрессбара
self.configFile = ".calculate.ini"
# При включениии репликации
# Временные задержки для монтирования в секундах
self.sleeps = [0.5, 2, 5]
# Название ветки хранения последнего посещенного пользователя
self.replBranchName = "Worked"
# Аттрибут ветки хранения последнего посещенного пользователя
self.replAttr = "ou"
# DN системной ветки
self.sysDN = self.addDN("ou=Replication","ou=LDAP", ServicesDN)
replBranch = self.replAttr + "=" + self.replBranchName
# DN хранения последнего посещенного пользователя
self.replHostsDN = self.addDN(replBranch,self.sysDN)
# Файл репликации
self.replLogoutFile = ".logout"
self.skipHomeFile = ["Home","Disks","FTP",
self.replLogoutFile]
# Если атрибут установлен то значит (ошибка и отмонтируются
# пользовательские ресурсы)
self.errorAndUnmountUserRes = False
# Имя пользователя
self.userName = ""
def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False):
"""Получить пароль у пользователя
options - полученные опции командной строки
optDialog - опция командной строки для вывода диалога для получения
пароля
optStdIn - опция командной строки для получения пароля из
стандартного ввода (stdin)
pwDialog - структура для вывода приглашения в режиме диалога
"""
userPwd = ""
if optStdIn and options.has_key(optStdIn):
pwdA = sys.stdin.readline().rstrip()
pwdB = sys.stdin.readline().rstrip()
elif optDialog and options.has_key(optDialog):
if not pwDialog:
pwDialog = [_("New password"),
_("Retype new password")]
pwdA = getpass.getpass(pwDialog[0]+":")
pwdB = getpass.getpass(pwDialog[1]+":")
if (optStdIn and options.has_key(optStdIn)) or\
(optDialog and options.has_key(optDialog)):
if not pwdA or not (pwdA == pwdB):
self.printERROR (_("ERROR") + ": " +\
_("passwords do not match"))
return False
userPwd = pwdA
return userPwd
def exit(self, exitCode):
"""Метод выхода при ошибке"""
self.errorExit()
sys.exit(exitCode)
def errorExit(self):
"""Отмонтирование пользовательских ресурсов при ошибке"""
# TODO: добавить удаление приватных файлов
if self.errorAndUnmountUserRes and self.userName:
self.umountUserResNoSync(self.userName, False, False, False, True)
def __del__(self):
"""Выполняется при удалении объекта"""
self.errorExit()
def isRoot(self, printError=True):
"""Определяет является ли пользователь root"""
if os.getuid() == 0 and os.getgid() == 0:
return True
else:
if printError:
self.printERROR(_("The user is not root"))
return False
def createClVars(self, clVars=False):
"""Создает объект Vars"""
if not clVars:
clVars = cl_base.DataVars()
clVars.flClient()
clVars.flIniFile()
# Устанавливаем у объекта объект Vars
self.clVars = clVars
return True
def addDN(self, *arg):
"""Складывает текстовые элементы DN"""
DNs = []
for dn in arg:
if dn:
DNs.append(dn)
return ','.join(DNs)
def searchLdapDN(self, name, relDN, attr, retAttr=None):
"""Находит DN в LDAP"""
baseDN = self.clVars.Get("ld_base_dn")
DN = self.addDN(relDN,baseDN)
#searchScope = ldap.SCOPE_SUBTREE
searchScope = ldap.SCOPE_ONELEVEL
searchFilter = "%s=%s" %(attr,name)
retrieveAttributes = retAttr
resSearch = self.ldapObj.ldapSearch(DN, searchScope,
searchFilter, retrieveAttributes)
return resSearch
def searchPrevHost(self, userName):
"""Находит сервер к которому был подключен пользователь"""
# Короткое имя системы
osLinuxShort = self.clVars.Get("os_linux_shortname")
# Имя для поиска в служебной ветке репликации
userLogin = "%s@%s"%(userName,osLinuxShort)
resSearch = self.searchLdapDN(userLogin, self.replHostsDN, "uid")
return resSearch
def searchReplBranch(self):
"""Находит ветку создаваемую при включении репликации
В ветке находятся ((имя пользователя, имя системы, хост) ....)
"""
resSearch = self.searchLdapDN(self.replBranchName,
self.sysDN,
self.replAttr)
return resSearch
def searchUnixUser(self, userName):
"""Находит пользователя сервиса Unix"""
resSearch = self.searchLdapDN(userName, self.relUsersDN, "uid")
return resSearch
def searchUnixGid(self, groupId):
"""Находит группу сервиса Unix по ёе id в ветке Unix"""
resSearch = self.searchLdapDN(str(groupId), self.relGroupsDN,
"gidNumber")
return resSearch
def searchSambaGid(self, groupId):
"""Находит группу сервиса Unix по ёе id в ветке Samba"""
resSearch = self.searchLdapDN(str(groupId), self.relSambaGroupsDN,
"gidNumber")
return resSearch
def getLdapObjBind(self, host, printError=True):
"""Получаем объект ldapFunction
Соединяемся пользователем bind
В выходном объекте есть соединение с LDAP сервером: self.conLdap
"""
self.createClVars(self.clVars)
bindDn = self.clVars.Get("ld_bind_dn")
bindPw = self.clVars.Get("ld_bind_pw")
if not (bindDn or bindPw):
if printError:
self.printERROR(_("Not found LDAP bind DN or password") +\
" ...")
return False
ldapObj = cl_utils2.ldapFun(bindDn, bindPw, host)
if ldapObj.getError():
if printError:
self.printERROR (_("LDAP connect error") + ": " +\
ldapObj.getError().strip())
return False
# Устанавливаем у объекта соединение и объект LDAP функций
self.ldapObj = ldapObj
self.conLdap = ldapObj.conLdap
return True
def searchLineInFile(self, name, fileName, numEl=0):
"""Ищет строку в которой есть название разделенное ':'
в файле похожем на /etc/passwd"""
if os.path.exists(fileName):
FD = open(fileName)
lines = FD.readlines()
FD.close()
lineFound = ""
for line in lines:
if name == line.split(":")[numEl]:
lineFound = line
break
if lineFound:
return lineFound
else:
return False
def searchPasswdUser(self, userName):
"""Ищет пользователей в /etc/passwd"""
filePasswd = "/etc/passwd"
return self.searchLineInFile(userName, filePasswd)
def searchGroupGid(self, groupId):
"""Ищет gid в /etc/group"""
gid = str(groupId)
fileGroup = "/etc/group"
return self.searchLineInFile(gid, fileGroup, 2)
def getUserLdapInfo(self, userName, printError=True):
"""Выдаем uid и gid пользователя"""
searchUser = self.searchUnixUser(userName)
if not searchUser:
if printError:
self.printERROR(_("User %s not found in Unix service")\
%str(userName))
return False
uid = False
gid = False
fullName = ""
mail = ""
jid = ""
group = ""
if searchUser[0][0][1].has_key('uidNumber'):
uid = searchUser[0][0][1]['uidNumber'][0]
if searchUser[0][0][1].has_key('gidNumber'):
gid = searchUser[0][0][1]['gidNumber'][0]
searchGroup = self.searchUnixGid(gid)
if searchGroup and searchGroup[0][0][1].has_key('cn'):
group = searchGroup[0][0][1]['cn'][0]
else:
searchGroup = self.searchSambaGid(gid)
if searchGroup and searchGroup[0][0][1].has_key('cn'):
group = searchGroup[0][0][1]['cn'][0]
if searchUser[0][0][1].has_key('cn'):
fullName = searchUser[0][0][1]['cn'][0]
if searchUser[0][0][1].has_key('mail'):
mail = searchUser[0][0][1]['mail'][0]
if searchUser[0][0][1].has_key('registeredAddress'):
jid = searchUser[0][0][1]['registeredAddress'][0]
if searchUser[0][0][1].has_key('homeDirectory'):
home = searchUser[0][0][1]['homeDirectory'][0]
if uid and gid:
return (uid, gid, fullName, mail, jid ,home, group)
else:
return ()
def createUserDir(self, uid, gid, userDir, mode=0700):
"""Создание пользовательской директории"""
if not os.path.exists(userDir):
os.makedirs(userDir)
if mode:
os.chmod(userDir,mode)
os.chown(userDir,uid,gid)
return True
else:
self.printERROR(_("Path %s exists") %userDir)
return False
def applyProfilesFromUser(self,progress=False):
"""Применяем профили для пользователя"""
# Cоздаем объект профиль
if progress:
clProf = ProgressProfile(self.clVars)
else:
clProf = cl_profile.profile(self.clVars)
# Объединяем профили
dirsFiles = clProf.applyProfiles()
if progress:
clProf.progress.close()
if clProf.getError():
self.printERROR(clProf.getError())
return False
else:
return dirsFiles
def applyProfilesFromSystem(self):
"""Применяем профили для cистемы"""
# Cоздаем объект профиль
clProf = cl_profile.profile(self.clVars)
# Объединяем профили
dirsFiles = clProf.applyProfiles()
if clProf.getError():
self.printERROR(clProf.getError())
return False
else:
return dirsFiles
def checkUserPwdLDAP(self, server, userDN, password):
"""Проверка пароля Unix пользователя на сервере"""
ldapInit = ldap.initialize("ldap://%s"%server)
errMessage = ""
try:
ldapInit.bind_s(userDN, password)
except ldap.INVALID_CREDENTIALS:
errMessage = _("Password incorrect")
return False, errMessage
except ldap.LDAPError, e:
errMessage = e[0]['desc']
return False, errMessage
return True, errMessage
def getServerDataUser(self):
"""Получение имени LDAP сервера и DN пользователей под пользователем"""
fileName = "/etc/ldap.conf"
serverName = ""
usersDN = ""
strServer = ("host","HOST")
lenStrServer = len(strServer[0])
strDN = ("nss_base_passwd","NSS_BASE_PASSWD")
lenStrDN = len(strDN[0])
splList = (" ", "\t")
try:
for i in open(fileName):
if not serverName and\
filter(lambda x: i.startswith(x),strServer) and\
len(i)>lenStrServer:
spl = i[lenStrServer]
if spl in splList:
serverName = i.rpartition(spl)[2].strip()
if not usersDN and filter(lambda x: i.startswith(x), strDN) and\
len(i)>lenStrDN:
spl = i[lenStrDN]
if spl in splList:
usersDN = i.rpartition(spl)[2].partition('?')[0].strip()
if serverName and usersDN:
break
except:
self.printERROR(_("Can not open %s")%fileName)
return False
if serverName and usersDN:
return (serverName, usersDN)
else:
return ()
def setUserPasswordToServer(self, options):
"""Установка пароля пользователя на сервере"""
# Проверяем на root
if self.isRoot(False):
self.printERROR(_("The user is root"))
self.printWARNING(\
_("The program can be executed from a non-root user only"))
return False
# DNS имя хоста
data = self.getServerDataUser()
if not data:
self.printERROR(_("The computer is not in domain"))
self.printWARNING(_("Use passwd"))
return False
server, usersDN = data
# Получаем старый пароль пользователя
curPassword = self.getUserPassword(_("Enter current password"))
if not curPassword:
self.printERROR(_("Current password is empty"))
return False
userDN = self.addDN("uid=%s"%os.environ["USER"], usersDN)
# Проверяем в LDAP сервере текущий пароль пользователя
ret, err = self.checkUserPwdLDAP(server, userDN, curPassword)
if not ret:
self.printERROR(err)
return False
optPasswd = options
if not options:
optPasswd = {"p":""}
password = self.getUserPwd(optPasswd, "p", False)
if password == False:
return False
# ~/.calculate
pathConfig = os.path.join(os.environ["HOME"],".calculate")
# Создаем директорию
if not os.path.exists(pathConfig):
os.makedirs(pathConfig)
# Переменные для записи в env файл
varsConfig = {"unix_hash":self.getHashPasswd(password,"ssha"),
"samba_lm_hash":self.getHashPasswd(password,"lm"),
"samba_nt_hash":self.getHashPasswd(password,"nt")}
if filter(lambda x: not x, varsConfig.values()):
return False
# ~/.calculate/server.env
fileConfig = os.path.join(pathConfig,"server.env")
txtConfig = cl_base.iniParser(fileConfig)
if not txtConfig.setVar(["passwd","samba"], varsConfig):
self.printERROR(_("Can not write password variable in file %s")\
%fileConfig)
return False
self.printOK(_("Changed password of user %s")%os.environ["USER"] + \
" ...")
self.printWARNING(_("Password will be changed when you logout from the \
X session"))
return True
def getUserPassword(self, pwDialog=False):
"""Получить пароль у пользователя
pwDialog - приглашение ввода пароля
"""
if not pwDialog:
pwDialog = _("Password")
userPwd = getpass.getpass(pwDialog+":")
return userPwd
def chownR(self, directory, uid, gid, dirsAndFiles=False):
"""изменяет владельца и группу
для всех файлов и директорий внутри directory
"""
if dirsAndFiles:
dirs, files = dirsAndFiles
# меняем владельца домашней директории
os.chown(directory, uid,gid)
# Меняем владельца директорий
for dirCh in dirs:
if os.path.exists(dirCh):
os.chown(dirCh, uid,gid)
# Меняем владельца файлов
for fileCh in files:
if os.path.exists(fileCh):
if os.path.islink(fileCh):
os.lchown(fileCh, uid, gid)
else:
os.chown(fileCh, uid,gid)
return True
else:
fileObj = cl_profile._file()
scanObjs = fileObj.scanDirs([directory])
# меняем владельца домашней директории
os.chown(directory, uid,gid)
# Меняем владельца директорий
for dirCh in scanObjs[0].dirs:
os.chown(dirCh, uid,gid)
# Меняем владельца файлов
for fileCh in scanObjs[0].files:
os.chown(fileCh, uid,gid)
# Меняем владельца ссылок
for linkCh in scanObjs[0].links:
os.lchown(linkCh[1], uid, gid)
return True
def execProg(self, cmdStrProg, inStr=False, retFull=True, envProg={}):
"""Выполняет внешнюю программу
Параметры:
cmdStrProg внешняя программа
inStr данные передаваемые программе на страндартный вход.
Возвращаемые параметры:
строка которую выведет внешняя программа или False в случае ошибки
"""
env_path = {"PATH":cl_utils.getpathenv()}
env = {}
env.update(os.environ.items() + env_path.items() + envProg.items())
retCode,programOut = cl_utils.runOsCommand(cmdStrProg,inStr,retFull,env)
if not retCode:
return programOut
return False
def getUserPasswdInfo(self, userName):
"""получаем uid и gid пользователя из /etc/passwd"""
resPasswd = self.searchPasswdUser(userName)
if resPasswd:
uid = resPasswd.split(":")[2]
gid = resPasswd.split(":")[3]
fullName = resPasswd.split(":")[4]
mail = ""
group = ""
jid = ""
home = os.path.join("/home",userName)
resGroup = self.searchGroupGid(gid)
if resGroup:
group = resGroup.split(":")[0]
return (uid, gid, fullName, mail, jid, home, group)
else:
return False
def setDaemonAutostart(self, daemon):
"""Прописывает демона в автозагрузку"""
execStr = "rc-update add %s default" %daemon
textLine = self.execProg(execStr)
if textLine:
return True
else:
self.printERROR(_("ERROR") + ": " + execStr)
self.printERROR(_("Can not add at default runlevel"))
return False
def delDaemonAutostart(self, daemon):
"""Удаляет демона из автозагрузки"""
execStr = "rc-update del %s default" %daemon
textLine = self.execProg(execStr)
if textLine:
return True
else:
self.printERROR(_("ERROR") + ": " + execStr)
self.printERROR(_("Can not delete from default runlevel"))
return False
def createHome(self, userName, applyAlways=False, progress=False):
"""Создание пользовательской директории с настройками для kde4"""
# Имя пользователя
self.userName = userName
# Проверяем на root
if not self.isRoot():
return False
# Создаем объект переменных
self.createClVars()
uidGid = False
# Подсоединяемся к серверу
domain = self.clVars.Get("cl_remote_host")
connectLdap = False
if domain:
if not self.getLdapObjBind(domain):
return False
connectLdap = True
if connectLdap:
# uid и gid и mail из Ldap
uidGid = self.getUserLdapInfo(userName,False)
if not domain:
# uid и gid и mail из passwd
uidGid = self.getUserPasswdInfo(userName)
if not uidGid:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Not found user uid and gid"))
return False
uid = int(uidGid[0])
gid = int(uidGid[1])
fullName = uidGid[2]
mail = uidGid[3]
jid = uidGid[4]
homeDir = uidGid[5]
group = uidGid[6]
if not group:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Not found user primary group (gid=%s)"%str(gid)))
return False
# Создаем пользовательскую директорию
self.clVars.Set('cl_root_path',homeDir,True)
homeExists = os.path.exists(homeDir)
# Первый проход
self.clVars.Set('cl_pass_step','first',True)
if homeExists:
self.printWARNING(_("Home dir %s exists")%homeDir)
if set(os.listdir(homeDir))-set(self.skipHomeFile):
if not applyAlways:
# Второй и последующие проходы
self.clVars.Set('cl_pass_step','next',True)
self.printSUCCESS(_("Apply always profiles") + " ...")
# Создаем домашнюю директорию
if not os.path.exists(homeDir):
self.createUserDir(uid, gid, homeDir)
# Записываем переменные
self.clVars.Set('ur_login', userName)
self.clVars.Set('ur_fullname', fullName)
self.clVars.Set('ur_mail', mail)
self.clVars.Set('ur_jid', jid)
self.clVars.Set('ur_group', group)
# Применяем профили для пользователя
dirsAndFiles = self.applyProfilesFromUser(progress)
if not dirsAndFiles:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Can not apply user profile"))
return False
self.chownR(homeDir, uid, gid, dirsAndFiles)
if not homeExists:
self.printSUCCESS(_("Created home dir %s")%homeDir + " ...")
self.printSUCCESS(_("User account is configured") + " ...")
return True
def isDomain(self):
"""Находится ли компьютер в домене"""
self.createClVars(self.clVars)
foundMountRemote =self.isMount("/var/calculate/remote" ,"cifs")
foundMountHome =self.isMount("/var/calculate/client-home","none",False)
if not (self.clVars.Get("cl_remote_host") and foundMountRemote and\
foundMountHome):
self.printERROR(_("The computer is not in domain"))
return False
return (foundMountRemote,foundMountHome)
def killRsync(self):
"""Убивает все процессы rsync и cl-sync --login"""
listProcess = self.execProg("ps ax",False,False)
if not listProcess:
return False
killPid = []
flagError = False
for process in listProcess:
if "rsync" in process:
killPid.append(process.split(" ")[0])
if "--login" in process and "cl-sync" in process:
killPid.append(process.split(" ")[0])
strPid = str(os.getpid())
# удалим свой pid из списка завершаемых процессов
if strPid in killPid:
killPid.remove(strPid)
if killPid and " ".join(killPid).strip():
textLine = self.execProg("kill -9 %s" %" ".join(killPid))
if not (textLine == None):
self.printERROR(_("Can not 'kill %s'")\
%" ".join(killPid))
flagError = True
if flagError:
return False
else:
return True
def isMount(self, pathMount ,typeMount, secondPath=True):
"""Примонтирована ли директория"""
path = os.path.realpath(pathMount)
foundMount = False
if secondPath:
reFoundMount = re.compile("on\s+%s\s+type\s+%s"%(path,typeMount))
else:
reFoundMount = re.compile("%s\s+.+type\s+%s"%(path,typeMount))
resMount = self.execProg("mount",False,False)
if resMount and type(resMount) == types.ListType:
for string in resMount:
if reFoundMount.search(string):
foundMount = string
break
return foundMount
def relevanceProfiles(self, hostAuth):
"""Определяем актуальность наложенных профилей
в зависимости от версии программы
Перед запуском обязательно должен быть определен объект переменных
self.clVars
Если актуальны - True, если нет False
"""
# 'local' или имя хоста
if hostAuth != self.clVars.Get("os_remote_auth"):
return False
clProf = cl_profile.profile(self.clVars)
# Текущая версия программы
currentVersion = self.clVars.Get("cl_ver")
# Версия программы которая ранее работала с профилями
previousVersion = self.clVars.Get("os_remote_client")
cVersion, pVersion = clProf._convertVers(currentVersion,previousVersion)
# Если версии программ не равны то профили не актуальные
if cVersion != pVersion:
return False
return True
def getPathProfiles(self, listPath):
"""Получаем список директорий хранения профилей"""
"""список накладываемых профилей при установке, наложении профилей"""
profpath = []
for profPath in listPath:
if os.path.isdir(profPath):
paths = os.listdir(profPath)
for path in paths:
ph = os.path.join(profPath,path)
filesAndDirs = os.listdir(ph)
if os.path.isdir(ph) and filesAndDirs:
profpath.append(ph)
return profpath
def applyRelevanceProfiles(self, hostAuth):
"""Накладывает релевантные профили
Перед запуском обязательно должен быть определен объект переменных
self.clVars
"""
if not self.relevanceProfiles(hostAuth):
# Обнуляем переменную удаленный хост
if hostAuth == "local":
self.clVars.Set("cl_remote_host","",True)
# Изменяем базовую директорию наложения профилей
self.clVars.Set("cl_root_path","/",True)
# Устанавливаем действие profiles_domain
self.clVars.Set("cl_pass_type","domain",True)
# Наложим профили profiles/domain
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
self.clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/domain' profiles"))
return False
if hostAuth == "local":
self.printOK(_("Set profiles of local mode"))
else:
self.printOK(_("Set profiles of network mode"))
currentVersion = self.clVars.Get("cl_ver")
self.clVars.Write("os_remote_client", currentVersion)
self.clVars.Write("os_remote_auth", hostAuth)
return True
def mountRemote(self):
"""Монтирование remote и домашней директории если компьютер в домене
а так-же ввод в домен если найдено имя хоста и пароль для подключения
"""
# Проверяем на root
if not self.isRoot():
return False
self.createClVars(self.clVars)
domain = self.clVars.Get("cl_remote_host")
if domain:
foundMountRemote = self.isMount("/var/calculate/remote" ,"cifs")
foundMountHome = self.isMount("/var/calculate/client-home","none",
False)
else:
self.printWARNING(_("This computer is not in domain"))
# Если профили не актуальны накладываем новую версию профилей
if not self.applyRelevanceProfiles("local"):
return False
return True
pathHome = "/var/calculate/client-home"
if foundMountRemote:
self.printWARNING(_("Samba resource [%s] is mount")%"remote" + \
" ...")
if foundMountHome:
self.printWARNING(str(pathHome) + " " +_("is mount")+
" ...")
if foundMountHome and foundMountRemote:
# Накладываем сетевые профили
if domain:
self.clVars.flIniFile()
if not self.applyRelevanceProfiles(domain):
return False
return True
flagLocalProfile = False
if not foundMountRemote:
pathRemote = "/var/calculate/remote"
pwdRemote = self.clVars.Get("cl_remote_pw")
if not (domain and pwdRemote):
self.printERROR(_("Not found variable")+\
": cl_remote_pw ...")
return False
if not os.path.exists(pathRemote):
os.makedirs(pathRemote)
mountStr = "mount -t cifs -o user=client //%s/remote %s"\
%(domain,pathRemote)
textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote})
if not (textLine == None):
self.printWARNING(_("Can not mount Samba resource [%s]")%\
"remote" + " ...")
flagLocalProfile = True
# Если профили не актуальны накладываем новую версию профилей
if not self.applyRelevanceProfiles("local"):
return False
if foundMountHome:
umountStr = "umount /home"
textLine = self.execProg(umountStr)
if not (textLine == None):
self.printERROR(_("Can not unmount") + " /home")
return False
return True
self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\
" ...")
if (not foundMountHome) and (not flagLocalProfile):
if not os.path.exists(pathHome):
os.makedirs(pathHome)
mountStr = "mount -o bind %s /home" %pathHome
textLine = self.execProg(mountStr)
if not (textLine == None):
self.printERROR(_("Can not mount") + " " + str(pathHome) +\
" ...")
return False
self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\
" ...")
# Накладываем сетевые профили
if domain:
self.clVars.flIniFile()
if not self.applyRelevanceProfiles(domain):
return False
self.restartDBus()
return True
def delDomain(self):
"""выводим из домена"""
# Проверяем на root
if not self.isRoot():
return False
self.createClVars()
pathRemote = "/var/calculate/remote"
pathHome = "/var/calculate/client-home"
foundMountRemote = self.isMount(pathRemote ,"cifs")
foundMountHome = self.isMount(pathHome ,"none",False)
domain = self.clVars.Get("cl_remote_host")
if not domain:
self.printWARNING(_("The computer is not in domain"))
return True
if foundMountRemote:
textLineUmount = self.umountSleepPath(pathRemote)
if not textLineUmount:
return False
if foundMountHome:
textLineUmount = self.umountSleepPath(pathHome ,"none", False)
if not textLineUmount:
return False
self.clVars.Delete("cl_remote_host","local")
self.clVars.Delete("cl_remote_pw","local")
self.clVars.Set("cl_remote_host","",True)
self.clVars.Set("cl_remote_pw","",True)
# Изменяем базовую директорию наложения профилей
self.clVars.Set("cl_root_path","/",True)
# Наложим профили profiles/domain
# Новые пути к профилям
# Устанавливаем действие profiles_domain
self.clVars.Set("cl_pass_type","domain",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
self.clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/domain' profiles"))
return False
if not self.delDaemonAutostart("client"):
return False
self.printOK(_("Computer removed from domain %s")%domain + " ...")
return True
def uninstallClient(self):
"""Наложение профиля клиента по умолчанию и
при необходимости отключение от домена"""
if not self.isRoot():
return False
self.createClVars()
clVars = self.clVars
messageOk = _("Removed use of scripts this package for window \
manager") + " ..."
# Устанавливаем переменную профиля
clVars.Set("cl_pass_run","off",True)
# Изменяем базовую директорию наложения профилей
clVars.Set("cl_root_path","/",True)
# Устанавливаем действие profiles_client
clVars.Set("cl_pass_type","install",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/client' profiles"))
return False
remoteHost = clVars.Get("cl_remote_host")
if remoteHost:
if not self.delDomain():
return False
self.printOK(messageOk)
return True
def installClient(self, clVars=False, printSuccess=True):
"""Наложение профиля клиента и при необходимости подключение к домену"""
if not self.isRoot():
return False
if not clVars:
#Создаем объект переменных
self.createClVars()
clVars = self.clVars
messageOk = _("Added use of scripts this package for window \
manager") + " ..."
# Устанавливаем переменную профиля
clVars.Set("cl_pass_run","on",True)
# Изменяем базовую директорию наложения профилей
clVars.Set("cl_root_path","/",True)
# Устанавливаем действие profiles_client
clVars.Set("cl_pass_type","install",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/client' profiles"))
return False
if printSuccess:
self.printOK(messageOk)
return True
def addDomain(self, domainName):
"""Вводим в домен"""
# Проверяем на root
if not self.isRoot():
return False
# Создаем объект переменных
self.createClVars()
netDomain = self.clVars.Get("os_net_domain")
# Получам имя сервера (домена)
if "." in domainName:
domain = domainName
else:
domain = "%s.%s" %(domainName,netDomain)
execStr = "ping -c 2 -i 0.3 %s" %domain
resPing = self.execProg(execStr, False, False)
if not resPing:
self.printERROR(_('Can not execute "%s"')%execStr)
return False
foudHost = False
foudHostSamba = False
foundMountRemote = False
reFoundHost = re.compile("(\d+)\% packet loss")
if resPing and type(resPing) == types.ListType and len(resPing)>=2:
pingStr = resPing[-2].strip()
reSearch = reFoundHost.search(pingStr)
if reSearch and reSearch.group(1) == "0":
foudHost = True
if not foudHost:
self.printERROR(_("Not found domain %s")%domain)
return False
reFoundHostSamba = re.compile("Server=\[Samba.+\]")
resSmbClient = self.execProg("smbclient -N -L %s" %domain,
False,False)
if resSmbClient and type(resSmbClient) == types.ListType:
for string in resSmbClient:
if reFoundHostSamba.search(string):
foudHostSamba = True
break
if not foudHostSamba:
self.printERROR(_("Not found Samba server in %s")%domain)
return False
pwd = False
if self.clVars.Get("cl_remote_host") and \
self.clVars.Get("cl_remote_host") != domain:
if not self.delDomain():
return False
elif self.clVars.Get("cl_remote_host") and \
self.clVars.Get("cl_remote_host") == domain:
pwd = self.clVars.Get("cl_remote_pw")
foundMountRemote =self.isMount("/var/calculate/remote" ,"cifs")
foundMountHome =self.isMount("/var/calculate/client-home","none",False)
if foundMountRemote:
self.printWARNING(_("Samba resource [%s] is mount")%\
"remote" + " ...")
else:
if pwd:
userPwd = pwd
else:
userPwd=self.getUserPassword(\
_("Domain password for the desktop"))
pathRemote = "/var/calculate/remote"
pwdRemote = userPwd
if not os.path.exists(pathRemote):
os.makedirs(pathRemote)
mountStr = "mount -t cifs -o user=client //%s/remote %s"\
%(domain,pathRemote)
textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote})
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource [%s]")%\
"remote" + " ...")
return False
else:
self.printSUCCESS(_("Mount Samba resource [%s]")%"remote" + \
" ...")
self.clVars.Write("cl_remote_host", domain, False, "local")
self.clVars.Write("cl_remote_pw", userPwd, False, "local")
pathHome = "/var/calculate/client-home"
if foundMountHome:
self.printWARNING(str(pathHome)+ " " +_("is mount")+
" ...")
else:
if not os.path.exists(pathHome):
os.makedirs(pathHome)
mountStr = "mount -o bind %s /home" %pathHome
textLine = self.execProg(mountStr)
if not (textLine == None):
self.printERROR(_("Can not mount") + " " + str(pathHome) +\
" ...")
return False
self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\
" ...")
self.clVars.flIniFile()
#считаем переменные для клиента
servDn = self.clVars.Get("ld_services_dn")
unixDN = self.clVars.Get("ld_unix_dn")
bindDn = self.clVars.Get("ld_bind_dn")
bindPw = self.clVars.Get("ld_bind_pw")
# запишем их
if not (servDn and unixDN and bindDn and bindPw):
self.printERROR(_("Not found variables") + ":")
self.printERROR("ld_services_dn or ld_unix_dn \
or ld_bind_dn or ld_bind_pw")
return False
# Наложим профили profiles/client
if not self.installClient(self.clVars, False):
return False
# Наложим профили profiles/domain
# Устанавливаем действие profiles_client
self.clVars.Set("cl_pass_type","domain",True)
# Новые пути к профилям
profPaths = self.getPathProfiles(self.rootProfilePaths)
if not profPaths:
self.printERROR(_("Empty profile paths %s")\
%", "(self.rootProfilePaths))
return False
# Изменяем переменную хранения профилей
self.clVars.Set("cl_profile_path",profPaths,True)
# Наложим профили
dirsAndFiles = self.applyProfilesFromSystem()
if not dirsAndFiles:
self.printERROR(_("Can not apply 'profiles/domain' profiles"))
return False
# Рестартуем dbus
self.restartDBus()
if not self.setDaemonAutostart("client"):
return False
self.printOK(_("Computer added to domain %s")%domain + " ...")
return True
def restartDBus(self):
"""Перезапускаем службу D-Bus"""
dbusDaemon = '/etc/init.d/dbus'
if os.path.exists(dbusDaemon):
if os.system(dbusDaemon + ' restart &>/dev/null') != 0:
self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...")
return False
return True
def removePrivateFiles(self, userHome):
"""Удаление приватных файлов"""
privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl',
'.calculate/server.env']
# файлы в .ssh
sshHome = ".ssh"
sshPath = os.path.join(userHome,sshHome)
if os.path.isdir(sshPath):
# .ssh файлы относительно домашней директории пользователя
privateFiles += map(lambda x:os.path.join(sshHome,x),\
filter(lambda x:\
os.path.isfile(os.path.join(sshPath,x)),\
os.listdir(sshPath)))
for prFile in privateFiles:
rmFile = os.path.join(userHome, prFile)
if os.path.exists(rmFile):
os.remove(rmFile)
return True
def umountSleepPath(self, path, typeMount='cifs', secondPath=True):
"""Отмонтирует путь при неудаче задержка потом повтор"""
#Проверяем на монтирование директории
if self.isMount(path, typeMount, secondPath):
textLine = self.execProg("umount %s"%path)
if textLine != None:
i = 0
flagError = False
while (i<len(self.sleeps) and textLine != None):
# Задержка перед следующей попыткой
time.sleep(self.sleeps[i])
# Отмонтируем Samba ресурс
if self.isMount(path, typeMount, secondPath):
textLine = self.execProg("umount %s"%path)
else:
textLine = None
break
i += 1
if textLine != None:
self.printERROR(_("Can not unmount path %s")%path + " ...")
return False
return True
def umountUserRes(self, userName,progress=False):
"""Отмонтирование пользовательских ресурсов и синхронизация настроек"""
# Имя пользователя
self.userName = userName
# Проверяем на root
if not self.isRoot():
return False
self.createClVars()
# В случае компьютера вне домена
if not self.clVars.Get("cl_remote_host"):
self.printSUCCESS(_("To be used local profile."))
return True
connectDomain = self.isDomain()
if not connectDomain:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
elif not connectDomain[0]:
self.printERROR(_("Can not mount Samba resource [%s]")%"remote"+\
" ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Если пользователь в X сессии тогда не будем отмонтировать ресурсы
if self.isSessionUser(userName):
self.printERROR(_("User %s is in X session")%userName)
self.printERROR(_("Can not unmount user %s resource")%userName)
return False
# Подсоединяемся к серверу
domain = self.clVars.Get("cl_remote_host")
if not self.getLdapObjBind(domain):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# homeDir из LDAP
resLdap = self.getUserLdapInfo(userName)
if not resLdap:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
uid = int(resLdap[0])
gid = int(resLdap[1])
homeDir = resLdap[5]
home = os.path.split(homeDir)[0]
pathRemote = []
# Удаленный ресурс профилей
pathRemote.append((os.path.join(home,"." + userName), "unix"))
# Удаленный ресурс home
pathRemote.append((os.path.join(homeDir,"Home"), "homes"))
# Удаленный ресурс ftp
if self.clVars.Get("cl_remote_ftp"):
pathRemote.append((os.path.join(homeDir,"FTP"), "ftp"))
# Удаленный ресурс share
pathRemote.append((os.path.join(homeDir,"Disks"), "share"))
if os.path.exists(homeDir):
self.moveHomeDir(homeDir)
else:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Directory %s not found") % homeDir)
return False
# Синхронизируем настройки
pathUnix = ""
for path, res in pathRemote:
if res == 'unix':
pathUnix = path
break
if pathUnix and self.isMount(pathUnix ,"cifs") and \
not(domain in self.isMount(pathUnix ,"cifs")):
# Убиваем rsync
if not self.killRsync():
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
textLine = self.umountSleepPath(pathUnix)
if not textLine:
self.printERROR(_("Can not unmount path %s")%pathUnix+ " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Монтируем ресурс Unix
# Получаем пароль пользователя из ключей ядра
userPwd = _cl_keys.getKey(userName)
if not userPwd:
self.printERROR(_("Not found user password"))
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
"unix",pathUnix)
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%"unix" + " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# проверяем произведен ли корректный вход в систему -
# в этом случае закачиваем профиль с локальной машины на сервер
logOutFile = os.path.join(homeDir,self.replLogoutFile)
# необходима ли синхронизация с локальным сервером
needSync = False
try:
# если актуальный профиль хранится не на локальном сервере
# то на локальный сервер закачиваем профиль
# так как даже если он будет поврежден на другом сервере
# остаётся правильная копия
if not self.isCorrectProfileOnLocalServer(userName):
needSync = True
else:
FD = open(logOutFile)
exitStr = FD.read().strip()
FD.close()
# проверяем logOutFile: не содержит ли он "PROCESS"
if exitStr != "PROCESS":
needSync = True
except:
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
if needSync and not self.syncUser(userName,homeDir,"logout",uid,gid):
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Удаляем приватные файлы
self.removePrivateFiles(homeDir)
flagError = False
for path, res in pathRemote:
if self.isMount(path ,"cifs"):
textLine = self.umountSleepPath(path)
if not textLine:
flagError = True
break
if os.path.exists(path) and not os.listdir(path):
try:
os.rmdir(path)
except:
self.printERROR(_("Can not remove dir %s")% path)
flagError = True
break
if flagError:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
self.printERROR(_("Can not save a user profile in the domain"))
return False
self.printSUCCESS(_("Saved a user profile in the domain"))
self.printOK(_("Umounted user resource in domain") + " ...")
return True
def umountUserResNoSync(self, userName, printError=True, printSuccess=True,
isXsession=True, killRsync=False, progress=False):
"""Отмонтирование пользовательских ресурсов
без синхронизации настроек"""
# Имя пользователя
self.userName = userName
# Проверяем на root
if not self.isRoot():
return False
if killRsync:
self.killRsync()
self.createClVars(self.clVars)
# В случае компьютера вне домена
if not self.clVars.Get("cl_remote_host"):
if printSuccess:
self.printSUCCESS(_("To be used local profile."))
return True
# Подсоединяемся к серверу
domain = self.clVars.Get("cl_remote_host")
home = ""
homeDir = ""
if not self.getLdapObjBind(domain, printError):
home = "/home"
homeDir = os.path.join(home,userName)
# Если пользователь в X сессии тогда не будем отмонтировать ресурсы
if isXsession and self.isSessionUser(userName):
self.printERROR(_("User %s is in X session")%userName)
self.printERROR(_("Can not unmount user %s resource")%userName)
return False
if not homeDir:
# homeDir из LDAP
resLdap = self.getUserLdapInfo(userName, printError)
if not resLdap:
home = "/home"
homeDir = os.path.join(home,userName)
else:
homeDir = resLdap[5]
home = os.path.split(homeDir)[0]
pathRemote = []
# Удаленный ресурс профилей
pathRemote.append((os.path.join(home,"." + userName), "unix"))
# Удаленный ресурс home
pathRemote.append((os.path.join(homeDir,"Home"), "homes"))
# Удаленный ресурс ftp
if self.clVars.Get("cl_remote_ftp"):
pathRemote.append((os.path.join(homeDir,"FTP"), "ftp"))
# Удаленный ресурс share
pathRemote.append((os.path.join(homeDir,"Disks"), "share"))
flagError = False
if self.isMount(pathRemote[1][0] ,"cifs"):
self.moveHomeDir(homeDir)
for path, res in pathRemote:
if self.isMount(path ,"cifs"):
textLine = self.umountSleepPath(path)
if not textLine:
flagError = True
break
if os.path.exists(path) and not os.listdir(path):
try:
os.rmdir(path)
except:
if printError:
self.printERROR(_("Can not remove dir %s")% path)
flagError = True
break
if flagError:
if printError:
self.printERROR(_("Can not unmount user %s resource")%userName)
return False
if printSuccess:
self.printOK(_("Umounted user %s resources") %userName + " ...")
return True
def isSessionUser(self, userName):
"""Проверка на вход пользователя в X"""
xSession = False
reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
resWho = self.execProg("who",False,False)
if resWho and type(resWho) == types.ListType:
for string in resWho:
if reFoundUser.search(string):
xSession = True
break
return xSession
def isTwoSessionsUser(self, userName):
"""Проверка на повторный вход пользователя"""
xSession = 0
foundTwoSession = False
reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
resWho = self.execProg("who",False,False)
if resWho and type(resWho) == types.ListType:
for string in resWho:
if reFoundUser.search(string):
xSession +=1
if xSession>1:
foundTwoSession = True
self.printERROR(\
_("Second X session for user %s can not be opened.")\
%userName)
break
return foundTwoSession
def mountSambaRes(self,userName,userPwd,uid,gid,res,path,
mountUidList=['ftp','home','share']):
"""Монтирует Samba ресурсы"""
if res in mountUidList:
# Монтируем директории c uid
mountStr="mount -t cifs -o user=%s,uid=%s,gid=%s,noperm"\
%(userName,uid,gid) +\
" //%s/%s %s" %(self.clVars.Get("cl_remote_host"),
res, path)
else:
# Монтируем директории
mountStr="mount -t cifs -o user=%s"%(userName)+\
" //%s/%s %s" %(self.clVars.Get("cl_remote_host"),
res, path)
textLine = self.execProg(mountStr, None, True, {"PASSWD":userPwd})
return textLine
def removeDir(self, rmDirOrScanObjs):
"""Рекурсивное удаление директории
входной параметр директория или результат сканирования файлов (объект)
"""
rmDir = False
if type(rmDirOrScanObjs) == types.StringType:
rmDir = rmDirOrScanObjs
if not os.path.exists(rmDir):
self.printERROR(_("Not found remove dir %s") %rmDir)
return False
fileObj = cl_profile._file()
# Сканируем директорию
scanObjs = fileObj.scanDirs([rmDir])
else:
scanObjs = rmDirOrScanObjs
for socketRm in scanObjs[0].sockets:
# Удаляем сокеты
if os.path.exists(socketRm):
os.remove(socketRm)
for linkRm in scanObjs[0].links:
# Удаляем ссылки
os.unlink(linkRm[1])
for fileRm in scanObjs[0].files:
# Удаляем файлы
os.remove(fileRm)
scanObjs[0].dirs.sort(lambda x, y: cmp(len(y), len(x)))
for dirRm in scanObjs[0].dirs:
# Удаляем директории
os.rmdir(dirRm)
if rmDir:
os.rmdir(rmDir)
return True
def isCorrectProfileOnLocalServer(self,userName):
"""Узнать находится ли актуальный профиль пользователя на локальном
сервере
"""
searchPrevHost = self.searchPrevHost(userName)
if searchPrevHost and searchPrevHost[0][0][1].has_key('host'):
prevHost = searchPrevHost[0][0][1]['host'][0]
else:
prevHost = None
# если местоположение актуального профиля найти не удалось
# или его местоположение не на локальном сервере
if not prevHost or prevHost == self.clVars.Get('cl_remote_host'):
return True
else:
return False
def mountUserRes(self, userName, sync=True, progress=False):
"""Монтирование пользовательских ресурсов и синхронизация настроек"""
# Имя пользователя
self.userName = userName
# Проверяем на root
if not self.isRoot():
return False
# Проверка на повторный вход пользователя
if self.isTwoSessionsUser(userName):
return False
self.createClVars()
# В случае компьютера вне домена
if not self.clVars.Get("cl_remote_host"):
self.printSUCCESS(_("To be used local profile."))
return True
# Проверим что компьютер в домене и смонтирован [remote]
connectDomain = self.isDomain()
if not connectDomain:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
elif not connectDomain[0]:
self.printERROR(_("Can not mount Samba resource [%s]")%"remote"+ \
" ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Подсоединяемся к серверу
domain = self.clVars.Get("cl_remote_host")
if not self.getLdapObjBind(domain):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# homeDir из LDAP
resLdap = self.getUserLdapInfo(userName)
if not resLdap:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
uid = int(resLdap[0])
gid = int(resLdap[1])
homeDir = resLdap[5]
# При отсуствии создаем домашнюю директорию
if not os.path.exists(homeDir):
os.makedirs(homeDir)
os.chown(homeDir,uid,gid)
os.chmod(homeDir,0700)
# записываем в .logout файл статус "в процессе"
logOutFile = os.path.join(homeDir,self.replLogoutFile)
self.createUserFile(logOutFile,"PROCESS", uid, gid)
# Получаем пароль пользователя из ключей ядра
userPwd = _cl_keys.getKey(userName)
if not userPwd:
self.printERROR(_("Not found user password"))
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
home = os.path.split(homeDir)[0]
pathRemote = []
# Удаленный ресурс share
pathRemote.append((os.path.join(homeDir,"Disks"), "share"))
# Удаленный ресурс профилей
pathRemote.append((os.path.join(home,"." + userName), "unix"))
# Удаленный ресурс home
pathRemote.append((os.path.join(homeDir,"Home"), "homes"))
if self.clVars.Get("cl_remote_ftp"):
# Удаленный ресурс ftp
pathRemote.append((os.path.join(homeDir,"FTP"), "ftp"))
flagError = False
# В случае включения репликации на сервере True
replOn = self.searchReplBranch()
# Путь к профилю пользователя по умолчанию
defaultPath = ""
# Хост пользователя по умолчанию
defaultHost = self.clVars.Get("cl_remote_host")
for path, res in pathRemote:
# Создаем директории для монтирования
if not os.path.exists(path):
try:
os.mkdir(path)
os.chown(path, uid, gid)
os.chmod(path,0700)
except OSError:
self.printERROR(_("Error creating directory"))
self.printERROR(_("Permission denied: '%s'")%path)
flagError = True
break
# Проверяем на монтирование директории
if self.isMount(path, 'cifs'):
continue
# В случае репликации не монтируем профиль пользователя
if replOn and res=="unix":
defaultPath = path
continue
# Монтируем Samba ресурс
textLine = self.mountSambaRes(userName,userPwd,uid,gid,res,path)
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%res + " ...")
flagError = True
break
if flagError:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
flagFirstSync = False
if replOn or sync:
# Ошибка при монтировании unix ресурса удаленного сервера при
# включенной репликации
replErrorMount = False
# Ошибка при cинхронизации unix ресурса удаленного сервера при
# включенной репликации
replErrorSync = False
# Синхронизируем настройки
if sync:
# Короткое имя системы
osLinuxShort = self.clVars.Get("os_linux_shortname")
# В случае репликации
prevHost = ""
# Монтирование по умолчанию (default - cвой сервер, remote - чужой)
mountServer = ""
if replOn:
searchPrevHost = self.searchPrevHost(userName)
if searchPrevHost and searchPrevHost[0][0][1].has_key('host'):
prevHost = searchPrevHost[0][0][1]['host'][0]
# Монтируем ресурс unix текущего сервера
mountServer = "default"
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
"unix",defaultPath)
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%"unix" + " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Переносим настройки пользователя в новую директорию
# .CLD
self.upgradeProfileClient(userName, homeDir)
# Если на текущем сервере в ресурсе unix есть файлы
# то синхронируем настройки
# Получаем директорию хранения профиля на сервере
homeProfile = os.path.join(home, "." + userName, osLinuxShort)
if not os.path.exists(homeProfile):
homeProfile = os.path.join(home, "." + userName,
"." + osLinuxShort)
if os.listdir(homeProfile):
if not self.syncUser(userName, homeDir, "login", uid, gid,\
progress=progress,
host=self.clVars.Get('cl_remote_host')):
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
else:
#Удаляем ненужные файлы
rmFiles = list(set(os.listdir(homeDir))-\
set(self.skipHomeFile))
for rmFile in rmFiles:
delFile = os.path.join(homeDir,rmFile)
if os.path.islink(delFile):
os.unlink(delFile)
elif os.path.isfile(delFile):
os.remove(delFile)
elif os.path.isdir(delFile):
if not self.removeDir(delFile):
# Отмонтируем пользовательские ресурсы
# в случае ошибки
self.errorAndUnmountUserRes = True
return False
elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]):
os.remove(delFile)
# Отмонтируем ресурс
textLine = self.umountSleepPath(defaultPath)
if not textLine:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
if prevHost and prevHost != self.clVars.Get("cl_remote_host"):
# Монтируем настройки пользователя удаленного сервера
# если сервер найден
self.clVars.Set("cl_remote_host", prevHost, True)
mountServer = "remote"
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
"unix",defaultPath)
self.clVars.Set("cl_remote_host", defaultHost, True)
if not (textLine == None):
if self.isMount(defaultPath, 'cifs'):
textLine = self.umountSleepPath(defaultPath)
if not textLine:
# Отмонтируем пользовательские ресурсы
# в случае ошибки
self.errorAndUnmountUserRes = True
return False
# Монтируем текущий сервер если ошибка подключения к
# к найденному серверу
mountServer = "default"
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
"unix",defaultPath)
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource \
[%s]")%"unix" + " ...")
# Отмонтируем пользовательские ресурсы
# в случае ошибки
self.errorAndUnmountUserRes = True
return False
replErrorMount = True
else:
mountServer = "default"
# Монтируем текущий сервер если сервер не найден в LDAP
textLine = self.mountSleepRes(userName,userPwd,uid,gid,
"unix",defaultPath)
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource \
[%s]")%"unix" + " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
# если репликации нет, то prevHost и remote_host одинаковые
else:
prevHost=self.clVars.Get('cl_remote_host')
# Синхронизируем настройки пользователя
if not (replOn and mountServer == "default"):
# Переносим настройки пользователя в новую директорию
# .CLD
self.upgradeProfileClient(userName, homeDir)
# Получаем директорию хранения профиля на сервере
homeProfile = os.path.join(home, "." + userName, osLinuxShort)
if not os.path.exists(homeProfile):
homeProfile = os.path.join(home, "." + userName,
"." + osLinuxShort)
if os.listdir(homeProfile):
if not self.syncUser(userName, homeDir, "login", uid, gid,\
progress=progress,host=prevHost):
if replOn and mountServer == "remote":
replErrorSync = True
else:
# Отмонтируем пользовательские ресурсы
# в случае ошибки
self.errorAndUnmountUserRes = True
return False
else:
#Удаляем ненужные файлы
rmFiles = list(set(os.listdir(homeDir))-\
set(self.skipHomeFile))
for rmFile in rmFiles:
delFile = os.path.join(homeDir,rmFile)
if os.path.islink(delFile):
os.unlink(delFile)
elif os.path.isfile(delFile):
os.remove(delFile)
elif os.path.isdir(delFile):
if not self.removeDir(delFile):
# Отмонтируем пользовательские ресурсы
# в случае ошибки
self.errorAndUnmountUserRes = True
return False
elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]):
os.remove(delFile)
if replOn and mountServer == "remote":
# В случае репликации перемонтируем ресурс профилей
# на текущий сервер (в случае необходимости)
textLine = self.umountSleepPath(defaultPath)
if not textLine:
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
textLine = self.mountSambaRes(userName,userPwd,uid,gid,
"unix",defaultPath)
if not (textLine == None):
self.printERROR(_("Can not mount Samba resource [%s]")\
%"unix" + " ...")
# Отмонтируем пользовательские ресурсы в случае ошибки
self.errorAndUnmountUserRes = True
return False
if replOn:
if replErrorMount or replErrorSync:
self.createUserFile(logOutFile,"ERROR", uid, gid)
else:
self.createUserFile(logOutFile,"SUCCESS", uid, gid)
else:
self.createUserFile(logOutFile,"SUCCESS", uid, gid)
self.printSUCCESS(_("Mounted user resource of the domain"))
self.printOK(_("Get a user profile from the domain") + " ...")
return True
def mountSleepRes(self,userName,userPwd,uid,gid,res,path):
"""Монтирует ресурс при неудаче задержка потом повторное монитрование"""
textLine = self.mountSambaRes(userName,userPwd,uid,gid,res,path)
if not (textLine == None):
# Проверяем на монтирование директории
if self.isMount(path, 'cifs'):
textLineUmount = self.umountSleepPath(path)
if not textLineUmount:
return False
i = 0
while (i<len(self.sleeps) and not (textLine == None)):
# Задержка перед следующей попыткой
time.sleep(self.sleeps[i])
# Монтируем Samba ресурс
textLine = self.mountSambaRes(userName,userPwd,uid,
gid,res,path)
if not (textLine == None):
# Проверяем на монтирование директории
if self.isMount(path, 'cifs'):
textLineUmount = self.umountSleepPath(path)
if not textLineUmount:
return False
i += 1
return textLine
def copyFilesToMovie(self, userHome):
"""Переносим файлы пользователя в Home/Moved"""
# Находим директории и файлы в домащней директории
pathProg = os.getcwd()
os.chdir(userHome)
dirs = []
files = []
movedLink = os.path.join('Moved')
movedPath = os.path.join('Home',"Moved")
filesAndDir = filter(lambda x: not ('.' in x[0] or x in\
['Disks','Home','Moved','FTP','Desktop'] or os.path.islink(x)),
os.listdir('.'))
filesDir = []
for fd in filesAndDir:
if os.path.islink(fd):
os.unlink(fd)
else:
filesDir.append(fd)
# Нахождение файлов отличных от иконок и ссылок в Desktop
pathDesktop = './Desktop'
filesDirDesk = []
if os.path.exists(pathDesktop):
filesDirDesk = filter(lambda x: not os.path.islink(x) and\
not x.rpartition('.')[2]=='desktop',\
map(lambda x: os.path.join(pathDesktop,x),\
os.listdir(pathDesktop)))
movedPathDesk = os.path.join(movedPath, pathDesktop)
filesDir += filesDirDesk
if not filesDir or not os.path.exists("Home"):
# Удаляем пустую папку Moved
if os.path.exists(movedPath) and not os.listdir(movedPath):
os.rmdir(movedPath)
# Удалям ссылку на Moved в домашней директории
if os.path.islink(movedLink) and not os.path.exists(movedPath):
os.unlink(movedLink)
return True
if not os.path.exists(movedPath):
os.mkdir(movedPath)
directFile = os.path.join(movedPath,".directory")
if not os.path.exists(directFile):
txt = "[Desktop Entry]\nIcon=folder-development"
fd = os.open(directFile, os.O_CREAT)
os.close(fd)
FD = open (directFile, "r+")
FD.write(txt)
FD.close()
if not os.path.exists(movedLink):
os.symlink(movedPath, movedLink)
for fd in filesDir:
execStr = "cp -r '%s' '%s'" %(fd, movedPath)
textLine = self.execProg(execStr)
if not (textLine == None):
self.printERROR(_("Can not exec") + " " + str(execStr) +\
" ...")
return False
execStr = "rm -rf '%s'" %fd
textLine = self.execProg(execStr)
if not (textLine == None):
self.printERROR(_("Can not exec") + " " + str(execStr) +\
" ...")
return False
os.chdir(pathProg)
return True
def moveHomeDir(self, userHome):
"""Переносим файлы пользователя в директорию Home/Moved если в домене"""
if self.isDomain():
return self.copyFilesToMovie(userHome)
return True
def createUserFile(self, fileName, fileTxt, uid, gid, mode=0644):
"""Создает пользовательский файл с содержимым
Если директория файла не существует то ошибка
"""
userDir = os.path.split(fileName)[0]
if not os.path.exists(userDir):
self.printERROR(_("Path %s not exists")