reorganization of the library

master3.3
Самоукин Алексей 14 years ago
parent 3dd26c94fe
commit b2a0679a60

@ -4,9 +4,10 @@ INSTALL
-------
calculate-lib needs the following library version installed, in order to run:
Python >= 2.6
python >= 2.5
python-ldap >= 2.0.0
pyxml >= 0.8
py-smbpasswd >= 1.0
To install calculate-lib , just execute the install script 'setup.py'.
Example:

@ -1,624 +0,0 @@
#-*- 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 sys
import cl_utils
from cl_lang import lang
from cl_template import iniParser
from cl_string import columnWrite
# Перевод модуля на другой язык
tr = lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])
class var:
'''Объект "Переменная окружения"'''
# название сервиса которому принадлежит переменная
#(Global, Builder, Client, Server итд)
service = None
# значение переменной
value = ""
# режим записи (атрибут mode)
mode = "r"
# переменная для внутреннего использования (official)
official = False
# количество вызовов метода заполнения
countFill = 0
# объект в котором создан этот объект
parentObj = None
# запускать или нет метод заполнения
fillStart = True
# dynamic = True то переменная динамическая при повтороном запуске
# запускается метод заполнения
# метод заполнения не запускается только если fillStart = False
# (осторожно возможно зацикливание программы если методы заполнения
# переменных используют методы друг друга)
dynamic = False
def __init__(self, parentObj):
# словарь зависимых переменных {имя:значение}
self.dependValues = {}
# тип переменной (атрибут type)
self.type = ('default')
# список допустимых значений переменных (select)
self.select = ()
# объект который создал этот объект
self.parentObj = parentObj
def is_update(self):
#Нужно ли перезапускать метод заполнения (если зависимые переменные
#обновились то нужно)
upd = False
for depVarName in self.dependValues.keys():
value = self.parentObj.__getattribute__(depVarName).Get()
if self.dependValues[depVarName] != value:
self.dependValues[depVarName] =\
self.parentObj.__getattribute__(depVarName).value
upd = True
break
return upd
def Get(self):
"""Получение значения переменной"""
if not self.fillStart:
return self.value
if self.dynamic:
self.value = self.Fill()
return self.value
if not self.value:
if self.countFill>0:
return self.value
self.countFill += 1
self.value = self.Fill()
if self.dependValues and self.is_update():
self.countFill += 1
self.value = self.Fill()
return self.value
def Set(self, value):
"""Запись значения переменной"""
self.value = value
return self.value
def Fill(self):
"""Заполнение переменной в далнейшем заменяем методом заполнения"""
return self.value
class DataVars(object):
class DataVarsError(Exception):
"""Класс ошибок"""
pass
# добавляем пути к модулям если они не добавлены
if not os.path.abspath(\
'/usr/lib/calculate/calculate-server/pym') in sys.path:
sys.path.insert(0,os.path.abspath(\
'/usr/lib/calculate/calculate-server/pym'))
if not os.path.abspath(\
'/usr/lib/calculate/calculate-lib/pym') in sys.path:
sys.path.insert(0,os.path.abspath(\
'/usr/lib/calculate/calculate-lib/pym'))
if not os.path.abspath(\
'/usr/lib/calculate/calculate-builder/pym') in sys.path:
sys.path.insert(0,os.path.abspath(\
'/usr/lib/calculate/calculate-builder/pym'))
# Импортируемые модули - (раздел: модуль переменных, модуль заполнения
#переменных)
__modlist={'Global':('cl_vars','cl_fill'),
'Server':('cl_vars_server','cl_fill_server'),
'Builder':('cl_vars_builder','cl_fill_builder'),
'Client':('cl_vars_client','cl_fill_client'),
}
def __init__(self):
#self.t1 = fillVars()
#self.t1.Get = self.Get
#self.t1.Set = self.Set
# Для нахождения зависимостей переменных
self.__levelNumber = 0
self.__LevelsVar = []
# Для хранения импортированных модулей и объектов
#[(cекция,импортированный модуль переменных, объект заполнения),]
self._importList = []
self._importData("Global")
def _importData(self, section):
"""Импортирует модули с переменными и модули с функциями заполнения
section секция раздела (Global, Server, Client итд)
создает необходимые структуры данных
"""
if not section in self.__modlist.keys():
raise self.DataVarsError(_("Unsupported section %s")%section)
modVar = self.__modlist[section][0]
modFill = self.__modlist[section][1]
# Импортируем класс описания переменных и класс заполнения
try:
exec ("import %s" % (modVar))
except ImportError, e:
err1 = _("Error in import module %s")%modVar
err2 = _("error") + ": " +str(e)
raise self.DataVarsError("%s\n%s"%(err1,err2))
flagFindFillModule = True
try:
exec ("import %s" % (modFill))
except ImportError, e:
if "No module named" in str(e):
flagFindFillModule = False
else:
err1 = _("Error in import module %s")%modFill
err2 = _("error") + ": " +str(e)
raise self.DataVarsError("%s\n%s"%(err1,err2))
if flagFindFillModule:
# Создаем объект с методами заполнения переменных
exec("fillObj = %s.fillVars()" %modFill)
# Подключаем методы получения и записи переменных
fillObj.Get = self.Get
fillObj.Set = self.Set
else:
fillObj = False
# Заполняем self._importList
exec("self._importList.insert(0,(section,%s,fillObj))"%(modVar))
def __findVarData(self, nameVar):
"""Находит данные для создания объекта переменная в модулях и
объектах
"""
# Ищем переменную в модуле
dataVar = False
e = False
for section, moduleVar, fillobj in self._importList:
try:
exec("dataVar=moduleVar.Data.%s"%nameVar)
except AttributeError, e:
pass
if dataVar:
break
if dataVar == False:
err1 = _("Not found variable %s")%nameVar
err2 = ""
if e:
err2 = _("error") + ": " +str(e)
raise self.DataVarsError("%s\n%s"%(err1,err2))
dataVar['service'] = section
# Ищем метод в объекте методов заполнения
nameMethod = "get_" + nameVar
flagFindMetod = False
for section, moduleVar, fillobj in self._importList:
if fillobj:
if nameMethod in dir(fillobj):
flagFindMetod = True
method = fillobj.__getattribute__(nameMethod)
break
if flagFindMetod:
return (dataVar,method)
else:
return (dataVar,False)
def __setAttributesVar(self, var ,nameVar, dict):
"""Установка аттрибутов для созданного объекта var
название аттрибута и его значение берется из словаря dict
"""
dict['type'] = nameVar.split('_')
if not set(dict.keys()) <= set(dir(var)):
raise self.DataVarsError(\
_("error initalize variable %s, incorrect data")%nameVar)
for nameAttr in dict.keys():
setattr(var,nameAttr, dict[nameAttr])
return True
def __Get(self, nameVar):
ret = ""
self.__LevelsVar.append((self.__levelNumber, nameVar))
self.__levelNumber += 1
#nameMethod = "get_" + nameVar
if self.__dict__.has_key(nameVar):
ret = self.__getattribute__(nameVar).Get()
elif self.__findVarData(nameVar):
dictVar, methodFill =self.__findVarData(nameVar)
varobj = var(self)
# Устанавливаем аттрибуты
self.__setAttributesVar(varobj, nameVar, dictVar)
if methodFill:
varobj.Fill = methodFill
self.__setattr__(nameVar, varobj)
ret = self.__getattribute__(nameVar).Get()
self.__levelNumber -= 1
if self.__levelNumber == 0 and\
self.__getattribute__(nameVar).fillStart and\
len(self.__LevelsVar)>1:
links = self.__getLinks(self.__LevelsVar)
for name in links.keys():
for nameLink in links[name].keys():
val = self.__getattribute__(nameLink).Get()
self.__getattribute__(name).dependValues[nameLink] = val
if self.__levelNumber == 0:
self.__LevelsVar = []
return ret
def Get(self, nameVar):
return self.__Get(nameVar)
def __Set(self, nameVar, value, force=False):
nameMethod = "get_" +nameVar
if not self.__dict__.has_key(nameVar) and self.__findVarData(nameVar):
dictVar, methodFill =self.__findVarData(nameVar)
varobj = var(self)
# Устанавливаем аттрибуты
self.__setAttributesVar(varobj, nameVar, dictVar)
if methodFill:
varobj.Fill = methodFill
self.__setattr__(nameVar, varobj)
if self.__dict__.has_key(nameVar):
if not force and "r" in getattr(self, nameVar).mode:
print _("Attempt to rewrite a variable for reading:%s")\
%nameVar
return False
self.__getattribute__(nameVar).fillStart = False
return self.__getattribute__(nameVar).Set(value)
def Set(self, nameVar, value, force=False):
return self.__Set(nameVar, value, force)
def __frame(self, lVar):
"""получить список областей зависимости переменных"""
data = []
if not lVar:
return data
firstLevel = lVar[0][0]
for level, name in lVar[1:]:
if level> firstLevel:
data.append((level, name))
else:
break
return data
def __getLinks(self, lVar):
"""Получить список переменных и от каких переменных они зависят
на вход список [(уровень рекурсии, название переменной),]
"""
links = {}
frames = {}
levelLinks = {}
lVarFr = lVar
for level, name in lVar:
fr = self.__frame(lVarFr)
if not frames.has_key(name):
frames[name] = fr
levelLinks[name] = level+1
lVarFr = lVarFr[1:]
for name in frames.keys():
level = levelLinks[name]
fr = frames[name]
links[name] = {}
for lv, nm in fr:
if level == lv:
links[name][nm] = ""
return links
def __getPathCalculateIni(self):
"""Получить пути до ini файлов"""
return self.Get('cl_env_path')
def __getSection(self, vname):
"""секция для записи в ini файл переменной
vname - имя переменной
"""
if self.__dict__.has_key(vname):
if self.__dict__[vname].service == 'Global':
return 'calculate'
else:
return self.__dict__[vname].service.lower()
def __writeVarValue(self, vname, val, location, header):
'''Записать значение в calculate.ini
Параметры:
vname имя переменной
val значение переменной
location расположение ini файла ('default', 'local', 'remote')
header раздел ini файла ('client', 'server', 'calculate')
Возвращаемые значение:
True запись успешна
False запись не удалсь
'''
# получаем все пути до ini файлов
calculate_ini = self.__getPathCalculateIni()
# получаем полный путь до файла ini
if location == 'default':
name_calculate_ini = calculate_ini[2]
elif location == 'local':
name_calculate_ini = calculate_ini[1]
elif location == 'remote':
name_calculate_ini = calculate_ini[0]
else:
return False
# извлекаем из полного имени файла путь
onlydir = os.path.split(name_calculate_ini)[0]
try:
# проверяем чтобы путь до ини файла существовал
if not os.path.exists(onlydir):
# создаем его если отсутствует
os.makedirs(onlydir)
except OSError (nerr,msg):
print nerr, msg
return False
config = iniParser(name_calculate_ini)
# Получаем секцию конфигурационного файла
if not header:
header = self.__getSection(vname)
return config.setVar(header,{vname: cl_utils.convertStrListDict(val)})
def __deleteVarValue(self, vname, location, header):
'''Удалить переменную в calculate.ini
Параметры:
vname имя переменной
location расположение ini файла ('default', 'local', 'remote')
Возвращаемые значение:
True удалено успешна
False удаление не удалсь
'''
# получаем все пути до ini файлов
calculate_ini = self.__getPathCalculateIni()
# получаем полный путь до файла ini
if location == 'default':
name_calculate_ini = calculate_ini[2]
elif location == 'local':
name_calculate_ini = calculate_ini[1]
elif location == 'remote':
name_calculate_ini = calculate_ini[0]
else:
return False
# извлекаем из полного имени файла путь
onlydir = os.path.split(name_calculate_ini)[0]
# проверяем чтобы путь до ини файла существовал
if not os.path.exists(onlydir):
return False
config = iniParser(name_calculate_ini)
# Получаем секцию конфигурационного файла
if not header:
header = self.__getSection(vname)
# Удаляем переменную
retDelVar = config.delVar(header, vname)
retDelArea = True
if not config.getAreaVars(header):
retDelArea = config.delArea(header)
if retDelArea and retDelVar:
return True
else:
return False
def Write(self, vname, val, force=False, location='default',header=False):
'''Установить и записать значение переменной в ini файл
Параметры:
vname имя переменной
val значение переменной
force "принудительный режим"
location расположение ini файла ('default', 'local', 'remote')
header раздел ini файла ('client', 'server', 'calculate')
'''
if self.__Set(vname, val, force)!= False:
if not val.strip():
self.__deleteVarValue(vname, location, header)
self.__writeVarValue(vname, val, location, header)
return True
return False
def Delete(self, vname, location='default', header=False):
'''Удалить переменную в calculate.ini
Параметры:
vname имя переменной
location расположение ini файла ('default', 'local', 'remote')
Возвращаемые значение:
True удалено успешна
False удаление не удалсь
'''
return self.__deleteVarValue(vname, location, header)
def __getActiveSections(self):
"""активные секции в ini файле"""
act_section = []
for service,t,t in self._importList:
if service == "Global":
act_section.append('calculate')
else:
act_section.append(service.lower())
return act_section
def flIniFile(self):
'''Заместить значение переменных значениями из ини файлов
Возвращаемые значения:
оварь импортированных переменных - переменные считаны
False - файл не был обнаружен
'''
#Cловарь переменных из ini файлов
importVars = {}
calculate_ini = self.__getPathCalculateIni()
# активные секции (секции из которых будут использованы переменные)
act_section = self.__getActiveSections()
set_act_section = set(act_section)
i = 0
locations = ['remote','local','default']
for name_calculate_ini in calculate_ini:
# проверить сущестование ini файла
if os.path.exists(name_calculate_ini):
# получить объект настроенный на ini
config = iniParser(name_calculate_ini)
# получаем все секции из конфигурационного файла
allsect = config.getAllSectionNames()
if not allsect:
continue
# находим встречающиеся у обоих секции
act_sect = tuple(set(allsect)& set_act_section)
# словарь переменных для ini - файла
importFileVars = {}
# получаем все переменные из всех секций
for section in act_sect:
allvars = config.getAreaVars(section)
if allvars == False:
return False
# словарь переменных для ini - файла
importFileVars = {}
# принудительно переписать все переменные окружения
# полученные из ini
for (k,v) in allvars.items():
k = k.encode("UTF-8")
value = cl_utils.convertStrListDict(v.encode("UTF-8"))
self.Set(k, value, True)
importFileVars[k] = value
if i < 3:
importVars[locations[i]] = importFileVars
i += 1
return importVars
def flServer(self, **args):
'''Заполнить конфигурацию переменных, для ldap'''
# заполнить переменные окружения алгоритмом по умолнанию
self._importData("Server")
def flClient(self, **args):
'''Заполнить конфигурацию переменных, для клиента'''
# заполнить переменные окружения алгоритмом по умолнанию
self._importData("Client")
def flBuilder(self, **args):
'''Заполнить конфигурацию переменных, для билдера'''
self.Set('setup_pass','builder',True)
# заполнить переменные окружения алгоритмом по умолнанию
self._importData("Builder")
def flInstall(self, **args):
'''Заполнить конфигурацию переменных для инсталятора'''
self.Set('setup_pass','install',True)
#def defined(self, vname):
#if vname:
#if self.__dict__.has_key(vname):
#return True
#return False
def defined(self, vname):
return True
def exists(self, nameVar):
""" Определяет существует ли переменная с таким имененм
"""
if self.__dict__.has_key(nameVar):
return True
foundVar = False
# Ищем переменную в импортируемых модулях
for section, moduleVar, fillobj in self._importList:
if moduleVar.Data.__dict__.has_key(nameVar):
foundVar = True
break
return foundVar
def getVars(self, type_names=None):
ret = {}
for section, moduleVar, fillobj in self._importList:
dataVar=moduleVar.Data
dictVars = dir(dataVar)
for nameVar in dictVars:
if not "__" in nameVar:
if not (getattr(dataVar,nameVar).has_key("official") and\
getattr(dataVar,nameVar)['official']):
self.Get(nameVar)
if type_names:
#type_names.sort()
varType =list(getattr(dataVar,nameVar)['type'])
#varType.sort()
#print type_names
#print varType
#print
if not set(type_names)<=set(varType):
continue
ret[nameVar] = getattr(self,nameVar)
return ret
#распечатать список переменных с значениями
def printVars(self,type_names=None):
var=None
var=self.getVars(type_names)
mlen_name=0;
mlen_type=0;
mlen_mode=0;
for i,j in var.items():
if len(i)>mlen_name:
mlen_name=len(i)
#if len(str(j.type))>mlen_type:
#mlen_type=len(str(j.type))
vtype=str(type(var[i].value)).split(" ")[1][1]
if not '[' in var[i].mode:
if vtype in ['d','l']:
mode="[%s%s]"%(var[i].mode.lower(),vtype)
else:
mode="[%s]"%(var[i].mode.lower())
var[i].mode=mode
if len(mode)>mlen_mode:
mlen_mode=len(mode)
plist=var.keys()
plist.sort()
br = cl_utils.fillstr("-",mlen_name) + " " +\
cl_utils.fillstr("-",mlen_mode) + " " + cl_utils.fillstr("-",10)
#cl_utils.fillstr("-",mlen_type) + " " +\
print "The list of variables:"
print "var name".center(mlen_name),\
"Mode","Value"
#"Type".center(mlen_type),\
print br
for i in plist:
#if var[i].value is None:
#continue
p_val=var[i].value
if var[i].official:
continue
columnWrite( i, mlen_name, var[i].mode.lower(),
mlen_mode,
#str(var[i].type),
#mlen_type,
p_val)
print br
class glob_attr:
"""Глобальные аттрибуты для методов заполнения переменных"""
def _runos(self,cmd, ret_first=None, env={}):
"""Вернуть результат выполнения команды ОС"""
if not env:
envDict = {}
env.update(os.environ.items() + [("PATH",cl_utils.getpathenv())] +\
env.items())
retCode, programOut = cl_utils.runOsCommand(cmd, None, ret_first, env)
if not retCode:
return programOut
return False

