|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
# Copyright 2008-2016 Mir Calculate. 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 sys
|
|
|
|
|
from os import path
|
|
|
|
|
import re
|
|
|
|
|
from calculate.lib.datavars import (Variable, VariableError, ReadonlyVariable,
|
|
|
|
|
ReadonlyTableVariable, FieldValue,
|
|
|
|
|
HumanReadable)
|
|
|
|
|
from calculate.lib.cl_ini_parser import iniParser
|
|
|
|
|
from calculate.lib.configparser import ConfigParser
|
|
|
|
|
import calculate.lib.utils.device as device
|
|
|
|
|
from calculate.lib.utils.files import (readFile, find,
|
|
|
|
|
FindFileType)
|
|
|
|
|
from calculate.lib.utils.mount import isMount
|
|
|
|
|
from calculate.lib.utils.common import getValueFromCmdLine, CmdlineParams
|
|
|
|
|
from calculate.lib.utils.portage import isPkgInstalled
|
|
|
|
|
from calculate.lib.variables import user
|
|
|
|
|
from calculate.lib.convertenv import convertEnv
|
|
|
|
|
from calculate.lib.utils.ip import isOpenPort
|
|
|
|
|
import time
|
|
|
|
|
import ldap
|
|
|
|
|
from socket import gethostbyname
|
|
|
|
|
from calculate.lib.cl_ldap import ldapUser
|
|
|
|
|
from calculate.lib.variables.user import LdapHelper
|
|
|
|
|
import pwd
|
|
|
|
|
from ..client import Client
|
|
|
|
|
from ..rsync import ProfileSyncer, ProfileSyncerError
|
|
|
|
|
|
|
|
|
|
from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate
|
|
|
|
|
|
|
|
|
|
_ = lambda x: x
|
|
|
|
|
setLocalTranslate('cl_client3', sys.modules[__name__])
|
|
|
|
|
__ = getLazyLocalTranslate(_)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClRemoteHost(Variable):
|
|
|
|
|
"""
|
|
|
|
|
IP or domain name of CDS
|
|
|
|
|
"""
|
|
|
|
|
value = ""
|
|
|
|
|
|
|
|
|
|
class VariableClRemoteDomain(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Имя домена
|
|
|
|
|
"""
|
|
|
|
|
value = ""
|
|
|
|
|
|
|
|
|
|
class VariableClRemoteHostNew(Variable):
|
|
|
|
|
"""
|
|
|
|
|
IP or domain name of CDS
|
|
|
|
|
"""
|
|
|
|
|
opt = ["cl_remote_host_new"]
|
|
|
|
|
metavalue = "DOMAIN"
|
|
|
|
|
type = "choiceedit"
|
|
|
|
|
value = ""
|
|
|
|
|
untrusted = True
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("Domain IP")
|
|
|
|
|
self.help = _("domain")
|
|
|
|
|
|
|
|
|
|
def check(self, value):
|
|
|
|
|
if self.Get('cl_client_mount_set') == 'off':
|
|
|
|
|
if self.Get('cl_localhost_set') == 'off':
|
|
|
|
|
if self.Get('cl_remote_host') == '':
|
|
|
|
|
if not value:
|
|
|
|
|
raise VariableError(_("Please specify the domain"))
|
|
|
|
|
elif not isOpenPort(value, 445):
|
|
|
|
|
raise VariableError(
|
|
|
|
|
_("The specified address is not available"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClRemoteHostLive(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Remote host from /proc/cmdline param domain
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return getValueFromCmdLine(CmdlineParams.Calculate,
|
|
|
|
|
CmdlineParams.Domain) or ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableOsRemoteAuth(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Client work mode (local or hostname)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableOsRemoteClient(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Version which apply templates
|
|
|
|
|
"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClRemotePw(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Client password
|
|
|
|
|
"""
|
|
|
|
|
type = "onepassword"
|
|
|
|
|
opt = ["--domain-password"]
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = __("Domain password")
|
|
|
|
|
self.help = _("specify the domain password")
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return getValueFromCmdLine(CmdlineParams.Calculate,
|
|
|
|
|
CmdlineParams.DomainPassword) or ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClMovedSkipPath(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Skip "Moved" path
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
value = ['Disks', 'Home', 'Moved', 'FTP', 'Desktop', 'Share']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClSyncSkipPath(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Skip sync path
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
value = [".googleearth", "Home", "Disks", "FTP",
|
|
|
|
|
'Share', ".local/share/akonadi/db_data", ".VirtualBox",
|
|
|
|
|
".mozilla/firefox/calculate.default/urlclassifier3.sqlite",
|
|
|
|
|
".local/share/mime/mime.cache", ".gvfs",
|
|
|
|
|
".kde4/share/apps/nepomuk/repository/main/data", ".logout",
|
|
|
|
|
".Xauthority", ".thumbnails", ".mozilla/firefox/*/Cache",
|
|
|
|
|
".kde4/socket-*", ".cache/", ".local/share/Trash"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClSyncDelPath(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Removed path on sync
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
value = [".kde4/share/config/phonondevicesrc",
|
|
|
|
|
".kde4/cache-*", ".kde4/tmp-*"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClProfileAllSet(Variable):
|
|
|
|
|
type = "bool"
|
|
|
|
|
value = "off"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientSync(Variable):
|
|
|
|
|
type = "bool"
|
|
|
|
|
value = "on"
|
|
|
|
|
metavalue = "ON/OFF"
|
|
|
|
|
|
|
|
|
|
opt = ["--sync"]
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("Synchronize the user profile")
|
|
|
|
|
self.help = _("synchronize user preferences")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClLocalhostSet(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Using autopartition
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
element = "radio"
|
|
|
|
|
value = "off"
|
|
|
|
|
opt = ["-r"]
|
|
|
|
|
metavalue = "ON/OFF"
|
|
|
|
|
untrusted = True
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("Workstation role")
|
|
|
|
|
self.help = _("remove the domain connection settings")
|
|
|
|
|
|
|
|
|
|
def choice(self):
|
|
|
|
|
return [("off", _("Domain workstation")),
|
|
|
|
|
("on", _("Local workstation"))]
|
|
|
|
|
|
|
|
|
|
def check(self, value):
|
|
|
|
|
if self.Get('cl_client_mount_set') == 'off':
|
|
|
|
|
if self.Get('cl_remote_host') == '' and value == "on":
|
|
|
|
|
raise VariableError(_("The workstation is not in the domain"))
|
|
|
|
|
if self.Get('cl_remote_host') != '' and value == "off":
|
|
|
|
|
raise VariableError(
|
|
|
|
|
_("The workstation is already in the domain %s")
|
|
|
|
|
% self.Get('cl_remote_host') + "\n" +
|
|
|
|
|
_("Before joining the domain, "
|
|
|
|
|
"you need to remove it from the previous domain"))
|
|
|
|
|
|
|
|
|
|
# def get(self):
|
|
|
|
|
# if self.Get('cl_remote_host') == '':
|
|
|
|
|
# return "on"
|
|
|
|
|
# else:
|
|
|
|
|
# return "off"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientMountSet(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Mount remote by configured domain information
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
value = "off"
|
|
|
|
|
metavalue = "ON/OFF"
|
|
|
|
|
opt = ["--mount"]
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("Only mount the domain resource")
|
|
|
|
|
self.help = _("only mount the [remote] domain resource")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableUrUserPw(Variable, LdapHelper):
|
|
|
|
|
"""
|
|
|
|
|
Current user password
|
|
|
|
|
"""
|
|
|
|
|
type = "need-onepassword"
|
|
|
|
|
opt = ["--old-password"]
|
|
|
|
|
metavalue = "OLDPASSWORD"
|
|
|
|
|
value = ""
|
|
|
|
|
untrusted = True
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("Current password")
|
|
|
|
|
self.help = _("current user password")
|
|
|
|
|
|
|
|
|
|
def checkUserPwdLDAP(self, server, userDN, password):
|
|
|
|
|
"""Check unix user password on server"""
|
|
|
|
|
ldapInit = ldap.initialize("ldap://%s" % server)
|
|
|
|
|
try:
|
|
|
|
|
ldapInit.bind_s(userDN, password)
|
|
|
|
|
except ldap.INVALID_CREDENTIALS:
|
|
|
|
|
raise VariableError(_("Wrong password"))
|
|
|
|
|
except ldap.LDAPError as e:
|
|
|
|
|
raise VariableError(f"{e.args[0]['desc']}; {e.args[0]['info']}")
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def check(self, value):
|
|
|
|
|
if not value:
|
|
|
|
|
raise VariableError(_("Empty password"))
|
|
|
|
|
# читаем os_remote_auth, так как при смене пароля
|
|
|
|
|
# чтение должно выполняться от пользователя,
|
|
|
|
|
# cl_remote_host не может быть прочитан пользователем
|
|
|
|
|
server = self.Get('os_remote_auth')
|
|
|
|
|
ldapObj = self.getLdapUserObject()
|
|
|
|
|
if ldapObj:
|
|
|
|
|
usersDN = ldapObj.getUsersDN()
|
|
|
|
|
userDN = ldapObj.addDN("uid=%s" % self.Get('ur_login'),
|
|
|
|
|
usersDN)
|
|
|
|
|
self.checkUserPwdLDAP(server, userDN, value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableUrUserNewPw(Variable):
|
|
|
|
|
"""
|
|
|
|
|
New user password
|
|
|
|
|
"""
|
|
|
|
|
type = "need-password"
|
|
|
|
|
opt = ["--new-password"]
|
|
|
|
|
metavalue = "NEWPASSWORD"
|
|
|
|
|
value = ""
|
|
|
|
|
untrusted = True
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("New password")
|
|
|
|
|
self.help = _("new user password")
|
|
|
|
|
|
|
|
|
|
def check(self, value):
|
|
|
|
|
if not value:
|
|
|
|
|
raise VariableError(_("Empty password"))
|
|
|
|
|
if len(value) < 8:
|
|
|
|
|
raise VariableError(_("Password should contain at least 8 symbols"))
|
|
|
|
|
elif not any([x.isupper() for x in value]):
|
|
|
|
|
raise VariableError(_("Password should contain at least 1 uppercase letter"))
|
|
|
|
|
elif not any([x.islower() for x in value]):
|
|
|
|
|
raise VariableError(_("Password should contain at least 1 lowercase letter"))
|
|
|
|
|
elif not any([x.isdigit() for x in value]):
|
|
|
|
|
raise VariableError(_("Password should contain at least 1 digit"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientLogin(user.VariableUrLogin):
|
|
|
|
|
"""
|
|
|
|
|
User Login
|
|
|
|
|
"""
|
|
|
|
|
opt = ["cl_client_login"]
|
|
|
|
|
alias = "ur_login"
|
|
|
|
|
|
|
|
|
|
def choice(self):
|
|
|
|
|
loginChoice = user.VariableUrLogin.choice(self)
|
|
|
|
|
if self.Get('cl_action') == 'passwd':
|
|
|
|
|
return [x for x in loginChoice if x != "root"]
|
|
|
|
|
else:
|
|
|
|
|
return loginChoice
|
|
|
|
|
|
|
|
|
|
def check(self, value):
|
|
|
|
|
"""Does user exist"""
|
|
|
|
|
if not value in self.choice() and self.Get('cl_action') == 'logout':
|
|
|
|
|
raise VariableError(_("X session users not found"))
|
|
|
|
|
if value == "":
|
|
|
|
|
raise VariableError(_("Please specify the user"))
|
|
|
|
|
if value == "root" and self.Get('cl_action') == 'passwd':
|
|
|
|
|
raise VariableError(
|
|
|
|
|
_("This action can be executed by a non-root user only"))
|
|
|
|
|
try:
|
|
|
|
|
pwd.getpwnam(value).pw_gid
|
|
|
|
|
except (TypeError, KeyError):
|
|
|
|
|
raise VariableError(_("User %s does not exist") % value)
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
if (self.Get('cl_action') == 'passwd' and
|
|
|
|
|
self.Get('ur_login') != 'root'):
|
|
|
|
|
return self.Get('ur_login')
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientRelevanceSet(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Актуальны ли сейчас выполненные шаблоны
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
# если происходят действия ввода или вывода из домена
|
|
|
|
|
if (self.Get('cl_action') in ("domain", "undomain") and
|
|
|
|
|
self.Get('cl_client_mount_set') == 'off'):
|
|
|
|
|
return "off"
|
|
|
|
|
# если изменился домен
|
|
|
|
|
if self.Get('cl_remote_host') != self.Get("os_remote_auth"):
|
|
|
|
|
return "off"
|
|
|
|
|
if (self.Get('cl_remote_host') and
|
|
|
|
|
not isMount(self.Get('cl_client_remote_path'))):
|
|
|
|
|
return "off"
|
|
|
|
|
return "on"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientRemotePath(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Путь для монитрования //domain/remote
|
|
|
|
|
"""
|
|
|
|
|
value = "/var/calculate/remote"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientProfileName(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Название удаленного профиля (CLD,CLDX,all)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return ("all" if self.Get('cl_profile_all_set') == 'on'
|
|
|
|
|
else self.Get('os_linux_shortname'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClLdapData(ldapUser, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Внутренняя переменная, содержащая объект для доступа к данным LDAP
|
|
|
|
|
"""
|
|
|
|
|
type = "object"
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def getReplDN(self):
|
|
|
|
|
"""
|
|
|
|
|
Получить из LDAP домен на котором находится актуальный профиль
|
|
|
|
|
Get from LDAP last domain server
|
|
|
|
|
|
|
|
|
|
Branch has ((username,systemname,host))
|
|
|
|
|
"""
|
|
|
|
|
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):
|
|
|
|
|
"""Find server which user use"""
|
|
|
|
|
connectData = self.getBindConnectData()
|
|
|
|
|
if connectData:
|
|
|
|
|
bindDn, bindPw, host = connectData
|
|
|
|
|
replDN = self.getReplDN()
|
|
|
|
|
# find string for service replication branch
|
|
|
|
|
userAndOsName = "%s@%s" % (userName, osLinuxShort)
|
|
|
|
|
findAttr = "uid=%s" % userAndOsName
|
|
|
|
|
# connect to LDAP
|
|
|
|
|
if not self.ldapConnect(bindDn, bindPw, host):
|
|
|
|
|
return False
|
|
|
|
|
resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL,
|
|
|
|
|
findAttr, ["host"])
|
|
|
|
|
return resSearch
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _gethostbyname(self, hostname):
|
|
|
|
|
try:
|
|
|
|
|
return gethostbyname(hostname)
|
|
|
|
|
except Exception:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def getNameRemoteServer(self, userName, osLinuxShort, curHost):
|
|
|
|
|
"""
|
|
|
|
|
Get remote domain hostname or empty if profile is keeped on
|
|
|
|
|
current server
|
|
|
|
|
"""
|
|
|
|
|
searchPrevHost = self.searchPrevHost(userName, osLinuxShort)
|
|
|
|
|
if searchPrevHost and 'host' in searchPrevHost[0][0][1]:
|
|
|
|
|
prevHost = searchPrevHost[0][0][1]['host'][0]
|
|
|
|
|
else:
|
|
|
|
|
prevHost = None
|
|
|
|
|
# get ip address of previous server and current server
|
|
|
|
|
prevIp = self._gethostbyname(prevHost)
|
|
|
|
|
curIp = self._gethostbyname(curHost)
|
|
|
|
|
# if actual profile not found or on local
|
|
|
|
|
if not prevHost or curIp and prevIp == curIp:
|
|
|
|
|
return ""
|
|
|
|
|
else:
|
|
|
|
|
return prevHost
|
|
|
|
|
|
|
|
|
|
def isRepl(self):
|
|
|
|
|
"""
|
|
|
|
|
Is on or off replication on server
|
|
|
|
|
"""
|
|
|
|
|
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"
|
|
|
|
|
# connect to 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 VariableClReplicationHost(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Удаленный сервер при репликации, который содержит актуальный профиль
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self.Get('cl_ldap_data').getNameRemoteServer(
|
|
|
|
|
self.Get('ur_login'), self.Get('cl_client_profile_name'),
|
|
|
|
|
self.Get('cl_remote_host'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientUserMountData(ReadonlyTableVariable):
|
|
|
|
|
"""
|
|
|
|
|
Таблица монтирования ресурсов
|
|
|
|
|
"""
|
|
|
|
|
source = ['cl_client_user_mount_name',
|
|
|
|
|
'cl_client_user_mount_resource',
|
|
|
|
|
'cl_client_user_mount_path',
|
|
|
|
|
'cl_client_user_mount_host']
|
|
|
|
|
|
|
|
|
|
def get(self, hr=HumanReadable.No):
|
|
|
|
|
home = path.split(self.Get('ur_home_path'))[0]
|
|
|
|
|
envFile = self.Get('cl_env_server_path')
|
|
|
|
|
samba_host = self.Get('sr_samba_host')
|
|
|
|
|
ftp_host = convertEnv().getVar("ftp", "host")
|
|
|
|
|
|
|
|
|
|
def generate():
|
|
|
|
|
yield (
|
|
|
|
|
"share", "share", path.join(self.Get('ur_home_path'), "Share"),
|
|
|
|
|
samba_host)
|
|
|
|
|
yield (
|
|
|
|
|
"unix", "unix", path.join(home, ".%s" % self.Get('ur_login'),
|
|
|
|
|
"profile"),
|
|
|
|
|
samba_host)
|
|
|
|
|
yield (
|
|
|
|
|
"homes", "homes", path.join(self.Get('ur_home_path'), "Home"),
|
|
|
|
|
samba_host)
|
|
|
|
|
if ftp_host:
|
|
|
|
|
yield ("ftp", "ftp", path.join(self.Get('ur_home_path'), "FTP"),
|
|
|
|
|
ftp_host)
|
|
|
|
|
else:
|
|
|
|
|
yield ("ftp", '', '', '')
|
|
|
|
|
if self.Get('cl_replication_host'):
|
|
|
|
|
yield ("remote_profile", "unix",
|
|
|
|
|
path.join(home, ".%s" % self.Get('ur_login'),
|
|
|
|
|
"remote_profile"),
|
|
|
|
|
self.Get('cl_replication_host'))
|
|
|
|
|
else:
|
|
|
|
|
yield ("remote_profile", 'unix', '', '')
|
|
|
|
|
|
|
|
|
|
return list(generate())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientUserMountUnixPath(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Путь до подключенного unix ресурса данного пользователя
|
|
|
|
|
"""
|
|
|
|
|
def get(self):
|
|
|
|
|
return self.select('cl_client_user_mount_path',
|
|
|
|
|
cl_client_user_mount_name='unix', limit=1) or ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientUserMountName(FieldValue, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Название удаленного ресурса
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
source_variable = "cl_client_user_mount_data"
|
|
|
|
|
column = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientUserMountResource(FieldValue, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Название удаленного ресурса
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
source_variable = "cl_client_user_mount_data"
|
|
|
|
|
column = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientUserMountPath(FieldValue, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Путь подключения удаленного ресурса
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
source_variable = "cl_client_user_mount_data"
|
|
|
|
|
column = 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientUserMountHost(FieldValue, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Удаленный сервер
|
|
|
|
|
"""
|
|
|
|
|
type = "list"
|
|
|
|
|
source_variable = "cl_client_user_mount_data"
|
|
|
|
|
column = 3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SyncHelper():
|
|
|
|
|
"""
|
|
|
|
|
Вспомогательный объект для определения статуса синхронизации и времени
|
|
|
|
|
по конфигурационным файлам
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def getSyncStatus(self, rpath):
|
|
|
|
|
"""
|
|
|
|
|
Получить status_sync из desktop файла
|
|
|
|
|
"""
|
|
|
|
|
fileConfig = path.join(rpath, Client.configFileDesktop)
|
|
|
|
|
# get data from desktop config on success run get by archive
|
|
|
|
|
if os.path.exists(fileConfig):
|
|
|
|
|
objConfig = iniParser(fileConfig)
|
|
|
|
|
data = self.getDataInConfig("main", ["status_sync"],
|
|
|
|
|
objConfig)
|
|
|
|
|
if data:
|
|
|
|
|
return data.get("status_sync", "")
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
def getDataInConfig(self, section, listVars, objConfig):
|
|
|
|
|
"""
|
|
|
|
|
Прочитать список переменных из области конфигурационного файла
|
|
|
|
|
"""
|
|
|
|
|
varsConfig = {}
|
|
|
|
|
for varName in listVars:
|
|
|
|
|
varsConfig[varName] = objConfig.getVar(section, varName)
|
|
|
|
|
if objConfig.getError():
|
|
|
|
|
return False
|
|
|
|
|
return varsConfig
|
|
|
|
|
|
|
|
|
|
def convertDate(self, strdate, dateformat="%Y-%m-%d %H:%M:%S"):
|
|
|
|
|
"""
|
|
|
|
|
Convert date from string format (dateformat) to stuct or None
|
|
|
|
|
"""
|
|
|
|
|
if strdate:
|
|
|
|
|
try:
|
|
|
|
|
return time.strptime(strdate, dateformat)
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
def getDateObjClientConf(self, rpath):
|
|
|
|
|
"""
|
|
|
|
|
Получить время синхронизации из .calculate/desktop.env
|
|
|
|
|
"""
|
|
|
|
|
fileConfig = path.join(rpath, Client.configFileDesktop)
|
|
|
|
|
if os.path.exists(fileConfig):
|
|
|
|
|
objConfig = iniParser(fileConfig)
|
|
|
|
|
data = self.getDataInConfig("main", ["date", "date_logout"],
|
|
|
|
|
objConfig)
|
|
|
|
|
timeLogout = data["date_logout"]
|
|
|
|
|
timeConfig = data["date"]
|
|
|
|
|
dates = [x for x in [self.convertDate(timeLogout),
|
|
|
|
|
self.convertDate(timeConfig)] if x]
|
|
|
|
|
if dates:
|
|
|
|
|
return dates[0]
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
def checkNeedSync(self, homeDir, rpath, curTimeObj, curStatusSync,
|
|
|
|
|
osLinuxShort):
|
|
|
|
|
"""
|
|
|
|
|
Проверить необходимость синхронизации текущего профиля с удаленным
|
|
|
|
|
"""
|
|
|
|
|
# profile directory
|
|
|
|
|
# fileConfig = os.path.join(homeDir, Client.configFileServer)
|
|
|
|
|
pathProfile = os.path.join(rpath, osLinuxShort)
|
|
|
|
|
# if readFile(fileConfig).strip():
|
|
|
|
|
# return True
|
|
|
|
|
fileSoftConfigThis = os.path.join(pathProfile,
|
|
|
|
|
Client.configFileSoft)
|
|
|
|
|
fileSoftConfigCur = os.path.join(homeDir,
|
|
|
|
|
Client.configFileSoft)
|
|
|
|
|
xSessionCur = iniParser(fileSoftConfigCur).getVar('main', 'xsession')
|
|
|
|
|
xSessionThis = iniParser(fileSoftConfigThis).getVar('main', 'xsession')
|
|
|
|
|
# check profile date on current server
|
|
|
|
|
# fileConfigThis = os.path.join(pathProfile, Client.configFileDesktop)
|
|
|
|
|
# if iniParser(fileConfigThis).getVar('main','status_sync') == "success":
|
|
|
|
|
# self.setVarToConfig("main", {"status_sync":"success_mount"},
|
|
|
|
|
# fileConfigThis)
|
|
|
|
|
thisTimeObj = self.getDateObjClientConf(pathProfile)
|
|
|
|
|
if curStatusSync == "success_logout" and \
|
|
|
|
|
xSessionCur == xSessionThis and \
|
|
|
|
|
thisTimeObj and curTimeObj and \
|
|
|
|
|
curTimeObj >= thisTimeObj:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def getSyncDate(self, osLinuxShort, ps):
|
|
|
|
|
"""
|
|
|
|
|
Получить время синхронизации из ::profile/.calculate/desktop.env
|
|
|
|
|
"""
|
|
|
|
|
desktopEnvRemoteData = ps.readfile("::profile/{}/{}".format(
|
|
|
|
|
osLinuxShort, Client.configFileDesktop))
|
|
|
|
|
if desktopEnvRemoteData:
|
|
|
|
|
cpRemote = ConfigParser(strict=False)
|
|
|
|
|
cpRemote.read_string(desktopEnvRemoteData)
|
|
|
|
|
timeLogout = cpRemote.get("main", "date_logout", fallback=None)
|
|
|
|
|
timeConfig = cpRemote.get("main", "date", fallback=None)
|
|
|
|
|
dates = [x for x in [self.convertDate(timeLogout),
|
|
|
|
|
self.convertDate(timeConfig)] if x]
|
|
|
|
|
if dates:
|
|
|
|
|
return dates[0]
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def checkNeedSyncNew(self, homeDir, rpath, curTimeObj, curStatusSync,
|
|
|
|
|
osLinuxShort, ps):
|
|
|
|
|
"""
|
|
|
|
|
Проверить необходимость синхронизации текущего профиля с удаленным
|
|
|
|
|
"""
|
|
|
|
|
iniEnvRemoteData = ps.readfile("::profile/{}/{}".format(
|
|
|
|
|
osLinuxShort, Client.configFileSoft))
|
|
|
|
|
fileConfigCur = os.path.join(homeDir, Client.configFileSoft)
|
|
|
|
|
iniEnvCurrentData = readFile(fileConfigCur)
|
|
|
|
|
cpCur = ConfigParser(strict=False)
|
|
|
|
|
cpCur.read_string(iniEnvCurrentData)
|
|
|
|
|
cpRemote = ConfigParser(strict=False)
|
|
|
|
|
cpRemote.read_string(iniEnvRemoteData)
|
|
|
|
|
|
|
|
|
|
xSessionCur = cpCur.get('main', 'xsession', fallback=None)
|
|
|
|
|
xSessionThis = cpRemote.get('main', 'xsession', fallback=None)
|
|
|
|
|
if curStatusSync == "success_logout" and \
|
|
|
|
|
xSessionCur == xSessionThis:
|
|
|
|
|
thisTimeObj = self.getSyncDate(osLinuxShort, ps)
|
|
|
|
|
if thisTimeObj and curTimeObj and \
|
|
|
|
|
curTimeObj >= thisTimeObj:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientSyncTime(SyncHelper, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Текущее время синхронизации профиля
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self.getDateObjClientConf(self.Get('ur_home_path'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientPackTime(SyncHelper, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Время комады упаковки профиля
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return str(float(time.time()))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientSyncStatus(SyncHelper, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Текущий статус синхронизации профиля
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self.getSyncStatus(self.Get('ur_home_path'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientLocalSyncTime(SyncHelper, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Текущий статус синхронизации профиля
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self.getDateObjClientConf(
|
|
|
|
|
path.join(
|
|
|
|
|
self.Select('cl_client_user_mount_path',
|
|
|
|
|
where='cl_client_user_mount_name', eq='unix',
|
|
|
|
|
limit=1), self.Get('cl_client_profile_name')))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientRsyncProfileSet(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Используется rsync через ssh для синхронизации пользовательского профиля
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
value = "off"
|
|
|
|
|
|
|
|
|
|
class VariableClClientSyncReplicationSet(SyncHelper, ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Нужно ли синхронизировать текущий профиль с удаленным доменом
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
|
|
|
|
|
host_varname = "cl_replication_host"
|
|
|
|
|
profile_type = "remote_profile"
|
|
|
|
|
|
|
|
|
|
def old_synchronize(self):
|
|
|
|
|
profilePath = self.Select('cl_client_user_mount_path',
|
|
|
|
|
where='cl_client_user_mount_name',
|
|
|
|
|
eq=self.profile_type, limit=1)
|
|
|
|
|
if self.Get('cl_action') == 'login' and not isMount(profilePath):
|
|
|
|
|
raise VariableError(_("Remote profile not mounted"))
|
|
|
|
|
return "on" if self.checkNeedSync(self.Get('ur_home_path'), profilePath,
|
|
|
|
|
self.Get('cl_client_sync_time'),
|
|
|
|
|
self.Get('cl_client_sync_status'),
|
|
|
|
|
self.Get(
|
|
|
|
|
'cl_client_profile_name')) else "off"
|
|
|
|
|
|
|
|
|
|
def new_synchronize(self):
|
|
|
|
|
user = self.Get('ur_login')
|
|
|
|
|
pwd = self.Get('desktop.ur_password')
|
|
|
|
|
remotehost = self.Get(self.host_varname)
|
|
|
|
|
ps = ProfileSyncer(remotehost, 2009, user, pwd)
|
|
|
|
|
|
|
|
|
|
#there was a typo before - profilePath was missing
|
|
|
|
|
profilePath = self.Select('cl_client_user_mount_path',
|
|
|
|
|
where='cl_client_user_mount_name',
|
|
|
|
|
eq=self.profile_type, limit=1)
|
|
|
|
|
|
|
|
|
|
if ps.check():
|
|
|
|
|
return "on" if self.checkNeedSyncNew(
|
|
|
|
|
self.Get('ur_home_path'), profilePath,
|
|
|
|
|
self.Get('cl_client_sync_time'),
|
|
|
|
|
self.Get('cl_client_sync_status'),
|
|
|
|
|
self.Get('cl_client_profile_name'),
|
|
|
|
|
ps) else "off"
|
|
|
|
|
return "off"
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
if not self.Get(self.host_varname):
|
|
|
|
|
return "off"
|
|
|
|
|
|
|
|
|
|
if self.GetBool('cl_client_rsync_profile_set'):
|
|
|
|
|
return self.new_synchronize()
|
|
|
|
|
else:
|
|
|
|
|
return self.old_synchronize()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientSyncLocalSet(VariableClClientSyncReplicationSet):
|
|
|
|
|
"""
|
|
|
|
|
Нужно ли синхронизировать текущий профиль с локальным доменом
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
|
|
|
|
|
host_varname = "cl_remote_host"
|
|
|
|
|
profile_type = "unix"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientSymlinks(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Список симлинков в пользовательском профиле
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
skipFiles = (self.Get('cl_sync_del_path') +
|
|
|
|
|
self.Get('cl_sync_skip_path'))
|
|
|
|
|
reSkip = re.compile("|".join((x.replace("*", ".*") for x in skipFiles))).search
|
|
|
|
|
return [x for x in find(self.Get('ur_home_path'), onefilesystem=True,
|
|
|
|
|
filetype=FindFileType.SymbolicLink)
|
|
|
|
|
if not reSkip(x)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClClientNscdCache(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Частота обновления кэша nscd при работе в домене в часах
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClCifsVer(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Версия модуля CIFS
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return device.sysfs.read(device.sysfs.Path.Module, "cifs/version")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClCifsCache(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Параметр cache= при монтировании cifs
|
|
|
|
|
"""
|
|
|
|
|
value = "loose"
|
|
|
|
|
|
|
|
|
|
class VariableClCifsMountVers(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Параметр vers= для cifs
|
|
|
|
|
"""
|
|
|
|
|
value = "1.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableClRsyncVer(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Версия rsync
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
data = isPkgInstalled('net-misc/rsync')
|
|
|
|
|
if data:
|
|
|
|
|
return data[0]['PVR']
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
class VariableClCifsutilsVer(ReadonlyVariable):
|
|
|
|
|
"""
|
|
|
|
|
Версия cifs-utils
|
|
|
|
|
"""
|
|
|
|
|
def get(self):
|
|
|
|
|
data = isPkgInstalled('net-fs/cifs-utils')
|
|
|
|
|
if data:
|
|
|
|
|
return data[0]['PV']
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
class VariableClClientIgnoreErrorsSet(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Параметр, для отключения отмонтирования пользовательских ресурсов,
|
|
|
|
|
при ошбиках, возникших во вермя cl-client-sync-login
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
value = "on"
|
|
|
|
|
metavalue = "ON/OFF"
|
|
|
|
|
|
|
|
|
|
opt = ["--unmount-on-error"]
|
|
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
|
self.label = _("Unmount user resources on error")
|
|
|
|
|
self.help = _("unmount user resources on error")
|
|
|
|
|
|
|
|
|
|
class VariableClSyncMovedSet(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Использовать или нет перенос файлов из домашней директории в Home/Moved при
|
|
|
|
|
синхронизации
|
|
|
|
|
"""
|
|
|
|
|
type = "bool"
|
|
|
|
|
value = "on"
|
|
|
|
|
|
|
|
|
|
class VariableSrSambaHost(Variable):
|
|
|
|
|
"""
|
|
|
|
|
Хост на котором находятся samba ресурсы
|
|
|
|
|
"""
|
|
|
|
|
def get(self):
|
|
|
|
|
return self.Get('cl_remote_host') or ''
|