@ -17,11 +17,197 @@
import re
import os
import types
from cl_overriding import exit
import cl_utils
import cl_data
import filecmp
import cl_datavars
class fillVars(object, cl_data.glob_attr):
class clLocale:
lang = {
#Belarussian
'be_BY' : {
'locale':'be_BY.UTF-8',
'keymap':'by',
'dumpkeys_charset': 'koi8-u',
'consolefont':'Cyr_a8x16',
'xkblayout':'us,by',
'language':'ru',
},
#Belgian
'fr_BE' : {
'locale':'fr_BE.UTF-8',
'keymap':'be-latin1',
'dumpkeys_charset':'',
'consolefont':'lat9w-16',
'xkblayout':'us,be',
'language':'en_US',
},
#Brazilian Portuguese
'pt_BR' : {
'locale':'pt_BR.UTF-8',
'keymap':'br-abnt2',
'dumpkeys_charset':'',
'consolefont':'lat9w-16',
'xkblayout':'pt,us',
'language':'pt_BR',
},
#Canadian French
'fr_CA' : {
'locale':'fr_CA.UTF-8',
'keymap':'cf',
'dumpkeys_charset':'',
'consolefont':'default8x16',
'xkblayout':'us,ca_enhanced',
'language':'en_US',
},
#Danish
'da_DK' : {
'locale':'da_DK.UTF-8',
'keymap':'dk-latin1',
'dumpkeys_charset':'',
'consolefont':'lat0-16',
'xkblayout':'us,dk',
'language':'da',
},
#French
'fr_FR' : {
'locale':'fr_FR.UTF-8',
'keymap':'fr-latin9',
'dumpkeys_charset':'',
'consolefont':'lat0-16',
'xkblayout':'fr,us',
'language':'en_US',
},
#German
'de_DE' : {
'locale':'de_DE.UTF-8',
'keymap':'de-latin1',
'dumpkeys_charset':'',
'consolefont':'lat9w-16',
'xkblayout':'de,us',
'language':'de',
},
#Icelandic
'is_IS' : {
'locale':'is_IS.UTF-8',
'keymap':'is-latin1',
'dumpkeys_charset':'',
'consolefont':'cp850-8x16',
'xkblayout':'us,is',
'language':'en_US',
},
#Italian
'it_IT' : {
'locale':'it_IT.UTF-8',
'keymap':'it',
'dumpkeys_charset':'',
'consolefont':'default8x16',
'xkblayout':'us,it',
'language':'it',
},
#Norwegian
'nn_NO' : {
'locale':'nn_NO.UTF-8',
'keymap':'no-latin1',
'dumpkeys_charset':'',
'consolefont':'lat9w-16',
'xkblayout':'us,no',
'language':'nn',
},
#Polish
'pl_PL' : {
'locale':'pl_PL.UTF-8',
'keymap':'pl',
'dumpkeys_charset':'',
'consolefont':'lat2-16',
'xkblayout':'us,pl',
'language':'pl',
},
#Russian
'ru_RU' : {
'locale':'ru_RU.UTF-8',
'keymap':'-u ruwin_cplk-UTF-8',
'dumpkeys_charset':'',
'consolefont':'ter-k14n',
'xkblayout':'us,ru(winkeys)',
'language':'ru',
},
#Spanish
'es_ES' : {
'locale':'es_ES.UTF-8',
'keymap':'es euro2',
'dumpkeys_charset':'',
'consolefont':'lat0-16',
'xkblayout':'es,us',
'language':'es',
},
#Swedish
'sv_SE' : {
'locale':'sv_SE.UTF-8',
'keymap':'sv-latin1',
'dumpkeys_charset':'',
'consolefont':'lat0-16',
'xkblayout':'us,se',
'language':'sv',
},
#Ukrainian
'uk_UA' : {
'locale':'uk_UA.UTF-8',
'keymap':'ua-utf',
'dumpkeys_charset':'koi8-u',
'consolefont':'ter-v14n',
'xkblayout':'us,ua(winkeys)',
'language':'uk',
},
#United Kingdom/British
'en_GB' : {
'locale':'en_GB.UTF-8',
'keymap':'uk',
'dumpkeys_charset':'',
'consolefont':'LatArCyrHeb-16',
'xkblayout':'us,gb',
'language':'en_US',
},
#United State/English
'en_US' : {
'locale':'en_US.UTF-8',
'keymap':'us',
'dumpkeys_charset':'',
'consolefont':'LatArCyrHeb-16',
'xkblayout':'us',
'language':'en_US',
}
}
def getLangs(self):
return self.lang.keys()
def isLangExists(self,lang):
return lang in self.lang.keys()
def getFields(self,field):
return [ l[1][field] for l in self.lang.items() ]
def getFieldByLang(self,field,lang):
return self.lang.get(lang, self.lang['en_US'])[field]
def getFieldByKeymap(self,field,keymap):
return self.lang.get(self.getLangByField('keymap',keymap),
self.lang['en_US'])[field]
def getLangByField(self,field,value):
langs = [lang[0] for lang in self.lang.items()
if lang[1][field] == value ]
if not langs:
return 'en_US'
else:
return langs[0]
def getdirlist(s_path):
#Получить список директорий по указаному пути
fdir=filecmp.dircmp(s_path, s_path)
dir_list=fdir.common_dirs
return dir_list
class fillVars(object, cl_datavars.glob_attr):
def get_os_net_domain(self):
''' Определим домен'''
@ -29,7 +215,7 @@ class fillVars(object, cl_data.glob_attr):
if not domain:
print _("Error:") + " " +_("Not found domain name")
print _("Command 'hostname -d' returns an empty value")
exit(1)
cl_base.exit(1)
elif re.search("^hostname: ",domain):
return "local"
else:
@ -61,6 +247,7 @@ class fillVars(object, cl_data.glob_attr):
if linuxShortName:
dictLinuxName = {"CLD":"Calculate Linux Desktop",
"CLDX":"Calculate Linux Desktop",
"CLDG":"Calculate Linux Desktop",
"CDS":"Calculate Directory Server",
"Gentoo":"Gentoo"}
if linuxShortName in dictLinuxName.keys():
@ -74,7 +261,7 @@ class fillVars(object, cl_data.glob_attr):
"""постфикс к названию системы"""
linuxShortName = self.Get("os_linux_shortname")
if linuxShortName:
dictLinuxSubName = {"CLD":"KDE", "CLDX":"XFCE"}
dictLinuxSubName = {"CLD":"KDE", "CLDX":"XFCE", "CLDG":"GNOME"}
if linuxShortName in dictLinuxSubName.keys():
return dictLinuxSubName[linuxShortName]
else:
@ -100,7 +287,7 @@ class fillVars(object, cl_data.glob_attr):
systemVersion = ""
flagGentoo = False
if os.path.exists(gentooFile):
gentooLink = "/etc/make.template"
gentooLink = "/etc/make.profile"
if os.path.islink(gentooLink):
systemVersion = os.readlink(gentooLink).rpartition("/")[2]
flagGentoo = True
@ -130,7 +317,7 @@ class fillVars(object, cl_data.glob_attr):
def get_os_net_ip(self):
"""все ip компьютера, разделитель запятая"""
IPs = []
netInterfaces=cl_utils.getdirlist("/sys/class/net/")
netInterfaces=getdirlist("/sys/class/net/")
for i in netInterfaces:
res = self._runos("/sbin/ifconfig %s"%i)
if not res:
@ -165,7 +352,7 @@ class fillVars(object, cl_data.glob_attr):
%(x>>24, x>>16&255, x>>8&255, x&255, nMask(maskNumb))
networks=[]
netInterfaces=cl_utils.getdirlist("/sys/class/net/")
netInterfaces=getdirlist("/sys/class/net/")
flagError = False
for i in netInterfaces:
res = self._runos("/sbin/ifconfig %s"%i)
@ -181,85 +368,6 @@ class fillVars(object, cl_data.glob_attr):
return ""
return ",".join(networks)
def get_os_locale_locale(self):
"""локаль (прим: ru_RU.UTF-8)"""
if os.environ.has_key("LANG"):
return os.environ["LANG"]
else:
return "en_US.UTF-8"
def get_os_locale_lang(self):
"""язык (прим: ru_RU)"""
locale = self.Get("os_locale_locale")
if locale:
return locale.split(".")[0]
return ""
def get_os_locale_language(self):
"""язык (прим: ru)"""
lang = self.Get("os_locale_lang")
if lang:
return lang.split("_")[0]
return ""
def get_os_locale_xkb(self):
"""раскладка клавиатуры для X"""
path = '/etc/conf.d/keymaps'
mapDict={"by":"us,by",
"be-latin1":"be,us",
"br-abnt2":"br,us",
"cf":"ca,us",
"dk-latin1":"dk,us",
"fr-latin9":"fr,us",
"de-latin1":"de,us",
"is-latin1":"is,us",
"it":"it,us",
"no-latin1":"no,us",
"pl":"pl,us",
"-u ru4":"us,ru(winkeys)",
"es euro2":"es,us",
"sv-latin1":"se,us",
"ua-utf":"us,ua(winkeys)",
"uk":"gb,us",
"us":"us"}
if os.path.exists(path):
FD = open(path)
data = FD.readlines()
FD.close()
shortNameList = filter(lambda y:y,
map(lambda x:\
len(x.split("="))==2 and\
x.split("=")[0]=="KEYMAP" and\
x.split("=")[1].replace('"',"").strip(),\
data))
if shortNameList:
if shortNameList[0] in mapDict.keys():
return mapDict[shortNameList[0]]
lang = self.Get("os_locale_lang")
# Языки:
# Португальский - pt_BR
# Французский - fr_FR
# Немецкий - de_DE
# Итальянский - it_IT
# Польский - pl_PL
# Русский - ru_RU
# Испанский - es_ES
# Украинский - uk_UA
# Английский - en_US
xkbDict = {'pt_BR':'br,us',
'fr_FR':'fr,us',
'de_DE':'de,us',
'it_IT':'it,us',
'pl_PL':'pl,us',
'ru_RU':'us,ru(winkeys)',
'es_ES':'es,us',
'uk_UA':'us,ua(winkeys)',
'en_US':'us'}
if lang:
if xkbDict.has_key(lang):
return xkbDict[lang]
return ""
def get_os_locale_xkbname(self):
"""названия используемых раскладок клавиатуры для X"""
localeXkb = self.Get("os_locale_xkb")
@ -344,3 +452,151 @@ class fillVars(object, cl_data.glob_attr):
virtName = virtSysDict[vName]
break
return virtName
def getValueFromConfig(self,config,name):
"""Get value of parameter from bash type file
Parameters:
config config file name
name param name
"""
reMatch = re.compile("^%s\s*=\s*\"?(.*)\"?$"%name, re.I)
try:
if os.path.exists(config):
for line in open(config,"r").readlines():
match = reMatch.match(line)
if match:
return group().strip()
except:
pass
return False
def getValueFromCmdLine(self,option,num):
"""Get value of parameter from boot params
Parameters:
option param name
num number part of value parameter (, split)
"""
cmdLine = "/proc/cmdline"
calculateParam = "calculate"
# try get timezone from kernel calculate param
try:
for param in open(cmdLine,"r").read().split(" "):
parname,op,value = param.partition("=")
if parname == calculateParam and op == "=":
values = value.split(",")
if len(values) > num and values[num].strip():
return values[num].strip()
except IOError,e:
return ""
def get_hr_board_model(self):
"""motherboard model"""
modelFile = "/sys/class/dmi/id/board_name"
try:
return open(modelFile,"r").read().strip()
except:
return ""
def get_hr_board_vendor(self):
"""motherboard vendor"""
vendorFile = "/sys/class/dmi/id/board_vendor"
try:
return open(vendorFile,"r").read().strip()
except:
return ""
def get_hr_cpus(self):
"""processors count"""
cpuinfoFile = "/proc/cpuinfo"
try:
return len(["" for line in open(cpuinfoFile,"r").readlines()
if "processor" in line])
except:
return 1
def get_os_locale_locale(self):
"""locale (example: ru_RU.UTF-8)"""
locale = clLocale()
# get locale from boot calculate param
localeVal = self.getValueFromCmdLine("calculate",0)
if locale.isLangExists(localeVal):
return locale.getFieldByLang('locale',localeVal)
elif os.environ.has_key("LANG"):
return os.environ["LANG"]
else:
return locale.getFieldByLang("locale","default")
def get_os_locale_lang(self):
"""lang (example: ru_RU)"""
locale = clLocale()
return locale.getLangByField("locale",self.Get('os_locale_locale'))
def get_os_locale_language(self):
"""language (example: ru)"""
locale = clLocale()
return locale.getFieldByLang("language",self.Get('os_locale_lang'))
def get_os_locale_xkb(self):
"""xkb layouts (example: en,ru)"""
locale = clLocale()
# is specified keymap support by locale hash
if self.Get('os_locale_keymap') in locale.getFields('keymap'):
return locale.getFieldByKeymap("xkblayout",
self.Get('os_locale_keymap'))
else:
return locale.getFieldByLang("xkblayout",
self.Get('os_locale_lang'))
def get_os_locale_keymap(self):
"""keymap of locale (used for /etc/conf.d/keymaps)"""
locale = clLocale()
# get keymap from boot calculate param (keymap specified
# by lang)
keymapConfd = '/etc/conf.d/keymaps'
keymap = self.getValueFromCmdLine("calculate",1)
if locale.isLangExists(keymap):
return locale.getFieldByLang('keymap',keymap)
# get keymap by os_locale_lang
keymap = self.getValueFromConfig(keymapConfd,'KEYMAP')
if keymap:
return keymap
return locale.getFieldByLang("keymap",self.Get("os_locale_lang"))
def get_os_locale_dumpkeys(self):
"""dumpkeys charset for keymap"""
locale = clLocale()
# is specified keymap support by locale hash
if self.Get('os_locale_keymap') in locale.getFields('keymap'):
return locale.getFieldByKeymap("dumpkeys_charset",
self.Get('os_locale_keymap'))
else:
return locale.getFieldByLang("dumpkeys_charset",
self.Get('os_locale_lang'))
def get_os_clock_timezone(self):
"""timezone for clock"""
zoneinfodir = "/usr/share/zoneinfo/"
localtimefile = "/etc/localtime"
# try get timezone from kernel calculate param
timezone = self.getValueFromCmdLine("calculate",2)
if timezone and \
os.path.exists(os.path.join(zoneinfodir,timezone)):
return timezone
# get timezone from localtime symlink
if os.path.lexists(localtimefile):
return os.readlink(localtimefile).replace(zoneinfodir,"")
return "UTC"
def get_os_clock_type(self):
"""type of clock (UTC or local)"""
clockTypeFile = ['/etc/conf.d/clock','/etc/conf.d/hwclock']
for f in clockTypeFile:
clock = self.getValueFromConfig(f,"clock")
if clock:
if clock.upper() == 'UTC':
return clock.upper()
elif clock.lower() == 'local':
return clock.lower()
return "local"

@ -1,375 +0,0 @@
#-*- 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 getopt
import sys
from cl_string import prettyColumnStr
pcs = prettyColumnStr
class opt:
def __init__(self,shortOpt,longOpt = []):
""" Длинные и короткие опции командной строки допустимые в программе
a - короткая опция
>program -a
a: - короткая опциия со значением
>program -a 10
a:: - короткая опциия у которой может быть или не быть значение
>program -a
>program -a 15
"ha:" - значение параметра shortOpt
две опции h - без значения, a - со значением
help - длинная опция без значения
test= - длинная опция со значением
["help","test="] - значение парамера longOpt
>program -a
две опции help - без значения, test - со значением
"""
self.shortOpt = shortOpt
self.longOpt = longOpt
self.sysArgv = sys.argv[1:]
def getopt(self):
try:
opts, args = getopt.getopt(self.sysArgv,self.shortOpt,self.longOpt)
except getopt.GetoptError:
self.handlerErrOpt()
sys.exit(1)
for option, value in opts:
if len(option) == 2:
option = option[1:]
else:
option = option[2:]
self.handlerOpt(option,value)
for param in args:
self.handlerParam(param)
def handlerErrOpt(self):
# Обработчик в случае неправильных параметров
pass
def handlerOpt(self,option,value):
# Обработчик (параметр значение)
pass
def handlerParam(self,param):
# Обработчик хвостов (значение)
pass
class cl_help:
"""Объект для работы со справкой, и обработкой параметров.
Конструктор __init__ должен определить следующие переменные:
self.chapter список разделов справки, каждый элементы состоит из
имени раздела, флаг видимый/скрытый, кол-во переводов строк
после названия раздела, количество строк после раздела,
тип раздела
Пример: [("Copyright",False,0,2),"options"]
self.relService словарь связей сервисов и действующих опций
ключ - название сервиса, значение - список отображаемых
разделов отмеченных как "options"
Пример: {"samba":[_("Common options"),
_("Service Samba options")]}
self.relOptions словарь связей длинных опций помощи и выводимых разделов
помощи с опциями
ключ - параметр справки, значение список отображаемых
разделов справки
Пример: {"help-ldap":[_("Common options"),
_("Service LDAP options)]}
self.progName словарь имена используемых программ и их номера для
доступа к переменным
Пример: {'cl-groupadd':0, 'cl-groupdel':1}
self.data список данных для справки, каждый элемент словарь:
progAccess: список номеров программ отображающих
Пример: {'progAccess':(0,),
'shortOption':"g",
'longOption':"gid",
'optVal':"GID",
'helpChapter':_("Options"),
'help':_("use GID for the new group")
},
после заполнения параметров необходимо выполнить
self._cl_help__setParamHelp() для заполнения справки
"""
def __init__(self, cmdName):
# ширина консоли взята за 80
# -1 чтобы компенсировать расстрояние между колонками
self.consolewidth = 79
self.column_width = 32
self.cmdName = cmdName
#короткие опции командной строки
self.shortOpt = []
#длинные опции командной строки
self.longOpt = []
# массив разделов (заполняется в __setParamHelp)
self.chapterBloc = []
#optEnd = ""
#if "user" in self.cmdName and not "mod" in self.cmdName:
#optEnd = _("user")
#elif "group" in self.cmdName and not "mod" in self.cmdName:
#optEnd = _("group")
#self.__setParamHelp()
def getChapterNumber(self,NameChapter):
"""Получить номер раздела по имени"""
num = 0
for i in self.chapter:
if i[0] == NameChapter:
return num
num += 1
return False
def __setParamHelp(self):
"""Внутренняя функция формирования справки по данным
Перебирает все элементы списка data, проверяет их на доступность
данной программы, разбирает опции на среди data и формирует
для по ним справку.
"""
# сформировать нужное количество блоков раздела
self.chapterBloc = [""]*len(self.chapter)
#
sp = {}
i = 0
# перебираем все элементы справки собираем элементы опции
# так же формируем разделы не опции
for par in self.data:
# перебираем только те опции, которые принадлежат команде
if self.access(par):
# есть короткая (возможно есть и длинная)
if par.has_key("shortOption"):
sp[par["shortOption"]+":"+par["helpChapter"]] = i
# есть только длинная опция
elif par.has_key("longOption"):
sp[par["longOption"]+":"+par["helpChapter"]] = i
# формирование разделов не опций
else:
helpTxt = par['help']
numChapter = self.getChapterNumber(par['helpChapter'])
self.addChapterHelp(numChapter,helpTxt)
i += 1
# перебираем все "собранные" опции
# опции перебираются по порядку в списке date
# для сортировки по ключам следует применить код:
# for index in sorted(sp.keys()):
# par = self.data[sp[index]]
for index in sorted(sp.values()):
par = self.data[index]
numChapter = self.getChapterNumber(par['helpChapter'])
# если есть и короткая и длинная
if "shortOption" in par and "longOption" in par:
paraminfo = "-%s, --%s "%(par["shortOption"],par["longOption"])
# если есть только короткая
elif "shortOption" in par:
paraminfo = "-%s "%par["shortOption"]
# если только длинная
else:
paraminfo = "--%s "%par["longOption"]
# если указан параметр для опции
if "optVal" in par:
optVal = par["optVal"]
else:
optVal = ""
# вывод вида: " [-o, ][--option] [PARAM]" "helpstring"
helpTxt = pcs(" "+paraminfo+optVal, self.column_width, \
par['help'], self.consolewidth-self.column_width)
# добавить строку в нужный раздел
self.addChapterHelp(numChapter,helpTxt)
def getHelp(self, optionsChapters=False):
"""Выдать справку.
Выдает справку в случае если указан optionsChapters, то фильтрует по
типу разделов.
Параметры:
optionsChapters Flase или список опциональных разделов для
отображения
Возвращаемые параметры:
Строка со справкой.
"""
# Выдать справку
help = ""
# перебираем все элементы справочных блоков
iterChapterBloc = iter(self.chapterBloc)
# перебираем все разделы по параметрам
for (nameChapter, visibleChapter, beforeStrChapter, \
afterStrChapter, typeChapter) in self.chapter:
# получаем следующий блок (т.о. textChapterBloc соответ, chapter)
textChapterBloc = iterChapterBloc.next()
# если тип раздела опциональный
if optionsChapters and typeChapter=="options":
# проверяем нужно ли его отображать
if not (nameChapter in optionsChapters):
continue
bef = "\n"*beforeStrChapter
aft = "\n"*afterStrChapter
# если блок не пустой и раздел отображаемый
if len(textChapterBloc) > 0:
if visibleChapter:
help += nameChapter + ": " + bef
help += textChapterBloc + aft
help = help.rstrip()+"\n"
return help
def addChapterHelp(self, numChapter, helpTxt):
"""Добавить в раздел помощи numChapteк тектстовую строку helpTxt
Параметры:
numChapter номер раздела в который нужно добавить данные справки
helpTxt строка, содержащая данные
"""
self.chapterBloc[numChapter] += helpTxt
return True
def addData(self,dataHash):
# На будущее (добавляет опции)
self.data.append(dataHash)
return True
def handleCheckAccess(self,dataHash):
"""Замещаемый дополнительный обработчик проверки
доступности опции.
Входные параметры:
dataHash элементы списка данных справки (self.data)
"""
return True
def access(self,dataHash):
"""Доступна ли опция вызывающей программе
Параметры:
dataHash словарь элемент типа self.data
Возвращаемые параметры:
True/False доступна/недоступна
"""
# доступна ли опция вызывающей программе
# опция без progAccess доступна
numProg = self.progName[self.cmdName]
if 'progAccess' in dataHash:
if numProg in dataHash['progAccess']:
# вызов дополнительной проверки доступа к опции
return self.handleCheckAccess(dataHash)
else:
return False
else:
# вызов дополнительной проверки доступа к опции
return self.handleCheckAccess(dataHash)
def getTypeChapter(self, nameChapter):
"""Получить тип раздела по его имени
Параметры:
nameChapter название раздела
Возвращаемые параметры:
строка тип раздела
Flase(Boolean) такой раздел отсутствует
"""
# фильтруем список по имени раздела, помещаем в список тип раздела
filtered = [typeChapter for name, na, na, na, typeChapter \
in self.chapter if name == nameChapter]
# если среди фильтрованных есть хоть один элемент
if len(filtered) > 0:
# возвращаем - он запрашиваемый
return filtered[0]
else:
# такой раздел отсутствует
return False
def clearAllOpt(self):
"""Очистить все опции, полученные посредством getAllOpt"""
if len(self.shortOpt) > 0:
self.shortOpt = []
if len(self.longOpt) > 0:
self.longOpt = []
return True
def getAllOpt(self,typeOpt="all", optionsChapters=False):
"""Получить все доступные опции
Параметры:
typeOpt 'short'/'long'/'all', вернуть короткие или длинные
опции или все (возвращаются кортежем)
optionsChapters фильтр для опций по типам разделов (список,кортеж)
Возвращаемые параметры:
строка коротки или список строк длинных опций ('hb:c:wg:G:k:ms:u:')
"""
# Выдать все действующие опции
if typeOpt=="short" or typeOpt=="all":
if len(self.shortOpt) == 0:
for par in self.data:
if optionsChapters and\
self.getTypeChapter(par['helpChapter'])=="options":
if not (par['helpChapter'] in optionsChapters):
continue
if par.has_key("shortOption") and self.access(par):
if par.has_key("optVal"):
self.shortOpt.append(par["shortOption"]+':')
else:
self.shortOpt.append(par["shortOption"])
if typeOpt=="long" or typeOpt=="all":
if len(self.longOpt) == 0:
for par in self.data:
if optionsChapters and\
self.getTypeChapter(par['helpChapter'])=="options":
#print par["longOption"]
if not (par['helpChapter'] in optionsChapters):
continue
if par.has_key("longOption") and self.access(par):
if par.has_key("optVal"):
self.longOpt.append(par["longOption"]+'=')
else:
self.longOpt.append(par["longOption"])
if typeOpt=="short":
return "".join(self.shortOpt)
elif typeOpt=="long":
return self.longOpt
elif typeOpt=="all":
return ("".join(self.shortOpt),self.longOpt)
def getShortOpt(self,option):
"""Из любой опции получить короткую опцию.
Фильтрация также происходит и по названию команды.
Параметры:
option запрашиваемая опция
Возвращаемые параметры:
короткая опция, если же для длинной опции нет короткой, возвращается
пустая строка.
"""
# Из любой опции получаем короткую опцию
for par in self.data:
if par.has_key("shortOption") and self.access(par):
if (par.has_key("longOption") and\
par["longOption"] == option) or \
par["shortOption"] == option:
return par["shortOption"]
return ""

@ -1,40 +0,0 @@
#-*- 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 sys
from cl_utils import removeDir
from cl_print import color_print
import cl_lang
tr = cl_lang.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])
class shareFile(color_print):
"""Общие методы для работы с файлами"""
def removeDir(self, rmDir):
"""Рекурсивное удаление директории
входной параметр директория или результат сканирования файлов (объект)
"""
try:
removeDir(rmDir)
except (OSError, Exception), e:
print e
self.printERROR(_("Can not delete dir: " ) + rmDir)
return False
return True

@ -13,7 +13,6 @@
# 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 time

@ -21,7 +21,7 @@ def __findFileMO(domain, localedir=None, languages=None, all=0):
замена gettext.find"""
if localedir is None:
localedir = _default_localedir
localedir = gettext._default_localedir
if languages is None:
languages = []
for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):

File diff suppressed because it is too large Load Diff

@ -14,20 +14,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import subprocess
import string
from random import choice
import os
import types
import stat
import subprocess
def _toUNICODE(val):
"""перевод текста в юникод"""
if type(val) == types.UnicodeType:
return val
else:
return str(val).decode('UTF-8')
class _error:
# Здесь ошибки, если они есть
error = []
def getError(self):
"""Выдать ошибки"""
if not self.error:
return False
error = ""
for e in self.error:
error += e + "\n"
return error
def setError(self, error):
"""Установка ошибки"""
self.error.append(error)
return True
def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None):
"""Выполняет внешнюю программу
@ -65,21 +75,8 @@ def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None):
return retcode, res
return retcode, None
def getpathenv():
"""вернуть пути для запуска программ"""
bindir=['/sbin','/bin','/usr/sbin','/usr/bin']
env=os.environ
if env and env.has_key('PATH'):
lpath=env['PATH'].split(":")
npath=[]
for dirname in bindir:
if os.path.exists(dirname) and dirname not in lpath:
npath.append(dirname)
lpath=npath+lpath
return ":".join(lpath)
def genpassword(passlen=9):
'''Вернуть случайный пароль указанной длины
'''Вернуть случайный пассворд указанной длины
Параметры:
passlen длина пароля который нужно сгенерировать
@ -95,6 +92,18 @@ def fillstr(char, width):
'''Заполнить строку указанным числом символов. Псеводоним символ*кол-во'''
return str(char) * width
def getpathenv():
"""Вернуть пути для запуска утилит"""
bindir=['/sbin','/bin','/usr/sbin','/usr/bin']
env=os.environ
if env and env.has_key('PATH'):
lpath=env['PATH'].split(":")
npath=[]
for dirname in bindir:
if os.path.exists(dirname) and dirname not in lpath:
npath.append(dirname)
lpath=npath+lpath
return ":".join(lpath)
def list2str(list):
'''Функция переводит список в строку'''
@ -138,120 +147,9 @@ def convertStrListDict(val):
else:
return val
def getdirlist(s_path):
"""Получить список директорий по указаному пути"""
return filter(lambda x: os.path.isdir(x), os.listdir(s_path))
class _error:
# Здесь ошибки, если они есть
error = []
def getError(self):
"""Выдать ошибки"""
if not self.error:
return False
error = ""
for e in self.error:
error += e + "\n"
return error
def setError(self, error):
"""Установка ошибки"""
if not error in self.error:
self.error.append(error)
return True
class scan:
"""Класс для сканирования директорий"""
class _dir:
"""Класс для хранения директорий"""
def __init__(self):
# Базовая директория
self.baseDir = False
# Все директории в базовой включая вложенные
self.dirs = []
# Все файлы внутри базовой директории
self.files = []
# Все ссылки внутри базовой директории
self.links = []
# Все сокеты внутри базовой директории
self.sockets = []
def __scanDir(self, scanDir, dirObj, flagDir=False):
"""Сканирование одной директории"""
if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]):
for fileOrDir in os.listdir(scanDir):
absPath = os.path.join(scanDir,fileOrDir)
statInfo = os.lstat(absPath)[stat.ST_MODE]
if stat.S_ISDIR(statInfo):
dirObj.dirs.append(absPath)
self.__scanDir(absPath, dirObj, True)
elif stat.S_ISLNK(statInfo):
dest = absPath
src = os.readlink(absPath)
dirObj.links.append((src,dest))
elif stat.S_ISREG(statInfo):
dirObj.files.append(absPath)
elif stat.S_ISSOCK(statInfo):
dirObj.sockets.append(absPath)
return dirObj
def scanDirs(self, scanDirs):
"""Сканирование директорий на вход список
Выход список объктов _dirProf
"""
dirs = []
for scanDir in scanDirs:
dirP = scan._dir()
try:
self.__scanDir(scanDir, dirP)
except OSError, e:
print e.strerror, e.filename
return []
dirP.baseDir = scanDir
dirs.append(dirP)
return dirs
def removeDir(rmDir):
"""Рекурсивное удаление директории
входной параметр директория для удаления
Ошибки по except
"""
if not os.path.exists(rmDir):
raise Exception(_("Not found remove dir %s") %rmDir)
fileObj = scan()
# Сканируем директорию
scanObjs = fileObj.scanDirs([rmDir])
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 os.path.exists(rmDir):
os.rmdir(rmDir)
return True
def getModeFile(nameFile):
"""Выдает информацию о файле
права файла, владелец, группа файла (777,test, group)
"""
fd = os.open(nameFile, os.O_RDONLY)
fst = os.fstat(fd)
uid = fst.st_uid
gid = fst.st_gid
mode = stat.S_IMODE(fst.st_mode)
os.close(fd)
return (mode,uid,gid)
def _toUNICODE(val):
"""перевод текста в юникод"""
if type(val) == types.UnicodeType:
return val
else:
return str(val).decode('UTF-8')

@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#Допустимые ключи значений
# mode - режим переменной r-не переназначается из командной строки,
# w-переназначается из командной строки
@ -55,13 +54,25 @@ class Data:
# названия используемых раскладок клавиатуры для X
os_locale_xkbname = {}
# keymap of locale (used for /etc/conf.d/keymaps)
os_locale_keymap = {}
# dumpkeys_charset for keymap
os_locale_dumpkeys = {}
# timezone for clock
os_clock_timezone = {}
# type of clock (UTC or local)
os_clock_type = {}
# архитектура компьютера (i686,x86_64)
os_arch_machine = {}
#проход при наложении шаблонов 1,2,3,4,5 и.т д
#проход при наложении профилей 1,2,3,4,5 и.т д
cl_pass_step = {'mode':"w"}
# обрабатываемый файл шаблона
# обрабатываемый файл профиля
cl_pass_file = {'mode':"w"}
# корневой раздел файловой системы
@ -76,14 +87,26 @@ class Data:
# постфикс к названию системы
os_linux_subname = {}
# motherboard model
hr_board_model = {}
# motherboard vendor
hr_board_vendor = {}
# processors count
hr_cpus = {}
# название виртуальной машины (virtualbox, vmware, qemu)
hr_virtual = {}
# версия системы
os_linux_ver = {}
# Тип шаблона
# Тип профиля
cl_pass_type = {'mode':"w"}
# Действие программы
cl_pass_run = {'mode':"w"}
#Логин пользователя
ur_login = {'mode':"w"}

@ -215,4 +215,5 @@ class apache(bind):
if not areas:
return docObj
self.createXML(areas, docObj.getNodeBody(), docObj)
return docObj
return docObj

@ -313,3 +313,5 @@ class bind(objShare):
"""Объединяем конфигурации"""
if isinstance(bindObj, bind):
self.docObj.joinDoc(bindObj.doc)

@ -98,3 +98,5 @@ class dhcp(bind):
"""Объединяем конфигурации"""
if isinstance(dhcpObj, dhcp):
self.docObj.joinDoc(dhcpObj.doc)

@ -62,4 +62,6 @@ class dovecot(bind):
self.docObj.joinDoc(dovecotObj.doc)
# Для добавления перевода строки перед закрывающим тегом
# конфигурационного файла
self.postXML()
self.postXML()

@ -130,31 +130,10 @@ class kde(samba):
#print docObj.doc.toprettyxml()
return docObj
def postXML(self):
"""Последующая постобработка XML"""
# Для добавления перевода строки между областями если его нет
#print self.docObj.body.toprettyxml()
xmlAreas = xpath.Evaluate("child::area", self.docObj.body)
for xmlArea in xmlAreas:
if xmlArea.previousSibling and\
self.docObj.getTypeField(xmlArea.previousSibling) == "br":
continue
firstArea = False
xmlFields = xpath.Evaluate("child::field", xmlArea)
if not (xmlFields and\
(self.docObj.getTypeField(xmlFields[-1]) == "br" or\
self.docObj.getTypeField(xmlFields[-1]) == "comment")):
if xmlArea.nextSibling:
parentNode = xmlArea.parentNode
nextNode = xmlArea.nextSibling
parentNode.insertBefore(self.docObj.createField("br",
[],"",[],
False,False),
nextNode)
def join(self, kdeObj):
"""Объединяем конфигурации"""
if isinstance(kdeObj, kde):
self.docObj.joinDoc(kdeObj.doc)
self.postXML()

@ -181,3 +181,4 @@ class ldap(samba):
z += 1
#print docObj.doc.toprettyxml()
return docObj

@ -446,6 +446,26 @@ class plasma(samba):
rootNode.appendChild(fieldXML)
#rootNode.appendChild(areaXML)
def createTxtConfig(self, strHeader, dictVar):
"""Cоздает область с заголовком
создает текст конфигурационного файла в формате samba из
заголовка (строка) и словаря переменных
"""
if not strHeader:
return ""
if type(strHeader) in (tuple, list):
outTxt = "".join(map(lambda x: "["+x+"]",strHeader))
if not outTxt:
return ""
outTxt += "\n"
else:
outTxt = "[" + strHeader + "]\n"
for key in dictVar.keys():
outTxt += "%s=%s\n" %(key,dictVar[key])
return outTxt
def _textToXML(self):
"""Преобразуем текст в XML"""
areas = []
@ -480,7 +500,7 @@ class plasma(samba):
elif len(quotes) == 1:
quotes.append("")
return quotes
xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body)
#print "-------------------------------------------------------"
#print xmlAreas
@ -507,7 +527,7 @@ class plasma(samba):
parentNode.removeChild(xmlArea.nextSibling.nextSibling)
parentNode.removeChild(xmlArea.nextSibling)
continue
# Собираем поля в кучку
xmlChildAreas = xpath.Evaluate("child::area", xmlArea)
if xmlChildAreas:
@ -535,6 +555,15 @@ class plasma(samba):
"br":
xmlArea.insertBefore(childNodes[it],
firstChildArea)
# Добавление перевода строк в если его нет между полями
if self.docObj.getTypeField(node) == "var" and\
node.previousSibling and\
not (self.docObj.getTypeField(node.previousSibling) in\
("br","comment")):
xmlArea.insertBefore(self.docObj.createField("br",
[],"",[],
False,False),
node)
# Добавляем BR если его нет первым полем
xmlFields = xpath.Evaluate("child::field", xmlArea)
@ -581,17 +610,10 @@ class plasma(samba):
parentNode = xmlArea.nextSibling.parentNode
parentNode.removeChild(xmlArea.nextSibling)
#xmlName = xpath.Evaluate("child::caption/name", xmlArea)[0]
#print "------------------------------------"
#print "Name =", xmlName.firstChild
#if xmlArea.previousSibling:
#print "PR_TYPE =", self.docObj.getTypeField(xmlArea.previousSibling)
def join(self, kdeObj):
"""Объединяем конфигурации"""
if isinstance(kdeObj, plasma):
self.docObj.joinDoc(kdeObj.doc)
self.postXML()

@ -115,3 +115,4 @@ class postfix(apache):
fields.append(field)
field = fieldData()
return fields

@ -62,6 +62,21 @@ class samba(objShare):
[],"",[],
False,False),
nextNode)
# Удаление лишних переводов строк
childNodes = self.docObj.getFieldsArea(self.docObj.body)
lenBr = 0
removeBrNodes = []
for node in childNodes:
if node.tagName == "field" and\
self.docObj.getTypeField(node) == "br":
lenBr += 1
if lenBr > 2:
removeBrNodes.append(node)
else:
lenBr = 0
# Удаление
for rmNode in removeBrNodes:
self.docObj.body.removeChild(rmNode)
def join(self, sambaObj):
@ -258,4 +273,4 @@ class samba(objShare):
z += 1
#print docObj.doc.toprettyxml()
return docObj

@ -84,3 +84,4 @@ class squid(procmail):
if isinstance(squidObj, squid):
#print squidObj.getConfig()
self.docObj.joinDoc(squidObj.doc)

@ -25,7 +25,6 @@ tr = lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])
class xml_gconf(xml_xfce):
"""Класс для объединения gconf-xml файлов"""
# root нода
@ -42,6 +41,8 @@ class xml_gconf(xml_xfce):
_comment = ("<!--","-->")
# поддерживаемые аттрибуты тега entry. Пример <entry type="int"/>
supportEntryTypes = ("int", "bool", "float", "string", "list", "pair")
# регулярное выражения для поиска \t в начале строки (преобразование xml)
reStartTabs = re.compile("^(\t+)(.*)$")
def __init__(self, text):
self.text = text
@ -81,7 +82,8 @@ class xml_gconf(xml_xfce):
"""Сравнение содержимого двух списков XML нод"""
getTextsNodes = lambda y: map(lambda x:\
x.toxml().replace(" ","").replace("\t","").replace("\n",""),
map(lambda x: x.removeAttribute("mtime") or x,
map(lambda x: x.hasAttribute("mtime") and\
x.removeAttribute("mtime") or x,
map(lambda x: x.cloneNode(True),
filter(lambda x: x.nodeType==x.ELEMENT_NODE, y))))
if set(getTextsNodes(listXmlA))==set(getTextsNodes(listXmlB)):
@ -103,13 +105,16 @@ class xml_gconf(xml_xfce):
nName = u''
nType = u''
nValue = u''
nSchema = u''
attrName = ''
attrType = ''
if flagRootNode:
if not tagName == "gconf":
self.setError(_("The text is not a valid gconf-XML format \
(not found '<gconf>...</gconf>')"))
return False
flagType = False
flagValue = False
flagSchema = False
else:
if not tagName == "entry":
self.setError(_("The text is not a valid gconf-XML format \
@ -118,20 +123,27 @@ class xml_gconf(xml_xfce):
if not n.hasAttribute("name"):
self.setError(_('Not found arrtibute "name" in tag entry'))
return False
if not n.hasAttribute("type"):
flagType = n.hasAttribute("type")
flagValue = False
flagSchema = n.hasAttribute("schema")
if flagSchema:
nSchema = n.getAttribute("schema")
if not flagType and not flagSchema:
self.setError(_('Not found arrtibute "type" in tag entry'))
return False
nName = n.getAttribute("name")
attrName = u"attribute::name='%s'"%nName
nType = n.getAttribute("type")
# Проверка правильности аттрибута type
if not nType in self.supportEntryTypes:
self.setError(\
_('Incorrect arrtibute "type" - <entry type="%s"/>')%nType)
return False
attrType = u"attribute::type='%s'"%nType
if n.hasAttribute("value"):
nValue = n.getAttribute("value")
if flagType:
flagValue = n.hasAttribute("value")
nType = n.getAttribute("type")
# Проверка правильности аттрибута type
if not nType in self.supportEntryTypes:
self.setError(\
_('Incorrect arrtibute "type" - <entry type="%s"/>')\
%nType)
return False
if flagValue:
nValue = n.getAttribute("value")
if n.hasAttribute("action"):
nAction = n.getAttribute("action")
if not nAction in ("join","replace","drop"):
@ -142,13 +154,9 @@ Valid values attribute 'action': \
self.setError(textError)
return False
if xmlOldNode.parentNode:
findStr = u"child::%s"%tagName
strAttr = [attrName, attrType]
findAttr = filter(lambda x: x, strAttr)
findAttrStr = ''
if findAttr:
strAttr = u' and '.join(findAttr)
findAttrStr = "[%s]"%strAttr
findAttrStr = ""
if attrName:
findAttrStr = "[%s]"%attrName
findPath = u"child::%s%s"%(tagName,findAttrStr)
# Рабочая нода
if flagRootNode:
@ -156,16 +164,19 @@ Valid values attribute 'action': \
else:
workNode = xmlOldNode
oldNodes = xpath.Evaluate(findPath, workNode)
# Новая нода список
flagArray = False
if nType == "list" or nType == "pair":
flagArray = True
flagDrop = False
# По умолчанию - объединение
flagJoin = True
flagReplace = False
flagDrop = False
# Замещаем ноду
if nType=="string" or nAction=="replace":
flagJoin = False
flagReplace = True
# Замещаем ноду в случае массива
elif nType == "list" or nType == "pair":
flagJoin = False
flagReplace = True
# Удаляем ноду
elif nAction == "drop":
flagJoin = False
flagDrop = True
@ -180,29 +191,34 @@ the same nodes at one level")
self.setError(textError)
return False
nextOldNode = oldNodes[0]
# Замещаем ноду в случае массива
if flagArray and not flagDrop:
replaceXmlNode = xmlNode.cloneNode(True)
# Сравнение содержимого нод
if not self.cmpListsNodesEntry([replaceXmlNode],
[nextOldNode]):
replaceXmlNode.setAttribute("mtime",
self.currentTime)
if nAction:
replaceXmlNode.removeAttribute("action")
workNode.replaceChild(replaceXmlNode,
nextOldNode)
flagJoin = False
flagReplace = False
childNodes = False
# Объединение нод
if flagJoin:
if nextOldNode.hasAttribute("value"):
oValue = nextOldNode.getAttribute("value")
if nValue != oValue:
if flagType and flagValue:
flagChange = False
foundValue = nextOldNode.hasAttribute("value")
if foundValue:
oValue = nextOldNode.getAttribute("value")
if nValue != oValue:
flagChange = True
else:
flagChange = True
if flagChange:
nextOldNode.setAttribute("mtime",
self.currentTime)
nextOldNode.setAttribute("value",nValue)
elif flagSchema:
flagChange = False
foundValue = nextOldNode.hasAttribute("schema")
if foundValue:
oSchema = nextOldNode.getAttribute("schema")
if nSchema != oSchema:
flagChange = True
else:
flagChange = True
if flagChange:
nextOldNode.setAttribute("mtime",
self.currentTime)
nextOldNode.setAttribute("schema",nSchema)
# Замещение ноды
elif flagReplace:
replaceXmlNode = xmlNode.cloneNode(True)
@ -252,11 +268,31 @@ the same nodes at one level")
self.joinDoc(xml_gconfObj.doc)
except:
self.setError(_("Can not join template"))
return False
return False
return True
def getConfig(self):
"""Получение текстового файла из XML документа"""
def expandStartTabs(s):
if s.startswith("\t"):
res = self.reStartTabs.search(s)
return res.group(1).replace("\t"," ") + res.group(2)
else:
return s
data = self.doc.toprettyxml().split("\n")
data = filter(lambda x: x.strip(), data)
return "\n".join(data).replace("\t"," ")
data = map(lambda x: expandStartTabs(x),
filter(lambda x: x.strip(), data))
dataOut = []
z = 0
lenData = len(data)
lenM2 = lenData - 2
for i in xrange(lenData):
if z>0:
z -= 1
continue
if i < lenM2 and data[i].endswith(">") and not "<" in data[i+1]:
dataOut.append(data[i] + data[i+1].strip() + data[i+2].strip())
z = 2
continue
dataOut.append(data[i])
return "\n".join(dataOut)

@ -158,7 +158,6 @@ Valid values attribute 'action': \
self.setError(textError)
return False
if xmlOldNode.parentNode:
findStr = u"child::%s"%path
strAttr = [attrName, attrType]
findAttr = filter(lambda x: x, strAttr)
findAttrStr = ''
@ -266,3 +265,4 @@ the same nodes at one level")
data = self.doc.toprettyxml(encoding='UTF-8').split("\n")
data = filter(lambda x: x.strip(), data)
return "\n".join(data).replace("\t"," ").decode("UTF-8")

@ -43,7 +43,7 @@ class xml_xfcepanel(xml_xfce):
try:
self.doc = xml.dom.minidom.parseString(self.text)
except:
self.setError(_("Can not text profile is XML"))
self.setError(_("Can not text template is XML"))
return False
self.rootNode = self.doc.documentElement
self.bodyNode = self.rootNode
@ -54,7 +54,7 @@ class xml_xfcepanel(xml_xfce):
return True
def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0):
"""Объединение корневой ноды профиля и корневой ноды файла"""
"""Объединение корневой ноды шаблона и корневой ноды файла"""
xmlNode = xmlNewNode
childNodes = xmlNode.childNodes
nextOldNode = xmlOldNode
@ -80,14 +80,13 @@ class xml_xfcepanel(xml_xfce):
if n.hasAttribute("action"):
nAction = n.getAttribute("action")
if not nAction in ("join","replace","drop"):
textError = _('''In the text, XML profile, look \
textError = _('''In the text, XML template, look \
for a reserved attribute 'action' with the incorrect value.\n\
Valid values attribute 'action': \
(action="join", action="replace", action="drop")''')
self.setError(textError)
return False
if xmlOldNode.parentNode:
findStr = u"child::%s"%path
findAttrStr = ""
if attrName:
findAttrStr = "[%s]"%attrName
@ -120,7 +119,7 @@ Valid values attribute 'action': \
self.panelNumbers[levelNumber] = 0
if oldNodes:
if len(oldNodes)>1 and path != "panel":
textError = _("The uncertainty in this profile are \
textError = _("The uncertainty in this template are \
the same nodes at one level")
self.setError(textError)
return False
@ -194,6 +193,7 @@ the same nodes at one level")
try:
self.joinDoc(xml_xfceObj.doc)
except:
self.setError(_("Can not join profile"))
self.setError(_("Can not join template"))
return False
return True

@ -1,178 +0,0 @@
#-*- 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 sys
import os
import hashlib
import crypt
import string
import time
from random import choice
from base64 import urlsafe_b64encode as b64encode
from cl_print import color_print
from server.users import users
from server.utils import execProg
import cl_lang
# Перевод модуля
tr = cl_lang.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])
class encrypt(color_print):
"""Класс хранения общих методов используемых для настройки сервисов
Методы шифрования, создания сертификатов и.т. д
"""
def __GenCryptSalt__(self):
"""Генерация соли для хеширования пароля (CRYPT)"""
chars = string.letters + string.digits + "./"
salt = ""
for i in range(2):
salt = salt + choice(chars)
return salt
def getHashPasswd(self, password, crypt):
"""Генерация хеша пароля,
Поддерживаемые алгоритмы шифрования пароля:
plain, md5, smd5, crypt, sha, ssha
"""
if not password:
self.printERROR(_("ERROR") + " getHashPasswd: " +\
_("password empty"))
return False
hashPwd = ""
if crypt == "plain":
hashPwd = password
elif crypt == "md5":
h = hashlib.md5(password)
hashPwd = "{MD5}" + b64encode(h.digest())
elif crypt == "smd5":
salt = os.urandom(4)
h = hashlib.md5(password)
h.update(salt)
hashPwd = "{SMD5}" + b64encode(h.digest() + salt)
elif crypt == "crypt":
salt = self.__GenCryptSalt__()
hashPwd = "{CRYPT}" + crypt.crypt(password, salt)
elif crypt == "sha":
h = hashlib.sha1(password)
hashPwd = "{SHA}" + b64encode(h.digest())
elif crypt == "ssha":
salt = os.urandom(4)
h = hashlib.sha1(password)
h.update(salt)
hashPwd = "{SSHA}" + b64encode(h.digest() + salt)
else:
self.printERROR(_("ERROR") + " getHashPasswd: " +\
_("Can not support '%s' crypto algoritm")%crypt)
return False
return hashPwd
def createCertificate(self, sslCountry="US",
sslState="California",
sslLocality="Santa Barbara",
sslOrganization="SSL Server",
sslUnit="For Testing Purposes Only",
sslCommonName="localhost",
sslEmail="root@localhost",
nsCertType="server",
sslDays=730,
sslBits=1024,
userName="root",groupName="root",
certFile="/tmp/server.pem",
certFileMode=0400,
keyFile="/tmp/server.key",
keyFileMode=0400):
"""Создает сертификат"""
certAndKeyFiles = [certFile, keyFile]
foundCertFiles = filter(lambda x: os.path.exists(x), certAndKeyFiles)
if len(foundCertFiles)==2:
return True
# Удаляем файл сертификата
map(lambda x: os.remove(x), foundCertFiles)
# Объект для работы с пользователями
usersObj = users()
# получаем id и gid пользователя
uidAndGid = usersObj.getUserUidAndGid(userName, groupName)
if not uidAndGid:
return False
uid, gid = uidAndGid
textCnf="""[ req ]
prompt = no
default_bits = %s
distinguished_name = req_dn
[ req_dn ]
C = %s
ST = %s
L = %s
O = %s
OU = %s
CN = %s
emailAddress = %s
[ cert_type ]
nsCertType = %s
"""%(sslBits, sslCountry, sslState, sslLocality, sslOrganization, sslUnit,
sslCommonName, sslEmail, nsCertType)
# генерируем название файла конфигурации
strData = time.strftime("%Y%m%d%H%M%S",time.localtime(time.time()))
cnfFile = "/tmp/%s.cnf" %strData
sslFile = "/usr/bin/openssl"
if not os.path.exists(sslFile):
self.printERROR(_("Can not found %s")%sslFile)
return False
# Cоздание директорий
for fileName in certAndKeyFiles:
dirName = os.path.split(fileName)[0]
if not os.path.exists(dirName):
self.createUserDir(0, 0, dirName, 0755)
# Создание конфигурационного файла
usersObj.createUserFile(cnfFile, textCnf, 0, 0, 0600)
# Создание сертификата
textLine = execProg(\
"%s req -new -x509 -nodes -config %s -days %s -out %s -keyout %s"\
%(sslFile, cnfFile, sslDays, certFile, keyFile))
# Удаление конфигурационного файла
if os.path.exists(cnfFile):
os.remove(cnfFile)
# Меняем права
if os.path.exists(certFile):
os.chown(certFile, uid,gid)
os.chmod(certFile, certFileMode)
if os.path.exists(keyFile):
os.chown(keyFile, uid,gid)
os.chmod(keyFile, keyFileMode)
if textLine == False:
self.printERROR(_("Can not create certificate %s")%certFile)
return False
# Проверка сертификата
textLine = execProg("%s x509 -subject -fingerprint -noout -in %s"\
%(sslFile, certFile))
if textLine == False:
self.printERROR(_("Can not create certificate %s")%certFile)
return False
return True

@ -104,7 +104,7 @@ class iniLdapParser(iniParser):
if not os.path.exists(pathIniFile):
os.makedirs(pathIniFile)
class ldap(_error, color_print, shareVars):
class shareldap(_error, color_print, shareVars):
"""Общие методы для работы с LDAP для серверных программ"""
# DN сервисов относительно базового

@ -1,6 +1,6 @@
#!/usr/bin/env python
# setup.py --- Setup script for calculate-server
# setup.py --- Setup script for calculate-lib
# Copyright 2008-2010 Mir Calculate Ltd. http://www.calculate-linux.org
#
@ -30,7 +30,7 @@ setup(
url = "http://calculate-linux.org",
license = "http://www.apache.org/licenses/LICENSE-2.0",
package_dir = {'calculate-lib': "."},
packages = ['calculate-lib.pym','calculate-lib.pym.format'],
packages = ['calculate-lib.pym'],
data_files = [("/usr/share/calculate/i18n",['i18n/cl_lib_ru.mo']),
("/var/calculate/remote",[])],
)

Loading…
Cancel
Save