commit 8d32480d79e613abf02b82af30c6ef4c16a6f124 Author: atratsevsky Date: Fri Jun 20 12:34:42 2008 +0000 git-svn-id: http://svn.calculate.ru/calculate2/calculate-lib/trunk@1 c91db197-33c1-4113-bf15-f8a5c547ca64 diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENCE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README b/README new file mode 100644 index 0000000..bfa5f51 --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +AUTHOR: Calculate Pack + +INSTALL +------- + +calculate-lib needs the following library version installed, in order to run: + Python >= 2.3 + python-ldap >= 2.0.0 + +To install calculate-lib , just execute the install script 'install.py'. +Example: + + ./install.py diff --git a/i18n/cl_lib_ru.mo b/i18n/cl_lib_ru.mo new file mode 100644 index 0000000..323ab4b Binary files /dev/null and b/i18n/cl_lib_ru.mo differ diff --git a/pym/__init__.py b/pym/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pym/cl_base.py b/pym/cl_base.py new file mode 100644 index 0000000..fefc36e --- /dev/null +++ b/pym/cl_base.py @@ -0,0 +1,1926 @@ +#-*- coding: utf-8 -*- + +#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru +# +# 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 gettext +import os +import getopt +import sys +############################################################################## +import re +import copy +import types +import string +#import os +import filecmp +import ConfigParser +import time +import socket +#import sys +import random +import string +import cl_utils +import cl_devices +############################################################################## + +_expand_lang = gettext._expand_lang + + +def __findFileMO(domain, localedir=None, languages=None, all=0): + # Модифицинрованный метод модуля gettext ищет файл перевода + if localedir is None: + localedir = _default_localedir + if languages is None: + languages = [] + for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + val = os.environ.get(envar) + if val: + languages = val.split(':') + break + if 'C' not in languages: + languages.append('C') + # now normalize and expand the languages + nelangs = [] + for lang in languages: + for nelang in _expand_lang(lang): + if nelang not in nelangs: + nelangs.append(nelang) + # select a language + if all: + result = [] + else: + result = None + for lang in nelangs: + if lang == 'C': + break + mofile = os.path.join(localedir, '%s_%s.mo' % (domain,lang)) + if os.path.exists(mofile): + if all: + result.append(mofile) + else: + return mofile + return result + +gettext.find = __findFileMO + +class GlobalParam(type): + """ Метакласс для глобальных параметров + """ + def __init__(cls, *args): + cls.GP = [] + cls.GP.append("") + +class lang: + """Класс многоязыковой поддержки lang для перевода сообщений программ на +другие языки. + +Типичное использование: + import sys + import lang + + # язык сообщений английский + tr = lang.lang(en) + # язык определяется системой + #tr = lang.lang() + + #Установка домена переводаldap + # в последующем можно не использовать - задается глобально + tr.setGlobalDomain('calc') + # задается локально для одного файла + #tr.setLocalDomain('calc') + + # Установка метода перевода для текущего модуля + tr.setLanguage(sys.modules[__name__]) + +Где: + tr -- объект перевода + 'en' -- язык выводимых сообщений + sys.modules[__name__] -- модуль сообщения которого переводятся + calc - домен перевода - имя файла перевода без расширения + +Если файл перевода не найден то сообщения не переводятся +Если в модуле сообщения которого переводим, экспортируются другие модули то +они тоже переводятся. + +По умолчанию директория в которой находятся переводы: 'lang/i18h' относительно +исполняемого файла названия файлов перевода совпадают с названиями модулей +если не определен методом setDomainTranslate() - домен перевода. + """ + __metaclass__ = GlobalParam + def __init__(self,l=''): + self.nameDomain = self.GP[0] + #self.nameDomain = '' + """ Название файла перевода (Домен) если используется 1 файл перевода + """ + self.__catalog = os.path.abspath('/usr/share/calculate/i18n') + """ Путь к каталогу переводов (в этом каталоге + ru_RU/LC_MESSAGES в котором файл перевода) + """ + env = os.environ + if l == "" and env.has_key('LANG'): + l = env['LANG'].split('.')[0].split("_")[0] + """ Определение языка """ + self.__modnames = {} + """ Словарь переведенных модулей + ключ --- имя модуля + значение --- был ли модуль переведен (1 или 0) + """ + self.__l = l + """Язык перевода для всех модулей""" + + def __translate(self,message): + """Метод translate возвращает полученное значение без +изменеsmfprjadnwний""" + return message + + def setLanguage(self,module): + """ Установка языка перевода для модуля module. + + параметр --- экспортируемый модуль python + если в этом модуле экспортируются другие модули + то язык устанавливается и для них + Метод запускается после экспорта модуля который будем переводить + """ + t = vars(module) + for i in dir(module): + q = str(t[i]) + if 'module' in q and not '__' in i and not '/usr/lib' in q\ + and not 'built-in' in q : + mod = vars(module)[i] + self.__setLang(mod) + return self.__setLang(module) + + def __setLang(self,module): + """ Установка языка перевода для модуля module. + + В случае нахождения файла перевода возвращает истину. + Во всех случаях устанавливает метод перевода для модуля. + Если нет файла перевода метод перевода возвращает то же + значение что получает + """ + if module.__name__ in self.__modnames.keys(): + return True + + if self.nameDomain == '': + if module.__name__ == "__main__": + nameDomain = module.__file__.split('.')[0] + else: + nameDomain = module.__name__ + else: + nameDomain = self.nameDomain + + if self.__l == 'en': + module._ = self.__translate + ret = 1 + else: + la = [] + la.append(self.__l) + if gettext.find(nameDomain,self.__catalog,la): + """Если найден словарь то инициализируем переводчик""" + transl = gettext.translation(nameDomain\ + ,self.__catalog,la) + module._ = transl.ugettext + ret = 1 + else: + module._ = self.__translate + ret = 0 + self.__modnames[module.__name__] = ret + return ret + + def getTranslatorByName(self,namemodule): + """ Метод который по имени модуля определяет, был ли модуль с этим + именем переведен + """ + if self.__modnames.has_key(namemodule): + return self.__modnames[namemodule] + return 0 + + def setGlobalDomain(self, nameDomain): + """ Метод для установки домена перевода (глобально для всех файлов) + """ + self.GP[0] = nameDomain + self.nameDomain = self.GP[0] + return True + + def setLocalDomain(self, nameDomain): + """ Метод для установки домена перевода (локально для одного файла) + """ + self.nameDomain = nameDomain + return True + +############################################################################## +# Перевод модуля на другой язык +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) +############################################################################## + +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.getopt() + 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() + 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 fillVars: + G_shr_path="/usr/calculate" + G_path=cl_utils.getpathenv() + G_calcname="Calculate" + + def __init__(self, objvar=None): + if objvar: + self._objvar=objvar + + #Вернуть результат выполнения команды ОС + def __runos(self,cmd,ret_first=None): + res=None + res=os.popen(cmd).readlines() + if res: + if len(res)>1: + if ret_first: + return res[0] + return res + else: + return res[0].strip() + + + def __getinifile(self,fname,def_sect): + s=ConfigParser.ConfigParser() + try: + s.read(fname) + return s + except ConfigParser.MissingSectionHeaderError: + f=open(fname,"rb") + s.add_section(def_sect) + for i in f.readlines(): + t=i.strip().split('=',1) + if len(t)==1: + s.set(def_sect,t[0],None) + elif len(t)>=2: + s.set(def_sect,t[0],t[1]) + else: + print 'Error parsing ini file' + return s + + def __getini(self, param): + inipath="/etc/calculate/calculate.ini" + if os.path.exists(inipath): + def_sect='default' + par=self.__getinifile(inipath,def_sect) + if par: + res={} + for i in par.options(def_sect): + res[i]=par.get(def_sect, i) + if res: + if res.has_key(param): + return res[param] + else: + return None + else: + return None + else: + return None + + #вернуть список переносимых путей при монтировании + def __getdisk_fstab(self, obj, lines, param): + g_load_dev=param['load_dev'] + g_install_dev=param['install_dev'] + Get=obj.Get + mount_dev=[] + mount_dir=[] + mount_line=[] + sch1='^\s*(\/dev\/[^\s]+)\s+([^\s]+)\s+([^\s]+)\s+\ +([^\s]+)\s+([0-9])\s+([0-9])\s*$' + sch2='^\s*([A-Za-z0-9\.]+:[^\s]+)\s+([^\s]+)\s+\ +([^\s]+)\s+([^\s]+)\s+([0-9])\s+ ([0-9])\s*$' + sch3='^\s*(\/[^\s]+)\s+(\/[^\s]+)\s+([^\s]+)\s+\ +(bind)\s+([0-9])\s+([0-9])\s*$' + for line in lines: + if re.search('^\s*#',line): + continue + res=re.search(sch1,line) + if not res: + res=re.search(sch2,line) + if not res: + res=re.search(sch3,line) + if res: + vals=res.groups() + if vals[0]=='/dev/%s%s'%(param['load_dev'],\ + param['load_num']): + continue + if vals[0]=='/dev/%s%s'%(param['install_dev'],\ + param['install_num']): + continue + res2=re.search('%s'%Get('sys_swap_dev'),vals[0]) + if res2: + continue + res2=re.search('^\/dev\/loop[0-9]$',vals[0]) + if res2: + continue + mount_dev.append(vals[0]) + mount_dir.append(vals[1]) + val3=" ".join(vals[2:]) + mount_line.append(val3) + return [mount_dev,mount_dir,mount_line] + + +#***************************************************************************** +#Заполнение переменных для Hardware +#***************************************************************************** + def __fillHardware(self): + obj=self._objvar + cm=self.__runos + Get=obj.Get + Set=obj.Set + #--------------------------------------------------------------------- + # Получим производителя оборудования + # $data{vendor}, $data{laptop}, $data{laptop_model} + # $data{board_model}, $data{board_vendor} + #--------------------------------------------------------------------- + if os.path.exists('/sys/class/dmi/id/chassis_vendor'): + vendor=cm('%s cat /sys/class/dmi/id/chassis_vendor' % self.G_path) + if re.search('^\s+$',vendor) or\ + re.search('Chassis Manufacture',vendor): + vendor='Chassis Manufacture' + Set('hrd_pc_vendor',vendor,True) + if os.path.exists('/sys/class/dmi/id/board_name'): + s=cm('%s cat /sys/class/dmi/id/board_name | grep "Portable PC"' % \ + self.G_path) + if s: + Set('hrd_laptop_vendor',s.lower,True) + s=cm('%s cat /sys/class/dmi/id/product_name' % self.G_path) + if s: + Set('hrd_laptop_model',s,True) + elif os.path.exists('/sys/class/dmi/id/board_vendor'): + if os.path.exists('/sys/class/dmi/id/board_name'): + bmodel=cm('%s cat /sys/class/dmi/id/board_name' %\ + self.G_path) + else: + bmodel='' + bvendor=cm('%s cat /sys/class/dmi/id/board_vendor' %\ + self.G_path) + Set('hrd_board_model',bmodel, True) + Set('hrd_board_vendor',bvendor, True) + + #---------------------------------------------------------------------- + # Получим архитектуру процессора, файл stage, маску нестабильных файлов + # $data{march}, $data{stage}, $data{unmask} + #---------------------------------------------------------------------- + march=cm('%s uname -m' % self.G_path) + if Get('setup_march') and (Get('setup_march')!=march): + Set('dif_march',True,True) + elif not Get('setup_march'): + Set('setup_march',march,True) + march=Get('setup_march') + if march=='i686': + Set('setup_unmask',"~x86",True) + elif march=='x86_64': + Set('setup_unmask',"~amd64",True) + + #---------------------------------------------------------------------- + # Определим кодичество процессоров + # $data{cpus} + #---------------------------------------------------------------------- + if not Get('hrd_cpu_num'): + cpucnt=0 + if os.path.exists('/proc/cpuinfo'): + for record in open('/proc/cpuinfo','rb').readlines(): + try: + param, pval=record.split(":") + except: + continue + if param.strip()=='processor': + cpucnt+=1 + Set('hrd_cpu_num',cpucnt) + + #---------------------------------------------------------------------- + # Определим видеоадаптер + # $data{video}, $data{video_drv} + #---------------------------------------------------------------------- + suppdrv=("nvidia", "i810", "vesa", "vmware") + nvdrv=('nVidia Aladdin TNT2','GeForce 6200','GeForce DDR',\ + 'GeForce2','GeForce4','Quadro2','Quadro4','RIVA TNT2','Vanta') + cards=cl_devices.Video().getcards() + if cards: + video=cards[0] + if re.search('GeForce',video) or \ + re.search('nVidia',video): + if re.search('\[([^\]]+)\]',video): + model="nVidia "+re.search('\[([^\]]+)\]',video).groups()[0] + Set('hrd_video_name',model,True) + elif re.search('(GeForce.+) nForce',video): + model="nVidia" + model+=re.search('(GeForce.+) nForce',video).groups()[0] + Set('hrd_video_name',model,True) + else: + Set('hrd_video_name',"nVidia GeForce",True) + video=Get('hrd_video_name') + if video not in nvdrv: + if not Get('hrd_video_drv'): + Set('hrd_video_drv','nvidia',True) + if not Get('hrd_opengl_set'): + Set('hrd_opengl_set','nvidia',True) + else: + if not Get('hrd_video_drv'): + Set('hrd_video_drv','vesa',True) + elif re.search('Intel', video): + res=re.search("Intel Corporation ([^\s]+)\s") + if res: + model="Intel "+res.groups()[0] + else: + model="Intel" + if not Get('hrd_video_drv'): + Set('hrd_video_drv','i810',True) + if not Get('hrd_opengl_set'): + Set('hrd_opengl_set','xorg-x11',True) + elif re.search('VMware', video): + if not Get('hrd_video_drv'): + Set('hrd_video_drv','vmware',True) + else: + if not Get('hrd_video_drv'): + Set('hrd_video_drv','vesa',True) + if Get('hrd_opengl_set') and not Get('cmd_run_opengl'): + val="/usr/bin/eselect opengl set %s"%(Get('hrd_opengl_set')) + Set('cmd_run_opengl',val,True) + + #--------------------------------------------------------------------- + # Определим разрешение экрана + # $data{resolution} + #--------------------------------------------------------------------- + xlog = "/var/log/Xorg.0.log" + def_res = "1024x768" + if not Get('hrd_video_res') and os.path.exists(xlog): + f=open(xlog, "rb") + sf=f.read() + f.close() + fstr="Virtual screen size determined to be ([0-9]+)\s*x\s*([0-9]+)" + if re.search(fstr, sf): + res=re.search(fstr, sf).groups() + resol=res[0]+"x"+res[1] + Set('hrd_video_res',resol,True) + elif re.search('Setting mode \"([0-9\@x]+)\"',sf): + res=re.search('Setting mode \"([0-9\@x]+)\"',sf).groups() + Set('hrd_video_res',res[0],True) + elif re.search('Virtual size is ([0-9\@x]+) ',sf): + res=re.search('Virtual size is ([0-9\@x]+) ',sf).groups() + Set('hrd_video_res',res[0],True) + elif re.search('Modeline "([0-9\@x]+)"',sf): + res=re.search('Modeline "([0-9\@x]+)"',sf).groups() + Set('hrd_video_res',res[0],True) + else: + Set('hrd_video_res',def_res,True) + if not Get('hrd_video_res'): + Set('hrd_video_res',def_res,True) + + #---------------------------------------------------------------------- + # Определим USB устройства + # $data{usb_*} + #---------------------------------------------------------------------- + res=cm('%s lsusb 2>/dev/null' % self.G_path) + sstr='^Bus [0-9]+ Device [0-9]+: ID [0-9a-f]+:[0-9a-f]+ ([^\s].+)$' + if res: + for i in res: + sres=re.search(sstr,i) + if sres: + val=sres.group(1) + if re.search('Hewlett-Packard',val): + Set('hrd_usb_hp', True, True) + elif re.search('Bluetooth', val): + Set('hrd_usb_bluetooth', True, True) + +#***************************************************************************** +#Заполнение переменных для Builder +#***************************************************************************** + def fillBuilder(self): + obj=self._objvar + cm=self.__runos + Get=obj.Get + Set=obj.Set + getini=self.__getini + G_path=Get('setup_path_env') + Set('setup_path_install',"/mnt/builder",True) + #Set('setup_pass','builder',True) + #---------------------------------------------------------------------- + # Определим ссылки на исходники программ + #---------------------------------------------------------------------- + if not Get('setup_gentoo_mirrors'): + val="http://mirror.yandex.ru/gentoo-distfiles \ +http://distfiles.gentoo.org \ +http://distro.ibiblio.org/pub/linux/distributions/gentoo" + Set('setup_gentoo_mirrors',val,True) + #--------------------------------------------------------------------- + # Версию портежей + #--------------------------------------------------------------------- + if not Get('setup_gentoo_release'): + Set('setup_gentoo_release',"2007.0",True) + #--------------------------------------------------------------------- + # Определим наличие stage, portage + #--------------------------------------------------------------------- + if not Get('setup_path_portagepkg'): + portagepath="/usr/calculate/snapshots/" + if not os.path.exists(portagepath): + cm(self.G_path+" mkdir "+portagepath) + else: + rdir=os.listdir(portagepath) + last=0 + for i in rdir: + res=re.search('^portage\-([0-9]+)\.tar\.bz2$',i) + if not res: + continue + else: + if int(res.groups()[0])>last: + last=int(res.groups()[0]) + if last: + Set('setup_path_portagepkg', "%sportage-%s.tar.bz2"%\ + (portagepath,last),True) + #---------------------------------------------------------------------- + # путь к stages + #---------------------------------------------------------------------- + if not Get('setup_path_stagepkg'): + march=Get('setup_march') + stagepath="/usr/calculate/stages/" + if march=='i686': + val="stage3-i686-%s.tar.bz2"%s(Get('setup_gentoo_release')) + if os.path.exists(stagepath+val): + Set('setup_path_stagepkg',stagepath+val,True) + elif march=='x86_64': + val="stage3-amd64-%s.tar.bz2" %(Get('setup_gentoo_release')) + if os.path.exists(stagepath+val): + Set('setup_path_stagepkg',stagepath+val,True) + + #---------------------------------------------------------------------- + # Команда создания учетных записей пользователей + #---------------------------------------------------------------------- + try: + f=open('/etc/passwd') + PASSWD=f.readlines() + f.close() + except: + print "Error opening file: passwd" + try: + f=open('/etc/group') + GROUP=f.readlines() + f.close() + except: + print "Error opening file: group" + if PASSWD: + uval="" + for pline in PASSWD: + user=[i.strip() for i in pline.split(":")] + #извлечем пользователей, добавленных после установки системы + if int(user[2])>=500 and\ + int(user[2])<6500 and\ + int(user[2])!=99: + grp="" + for gline in GROUP: + group=[i.strip() for i in gline.split(":")] + gusers=None + if group[-1]: + gusers=group[-1].split(",") + if gusers: + if user[0] in gusers: + if not grp: + grp=group[0] + else: + grp=grp+","+group[0] + groups="" + if grp: + groups="--groups "+grp + val="\n/usr/sbin/useradd --uid %s --gid %s --home-dir %s"%\ + (user[2],user[3],user[5]) + val+=" --shell %s %s %s" %\ + (user[6], groups, user[0]) + uval+=val + Set('setup_run_user',uval,True) + #--------------------------------------------------------------------- + # Определим запущенные сервисы CDS + #--------------------------------------------------------------------- + for i in ('ldap','mail','ftp'): + if getini("up.%s"%i)=="on": + Set('sys_set_%s'%i,True,True) + else: + Set('sys_set_%s'%i,False,True) + + #--------------------------------------------------------------------- + # Сформируем хэш конвертации устаревшего формата hda на sda + #--------------------------------------------------------------------- + dev_list=cl_devices.block_dev() + disks={'removable':[],'sata':[],'ata':[]} + for dev in dev_list.get_listdev(): + if dev_list.isremovable(dev): + val=disks['removable'] + disks['removable']=val+[dev] + else: + if dev[:2]=='sd': + val=disks['sata'] + disks['sata']=val+[dev] + else: + val=disks['ata'] + disks['ata']=val+[dev] + sata=None + ata={} + if len(disks['ata'])>0: + devs=[] + devkey=disks['sata']; devkey.sort() + devs+=devkey + devkey=disks['ata']; devkey.sort() + devs+=devkey + devkey=disks['removable']; devkey.sort() + devs+=devkey + for i in devs: + if not sata: + sata='sda' + else: + val=re.search('sd([a-z])',i) + if val: + val=val.groups()[0] + else: + val=re.search('sd([a-z])',sata).groups()[0] + sata='sd'+(chr(ord(val)+1)) + ata[i]=sata + Set('setup_ata',ata,True) + +#***************************************************************************** +#Заполнение переменных для LDAP +#***************************************************************************** + def fillLDAP(self): + obj=self._objvar + if not obj: + return False + #сокращенные имена для вызовов + cm=self.__runos + Get=obj.Get + Set=obj.Set + getini=self.__getini + prinst=['/usr/lib/calculate/calculate-server/profile'] + pruser=['/usr/calculate/profile/server'] + if not os.path.exists(prinst[0]): + prinst='' + if not os.path.exists(pruser[0]): + pruser='' + Set('setup_path_install','/',True) + Set('setup_path_profinstall',\ + prinst,True) + Set('setup_path_profuser',\ + pruser,True) + #переменные необходимые для работы + g_hostname=Get('net_host') + g_domain=Get('sys_domain') + #-------------------------------------------------------------------- + # Настройки PDC + #-------------------------------------------------------------------- + Set('soft_samba_domain',"CDS",True) + Set('soft_samba_netbios',"PDC-CDS",True) + #---------------------------------------------------------------------- + # Определим путь к LDAP-записям пользователей + #---------------------------------------------------------------------- + #базовый суффикс + Set('soft_ldap_base',"dc=CDS",True) + #алгоритм шифрования паролей + Set('soft_ldap_hash_encrypt',"{SSHA}",True) + #пользовательский доступ к базе с доступом только для чтения + Set('soft_ldap_bindname',"proxyuser",True) + Set('soft_ldap_bind',"cn=%s,%s"%\ + (Get('soft_ldap_bindname'),\ + Get('soft_ldap_base'))\ + ,True) + Set('soft_ldap_bindpw',"CDS",True) + val="%s slappasswd -s %s -h %s"%\ + (self.G_path,\ + Get('soft_ldap_bindpw'),\ + Get('soft_ldap_hash_encrypt')) + res=cm(val) + if res: + Set('soft_ldap_bindpw_hash',res,True) + #временный пользователь root для инициализации базы данных + Set('soft_ldap_root_tmp', "cn=ldaproot,%s"%Get('soft_ldap_base'),\ + True) + Set('soft_ldap_rootpw_tmp',cl_utils.genpassword().strip(),True) + cstr='%s slappasswd -s %s -h %s'%\ + (self.G_path,\ + Get('soft_ldap_rootpw_tmp'),\ + Get('soft_ldap_hash_encrypt') + ) + res=cm(cstr) + if res: + Set('soft_ldap_rootpw_tmp_hash',res.strip(),True) + #постоянный пользователь root, прописываемый в базу при первой загрузке + #с одновременным удалением временного root-а + Set('soft_ldap_rootname',"ldapadmin",True) + Set('soft_ldap_root',"cn=%s,%s"%\ + (Get('soft_ldap_rootname'),\ + Get('soft_ldap_base')\ + ),True) + if os.path.exists('/etc/smbldap-tools/smbldap_bind.conf'): + cstr='%s cat /etc/smbldap-tools/smbldap_bind.conf | grep masterPw'\ + % self.G_path + res=cm(cstr) + rs=re.search('[^=]+="(.+)',res.strip()) + if rs: + Set('soft_ldap_rootpw',rs.group(1),True) + if not Get('soft_ldap_rootpw') or\ + Get('soft_ldap_rootpw')=='secret': + Set('soft_ldap_rootpw',cl_utils.genpassword().strip(),True) + cstr='%s slappasswd -s %s -h %s'%\ + (self.G_path,\ + Get('soft_ldap_rootpw'),\ + Get('soft_ldap_hash_encrypt')\ + ) + res=cm(cstr) + if res: + Set('soft_ldap_rootpw_hash',res.strip(),True) + +#***************************************************************************** +#Заполнение глобальных переменных +#***************************************************************************** + def fillGlobal(self): + obj=self._objvar + cm=self.__runos + Get=obj.Get + Set=obj.Set + getini=self.__getini + #получим путь для выполнения команд + if self.G_path: + Set('setup_path_env',self.G_path,True) + else: + print "Not found path for execute commands" + sys.exit(0) + if not os.path.exists(self.G_shr_path): + print "Not found Calculate dir: %s"%self.G_shr_path + sys.exit(0) + #********************************************** + #checkerrors + #********************************************** + g_install_dev="" + g_install_num="" + if Get('setup_installdev') and\ + re.search('^([a-z]+)([0-9]+)$',Get('setup_installdev')): + val=re.search('^([a-z]+)([0-9]+)$',Get('setup_installdev')) + if val: + vals=val.groups() + g_install_dev=vals[0] + g_install_num=vals[1] + Set('setup_installdev',vals[0]+vals[1],True) + if Get('setup_installdev') and\ + (not g_install_dev or\ + not g_install_num or\ + not os.path.exists('/dev/%s%s'%\ + (g_install_dev,\ + g_install_num))): + print "Incorrect partition" + sys.exit(0) + #--------------------------------------------------------------------- + # Проверим id пользователя, запустившего скрипт + #--------------------------------------------------------------------- + if os.getegid()!=0: + print "Only root can perform system installation" + sys.exit(0) + + if Get('setup_sys_shortname'): + sh_name=Get('setup_sys_shortname') + ver=Get('setup_dist_ver') + if ver.has_key(sh_name.upper()): + Set('setup_sys_fullname',ver[sh_name.upper()],True) + if not Get('setup_sys_shortname'): + #Set('setup_pass','install',True) + path='/etc/issue' + #если файл с версией системы найден, определим версию + sp="""Welcome to \\\\n.\\\\O \(([a-zA-Z ]+) ([^\s\)]+)""" + res=cm('%scat %s | grep "Welcome to "'%\ + (self.G_path,\ + path)) + if res: + if re.search(sp,res): + vals=re.search(sp,res).groups() + issuename=vals[0] + ver=Get('setup_dist_ver') + for i in ver.keys(): + if ver[i]==issuename: + Set('setup_sys_shortname',i,True) + Set('setup_sys_fullname',ver[i],True) + break + #если имя не определено, но оно новое и мы его уже + #передали параметром --configure + if not Get('setup_sys_shortname'): + Set('setup_sys_fullname',issuename,True) + spl=issuename.split(" ") + nname="" + if len(spl)>1: + for i in spl: + nname+=i[1] + Set('setup_sys_shortname',nname,True) + else: + Set('setup_sys_shortname',issuename,True) + #устанавливаемая или собираемая система по умолчанию + if not Get('setup_sys_shortname'): + Set('setup_sys_shortname',"CLD",True) + val=Get('setup_dist_ver')['CLD'] + Set('setup_sys_fullname',val,True) + #---------------------------------------------------------------------- + # Получим тип системы + #---------------------------------------------------------------------- + if re.search('server',Get('setup_sys_fullname')): + Set('sys_linux_type','server',True) + else: + Set('sys_linux_type','desktop',True) + + #--------------------------------------------------------------------- + #одетектим устройства + self.__fillHardware() + #--------------------------------------------------------------------- + Set('setup_name',self.G_calcname+" "+Get('setup_ver'),True) + #--------------------------------------------------------------------- + # Выполним проверки доступности необходимых программ и определим пути + # exec_host, exec_nmap + #--------------------------------------------------------------------- + if not os.system("which host >/dev/null 2>&1"): + res=os.popen("which host").readlines() + Set('cmd_exec_host',res[0].rstrip(),True) + if not os.system("which nmap >/dev/null 2>&1"): + res=os.popen("which nmap").readlines() + Set('cmd_exec_nmap',res[0].rstrip(),True) + + #--------------------------------------------------------------------- + # Установим значение переменной в ram, в случае если установка профиля + # производится в оперативную память (настройка сеанса CDROM), а также + # load - носитель в значение ram в случае загрузки с CD/DVD + #--------------------------------------------------------------------- + Set('sys_load',"", True) + if cm("""mount | grep "/dev/loop/0 on / type" """): + Set('sys_load',"ram", True) + + #--------------------------------------------------------------------- + # Считаем имя компьютера + #--------------------------------------------------------------------- + if Get('net_host') is None: + hostname=cm("""%s hostname -s 2>&1"""%self.G_path) + Set('net_host',hostname, True) + #упрощенный вариант, следует выполнять только если не указан домен + #в системе + if re.search("^hostname: ",hostname): + hostname=cm("""%s hostname 2>&1"""%self.G_path) + if re.search("^hostname: ",hostname): + Set('net_host',Get('setup_sys_shortname'), True) + else: + if hostname=='livecd': + Set('net_host',Get('setup_sys_shortname'), True) + + #---------------------------------------------------------------------- + # Определим домен + #---------------------------------------------------------------------- + if not Get('sys_domain'): + domain=cm("%s hostname -d 2>&1"%self.G_path) + Set('sys_domain',domain, True) + if re.search("^hostname: ",domain): + Set('sys_domain',"local", True) + + #---------------------------------------------------------------------- + # Определим сетевые настройки: + # шлюз, локальные интерфейсы, локальные сети, настройки + #/etc/conf.d/net, + # сетевые устройства + #---------------------------------------------------------------------- + if not Get('net_gw'): + Set('net_conf',"",True) + Set('net_hosts_allow',"",True) + Set('net_networks',"",True) + route=cm("""%sroute -n | grep "^0.0.0.0" """%self.G_path) + route=re.split("\s+",route) + if route: + Set('net_gw', route[1],True) + Set('net_gw_dev', route[7],True) + net={'255.255.0.0':'/16',\ + '255.255.255.0':'/24',\ + '255.255.255.128':'/25',\ + '255.255.255.252':'/30',\ + '255.255.255.255':''} + netpath='/sys/class/net/' + nets={} + net_if=cl_utils.getdirlist(netpath) + for i in net_if: + if i=='lo': + continue + res=cm("cat %s%s/operstate"%(netpath,i)) + if res=='up': + nets[i]=True + elif res=='down': + nets[i]=False + else: + nets[i]=None + ip={} + host_allow="" + networks="" + netconf="" + gateway_dhcp=0 + dev_open=0 + for i in nets.keys(): + res=cm("/sbin/ifconfig %s"%i) + #res=cm("ifconfig %s"%i) + sline="""""" + for j in res: + s_ip=re.search('addr:([0-9\.]+).+Bcast:.+Mask:([0-9\.]+)'\ + ,j) + if s_ip: + ip, netmask=s_ip.groups() + host_allow+=ip+net[netmask]+"" + ipd=ip + ip=ip.split('.'); + if ip[0]=='10' or\ + (ip[0]=='172' and int(ip[1])>=16 and int(ip[1])<=31)or\ + (ip[0]=='192' and ip[1]=='168'): + if netmask=='255.255.255.255': + networks+=ip+" " + elif netmask=='255.255.255.252': + networks+=ip[0]+"."+ip[1]+"."+ip[2]+"."+"252"+\ + net[netmask] + elif netmask=='255.255.255.128': + networks+=ip[0]+"."+ip[1]+"."+ip[2]+"."+"128"+\ + net[netmask] + elif netmask=='255.255.255.0': + networks+=ip[0]+"."+ip[1]+"."+ip[2]+"."+"0"+\ + net[netmask] + elif netmask=='255.255.0.0': + networks+=ip[0]+"."+ip[1]+".0.0"+net[netmask] + #определим получен ли IP через DHCP + #print host_allow,networks + if not os.path.exists(\ + '/var/lib/dhcpcd/dhcpcd-%s.info'%i) and\ + not (os.path.exists(\ + '/var/lib/dhcp/dhclient.leases') and \ + cm('grep %s /var/lib/dhcp/dhclient.leases'%i)): + netconf+="\nconfig_%s=(\"%s\")"%(i,ipd\ + +net[netmask]) + else: + netconf+="\nconfig_%s=(\"dhcp\")"%(i) + if i==Get('net_gw_dev'): + gateway_dhcp+=1 + nets[i]=True + else: + netconf+="\nconfig_%s=(\"%s%s\")"%(i,\ + ipd,net[netmask]) + dev_open=1 + netconf+="\nmodules_eth0=( \"!plug\" )" + if dev_open==0: + nets[i]=False + if Get('net_gw_dev') and gateway_dhcp==0: + netconf+="\nroutes_%s=(\"default gw %s\" )"%\ + (Get('net_gw_dev'), Get('net_gw')) + n_key=nets.keys() + n_key.sort() + if n_key: + Set('net_lan',n_key[0],True) + tval=Get('net_networks') + Set('net_networks',tval+networks,True) + tval=Get('net_hosts_allow') + Set('net_hosts_allow',tval+host_allow,True) + tval=Get('net_conf') + Set('net_conf',tval+netconf,True) + + #--------------------------------------------------------------------- + # Определим сетевые сервисы + #--------------------------------------------------------------------- + #определим прокси-сервер + if Get('cmd_exec_host') and\ + Get('sys_domain') and\ + cm('%s proxy.%s | grep "has address"'%\ + (Get('cmd_exec_host'), \ + Get('sys_domain'))): + ports=[3128,8080] + for port in ports: + sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM) + arg_addr=('proxy.%s'%Get('sys_domain'),port) + if sock.connect_ex(arg_addr)==0: + if not Get('net_proxy'): + Set('net_proxy','proxy.%s'%Get('sys_domain'),True) + if not Get('net_proxy_port'): + Set('net_proxy_port',str(port),True) + if not Get('net_proxy_url'): + val="http://%s:%s"%\ + (Get('net_proxy'),\ + Get('net_proxy_port')) + Set('net_proxy_url',val,True) + + #определим наличие сетевой файловой системы + sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if Get('cmd_exec_host') and\ + not Get('net_nfs') and\ + Get('sys_domain') and\ + cm('%s nfs.%s | grep "has address"'%\ + (Get('cmd_exec_host'),\ + Get('sys_domain'))) and\ + sock.connect_ex(('nfs.%s'%Get('sys_domain'),2049))==0: + Set('net_nfs','nfs.%s'%Get('sys_domain'),True) + + #определим наличие локального сервера времени + if not Get('net_ntp'): + if Get('cmd_exec_host') and\ + Get('sys_domain') and\ + cm('%s ntp.%s | grep "has address"'%\ + (Get('cmd_exec_host'),\ + Get('sys_domain'))): + Set('net_ntp','ntp.%s'%Get('sys_domain'),True) + else: + Set('net_ntp','ntp0.zenon.net',True) + + #--------------------------------------------------------------------- + # Определим наличие сервера CDS + #--------------------------------------------------------------------- + if not Get('net_cds'): + if Get('sys_domain'): + Set('net_cds','cds.%s'%Get('sys_domain'),True) + else: + Set('net_cds','cds',True) + + #--------------------------------------------------------------------- + # Получим версию обновляемой, наличие обновлений, название + # устанавливаемой системы + #--------------------------------------------------------------------- + sp="""Welcome to \\\\n.\\\\O \(([a-zA-Z ]+) ([^\s\)]+)""" + if os.path.exists('/etc/issue'): + cline=""" """ + res=cm('%s cat /etc/issue | grep "Welcome to "'%self.G_path) + if res: + par=re.search(sp,res).groups() + Set('setup_os_current',par[0],True) + res_n=par[1].split("-") + patch="" + osn="" + if len(res_n)>1: + patch=res_n[1] + Set('sys_patchcur',patch,True) + Set('sys_current_ver',res_n[0],True) + if not par[0]: + Set('setup_os_current','Calculate',True) + if par[1]: + Set('setup_ospatchsplit',"-",True) + elif os.path.exists('/etc/gentoo-release'): + Set('setup_os_current',"Gentoo Linux",True) + else: + Set('setup_os_current',"Old Linux",True) + + #--------------------------------------------------------------------- + # Заголовок изменённых файлов + #--------------------------------------------------------------------- + #Changed by installator + if not Get('setup_mestitle'): + val='# Изменено %s %s' % (self.G_calcname, Get('setup_ver')) + Set('setup_mestitle',val,True) + if not Get('setup_mesline'): + val="#------------------------------------------------------------\ +------------------" + Set('setup_mesline',val,True) + + #--------------------------------------------------------------------- + # Определим диск, с которого загружена система + #--------------------------------------------------------------------- + for record in open('/proc/cmdline','rb').readlines(): + re_res=re.search('^root=\/dev\/([a-z]+[0-9]).*',record.strip()) + if re_res: + res=re_res.group(1) + break + g_load_dev="" + g_load_num="" + if res: + fstr="^([a-z]{3})([0-9]+)" + fres=re.search(fstr, res) + if fres: + vals=fres.groups() + g_load_dev=vals[0] + g_load_num=vals[1] + #--------------------------------------------------------------------- + # Определим диск и номер раздела для установки + #--------------------------------------------------------------------- + #выберем альтернативный диск для установки + if not Get('setup_installdev') and\ + getini('install.from'): + res=getini('install.from') + Set('setup_installdev',res,True) + val=re.search('^([a-z]+)([0-9]+)$',res) + if val: + vals=val.groups() + g_install_dev=vals[0] + g_install_num=vals[1] + elif not Get('setup_installdev') and\ + not g_install_dev and\ + not g_install_num and\ + g_load_dev: + g_install_dev=g_load_dev + if int(g_load_num)==2: + g_install_num=3 + elif int(g_load_num)==3: + g_install_num=2 + Set('setup_installdev',g_install_dev+\ + str(g_install_num),True) + #проверим на существование диска + if not os.path.exists("/dev/%s"%Get('setup_installdev')): + print "Incorrect partition" + sys.exit(0) + + #--------------------------------------------------------------------- + # Выберем доступную утилиту форматирования + #--------------------------------------------------------------------- + if not Get('setup_formatfs'): + #попытаемся подмонтировать, чтобы получить информацию о + #файловом формате + format="" + cstr="%s mkdir -p %s" % (self.G_path, Get('setup_path_install')) + cm(cstr) + cstr="%s mount /dev/%s%s %s &>/dev/null"\ + % (self.G_path,\ + g_install_dev,\ + g_install_num,\ + Get('setup_path_install')) + if not os.system(cstr): + cstr="%s mount | grep /dev/%s%s"\ + %(self.G_path,\ + g_install_dev,\ + g_install_num) + format=cm(cstr,True) + cstr="%s umount %s"%(self.G_path, Get('setup_path_install')) + os.system(cstr) + else: + cstr="%s rmdir %s"%(self.G_path, Get('setup_path_install')) + os.system(cstr) + if format: + fs=None + if re.search(' reiserfs ',format.strip()): + fs='reiserfs' + elif re.search(' xfs ',format.strip()): + fs='xfs' + elif re.search(' ext3 ',format.strip()): + fs='ext3' + elif re.search(' ext2 ',format.strip()): + fs='ext2' + Set('setup_formatfs',fs,True) + + formatrun={"reiserfs":'"/sbin/mkfs.reiserfs -f"',\ + "xfs":"/sbin/mkfs.xfs -f",\ + "ext3":"/sbin/mkfs.ext3",\ + "ext2":"/sbin/mkfs.ext2"} + + if Get('setup_formatfs') in formatrun.keys() and\ + os.path.exists("/sbin/mkfs.%s"%(Get('setup_formatfs'))): + val=formatrun[Get('setup_formatfs')] + Set('cmd_run_format',val,True) + else: + if os.path.exists('/sbin/mkfs.reiserfs'): + Set('setup_formatfs','reiserfs',True) + elif os.path.exists('/sbin/mkfs.xfs'): + Set('setup_formatfs','xfs',True) + elif os.path.exists('/sbin/mkfs.ext3'): + Set('setup_formatfs','ext3',True) + elif os.path.exists('/sbin/mkfs.ext2'): + Set('setup_formatfs','ext2',True) + else: + print "Formatting utility not found" + sys.exit(0) + + #--------------------------------------------------------------------- + # Хэш доступных grub-у дисков, диск и раздел для установки в grub + #--------------------------------------------------------------------- + sdir="/sys/block/" + ldir=cl_utils.getdirlist(sdir) + grub_dev=[] + #определим неизвлекаемые устройства + if ldir: + for dev in ldir: + if re.search('^(sd|hd)',dev): + cstr="%s cat %s%s/removable" %(self.G_path,\ + sdir,\ + dev) + val=cm(cstr) + if val: + if int(val[0].strip())==0: + grub_dev.append(dev.strip()) + grub_dev.sort() + num=0 + grub_map={} + for dev in grub_dev: + grub_map['hd'+str(num)]=dev + if g_install_dev==dev: + Set('boot_grub_setupdev','hd'+str(num),True) + if g_load_dev==dev: + Set('boot_grub_loaddev','hd'+str(num),True) + num+=1 + g_grub_map=grub_map + if g_install_num: + install_num=int(g_install_num)-1 + Set('boot_grub_setupnum',str(install_num),True) + if g_load_num: + load_num=int(g_load_num)-1 + Set('boot_grub_loadnum',load_num,True) + Set('boot_grub_map',g_grub_map,True) + #--------------------------------------------------------------------- + # Диски файла devicemap для grub-а + #--------------------------------------------------------------------- + if not Get('boot_devicemap'): + grubmap=g_grub_map + grubkeys=grubmap.keys() + grubkeys.sort() + devmap="" + for dev in grubkeys: + devmap+="%s /dev/%s\n" % (dev, grubmap[dev]) + Set('boot_devicemap',devmap.strip(),True) + #Скопируем карту устройств для установки граба. Новая карта на libata + #может отличаться + Set('boot_devicemap_old',Get('boot_devicemap'),True) + + #-------------------------------------------------------------------- + # Считаем меню граба grub.conf для переноса настроек + #-------------------------------------------------------------------- + if Get('sys_load')!='ram': + grub="/boot/grub/grub.conf" + if os.path.exists(grub): + f=open(grub,"rb") + fstr=f.readlines() + f.close() + config="" + record="" + skip=None + for line in fstr: + if re.search('^\s*title[\s|=]+',line): + if record and not skip: + config+=record + record=line + skip=0 + else: + if re.search('[^\s]', line) and record: + sstr="^\s*kernel .*root=\/dev\/%s" %\ + Get('setup_installdev') + if Get('setup_installdev') and\ + re.search(sstr,line): + skip+=1 + record+=line + if record and not skip: + config+=record + Set('boot_grub_another',config,True) + else: + oldmarch=cm("%s uname -m"%self.G_path) + curos=None + if Get('sys_current_ver'): + curos=Get('sys_current_ver') + val ="\ntitle %s %s %s\n" %\ + (Get('setup_os_current'),\ + curos,\ + oldmarch.strip()) + val+="root (%s,%s)\n" %\ + (Get('boot_grub_loaddev'),\ + Get('boot_grub_loadnum')) + val+="kernel /boot/vmlinuz root=/dev/%s%s" %\ + (Get('load_dev'),\ + Get('load_num')) + Set('boot_grub_another',val,True) + + if not Get('setup_set_mbr'): + Set('setup_set_mbr',True,True) + else: + Set('setup_set_mbr',False,True) + + #--------------------------------------------------------------------- + # Определим раздел свопа + # $data{swap} + #--------------------------------------------------------------------- + if not Get('sys_swap_dev'): + res=cm('%s tail -n 1 /proc/swaps' % self.G_path) + if res: + val=re.search('^\/dev\/([^\s]+)',res).group(1) + Set('sys_swap_dev',val,True) + elif Get('sys_swap_dev'): + res=re.search('^\/dev\/(.+)$',Get('sys_swap_dev')) + if res: + val=res.group(1) + Set('sys_swap_dev',val,True) + if not Get('sys_swap_dev') and Get('install_dev'): + scm='LANG=C fdisk -l /dev/%s 2>&1 | grep "Linux swap"' %\ + (Get('install_dev')) + res=cm(scm) + if res: + val=re.search('^\/dev\/([^\s]+)',res) + if val: + Set('sys_swap_dev',val.group(1),True) + if Get('sys_swap_dev'): + val='/dev/%s\tnone\t\tswap\tsw\t\t\t0 0'%(Get('sys_swap_dev')) + Set('sys_swap_line',val,True) + + #--------------------------------------------------------------------- + # С текущей системы определим дополнительные монтируемые диски + # (в том числе сетевые), пути монтирования + #--------------------------------------------------------------------- + if os.path.exists('/etc/fstab'): + f=open('/etc/fstab',"rb") + lines=f.readlines() + f.close() + param_t={'load_dev':g_load_dev,'install_dev':g_install_dev,\ + 'load_num':g_load_num,'install_num':g_install_num} + res=self.__getdisk_fstab(obj, lines, param_t) + if len(res[0])>0: + Set('sys_mount_dev',res[0],True) + Set('sys_path_mounts',res[1],True) + Set('sys_mount_line',res[2],True) + else: + Set('sys_mount_dev',"",True) + Set('sys_path_mounts',"",True) + Set('sys_mount_line',"",True) + + #--------------------------------------------------------------------- + # Определим версию последнего доступного образа системы, либо версию + # собираемого образа, имя файла с образом, имя ISO-файла, который можно + # собрать + #--------------------------------------------------------------------- + #установка системы + if os.path.exists("%s/linux"%(self.G_shr_path)) and\ + Get('sys_load')!='ram': + find="(%s\-.*\-%s\.tar\.bz2)" %\ + (Get('setup_sys_shortname').lower(),\ + Get('setup_march')) + scm=self.G_path+" find -L %s/linux"%(self.G_shr_path) + f_res=cm(scm) + if not isinstance(f_res, types.ListType): + f_res=[f_res] + for i in f_res: + res=re.search('(%s)'%find, i.strip()) + if res: + ver=res.groups()[0] + linux=Get('setup_linuxpkg') + if not linux: + Set('setup_linuxpkg',ver,True) + ver=Get('setup_linuxpkg') + if ver: + val=re.search('\-([\d\.]+)\-.*',ver) + if val: + Set('setup_linux_ver',val.group(1),True) + if not Get('setup_iso_name'): + val="%s-%s-%s.iso" % (Get('setup_sys_shortname').lower(), + Get('setup_linux_ver'), Get('setup_march')) + Set('setup_iso_name',val,True) + elif Get('sys_load')=='ram' and os.path.exists('/mnt/livecd'): + Set('setup_linux_ver',Get('sys_current_ver'), True) + Set('setup_linuxpkg','livecd', True) + + #--------------------------------------------------------------------- + # Пути к архиву образа + #--------------------------------------------------------------------- + #пути к архивам систем + Set('setup_path_linuxpkg',"%s/linux"%(self.G_shr_path),True) + #---------------------------------------------------------------------- + # Определим и проверим директорию установки, директорию временных + #файлов + #---------------------------------------------------------------------- + if not Get('setup_path_tmp'): + Set('setup_path_tmp','/tmp/calculate',True) + if not Get('setup_path_install'): + Set('setup_path_install',"/mnt/install",True) + + #---------------------------------------------------------------------- + # Получим список доступных патчей + #---------------------------------------------------------------------- + g_patches=None + if Get('sys_current_ver'): + if not Get('setup_path_patch'): + val=self.G_shr_path+"/patch" + Set('setup_path_patch',val,True) + if not Get('sys_patchlast'): + Set('sys_patchlast',Get('sys_patchcur'),True) + if not os.path.exists(Get('setup_path_patch')): + print "Not found patch dir: %s"%(Get('setup_path_patch')) + #sys.exit(0) + else: + rdir=os.listdir(Get('setup_path_patch')) + if not g_patches: + g_patches=[] + patchlast=Get('sys_patchlast') + if not patchlast: + patchlast=0 + for i in rdir: + dcalculate=Get('setup_sys_shortname') + dlinuxver=Get('setup_linux_ver') + fstr='^%s\-%s\-([0-9]+)$'%(dcalculate, dlinuxver) + res=re.search(fstr, i) + if not res: + continue + val=int(res.groups()[0]) + if not Get('sys_patchcur'): + Set('sys_patchcur',0, True) + if val > int(Get('sys_patchcur')): + g_patches.append(i) + if patchlast < val: + Set('sys_patchlast',val,True) + Set('setup_patches',g_patches,True) + + #--------------------------------------------------------------------- + # Определим переменные окружения emerge и команду запуска emerge + #--------------------------------------------------------------------- + if not Get('setup_makeopts'): + makecpu="" + if int(Get('hrd_cpu_num'))==1: + makecpu=1 + else: + makecpu=int(Get('hrd_cpu_num'))+1 + Set('setup_makeopts',"-j%s"%makecpu,True) + if not Get('setup_path_portage'): + if os.path.exists('/usr/calculate/portage'): + Set('setup_path_portage',"/usr/calculate/portage",True) + if not Get('setup_path_distfiles'): + if os.path.exists('/usr/calculate/distfiles'): + Set('setup_path_distfiles',"/usr/calculate/distfiles",True) + if not Get('setup_path_pkg'): + pkgpath="/usr/calculate/packages/%s/%s/%s"%\ + (Get('setup_sys_shortname'),\ + Get('setup_linux_ver'),\ + Get('setup_march')) + if os.path.exists(pkgpath): + Set('setup_path_pkg',pkgpath,True) + if Get('setup_path_portage'): + Set('cmd_run_emerge1','PORTDIR_OVERLAY="%s" MAKEOPTS="%s"'%\ + (Get('setup_path_portage'),\ + Get('setup_makeopts')),\ + True) + Set('cmd_run_emerge2',"",True) + + #---------------------------------------------------------------------- + # Пути к директориям пакетов сборки + #---------------------------------------------------------------------- + #packages=(G_inst_path+"/builder/packages",\ + #self.G_shr_path+"/builder/packages") + #Set('setup_path_constpkg',packages,True) + + def fillInstall(self): + #--------------------------------------------------------------------- + # Определим профили установки или сборки + #--------------------------------------------------------------------- + prof=self.getProfileList('/usr/calculate/profile/install/') + if prof: + self._objvar.Set('setup_path_profuser',prof,True) + prof=self.getProfileList('/usr/lib/calculate-install/profile/') + if prof: + self._objvar.Set('setup_path_profinst',prof,True) + + + def getProfileList(self, path): + obj=self._objvar + Get=obj.Get + Set=obj.Set + _ex=os.path.exists + profPath=path + if not os.path.exists(profPath): + return "" + else: + Prof_gr1=[] + Prof_gr2='' + Prof_gr3='' + listProf=cl_utils.getdirlist(profPath) + #создадим список профилей в порядке их наложения + #профили 1*, сборки и для имени хоста + ver=Get('setup_dist_ver') + re_ver="|".join(ver.keys()) + i=0 + for i in xrange(len(listProf)): + el=listProf[i] + if re.search('^[0-9]+[^\.]\w+',el): + listProf[i]='' + Prof_gr1.append(profPath+el) + elif re.search('^(%s)$'%re_ver,el): + if el==Get('setup_sys_shortname'): + Prof_gr2=profPath+el + listProf[i]='' + elif re.search(\ + '^[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}$',el): + if Get('net_hosts_allow').split('/')[0]==el: + Prof_gr3=profPath+el + listProf[i]='' + i+=1 + #профили для DNS зоны + sdn=Get('sys_domain').split(".") + sdn.reverse() + Prof_gr4=[] + i=0 + for i in xrange(len(sdn)): + if i==0: + el=sdn[i] + else: + el=sdn[i]+"."+el + j=0 + if el in listProf: + Prof_gr4.append(profPath+el) + listProf.remove(el) + #профиль для ip компьютера + pcip=str(Get('net_host')) + if pcip in listProf: + Prof_gr4.append(profPath+pcip) + listProf.remove(pcip) + #компонуем профили + Prof_gr1.sort() + if Prof_gr2: + Prof_gr1+=[Prof_gr2] + if len(Prof_gr4)>0: + Prof_gr1+=Prof_gr4 + if Prof_gr3: + Prof_gr1+=[Prof_gr3] + if len(Prof_gr1)>0: + return Prof_gr1 + else: + return "" + + + +class CLError(Exception): + def __init__(self, value): + self.value=value + def __str__(self): + return repr(self.msg+self.value) + +class VarNameError(CLError): + msg="Incorrect name of a variable: " + +class VarValueError(CLError): + msg="Incorrect value of a variable: " + + + +class objValue: + v_mode="r" + v_type=() + v_vtype=None + v_value=None + v_select=None + v_official=False + v_printval=None + + def __init__(self,**args): + for atname,avalue in args.items(): + self.__setattr__(atname,avalue) + + def __getattribute__(self, instance): + try: + return object.__getattribute__(self, instance) + except: + return None + + def __setattr__(self, atname, value): + matching={'mode' :'v_mode', + 'type' :'v_type', + 'vtype' :'v_vtype', + 'value' :'v_value', + 'select' :'v_select', + 'official' :'v_official'} + if matching.has_key(atname): + self.__dict__[matching[atname]]=value + return + if not self.__class__.__dict__.has_key(atname): + print 'not attribute' + else: + self.__dict__[atname]=value + +class DataVars: + 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')) + sys.path.insert(0,os.path.abspath('/home/okovalenko/Home/Calculate\ +/pvars')) + __modlist={'Global':'cl_vars', + 'Server':'cl_vars_server', + 'Builder':'cl_vars_builder'} + __implist=[] + + def __init__(self, modname='Global'): + self.__varslist={} + self.__typelist=() + if modname and self.__modlist.has_key(modname): + modname=self.__modlist[modname] + #импортируем модуль с переменными + exec ("import %s" % (modname)) + #создаем экземпляр импортированного модуля + exec ("obj=%s.Data" %(modname)) + #проходимся по переменным класса и готовим объект + for idnt in obj.__dict__: + #отбрасываем служебные переменные + if re.search("\__.*",idnt): + continue + #получаем значение переменной + exec("vals=obj.%s"%idnt) + #если значение переменной некорректно то убиваем все окружение + #и вываливаемся с пустым объектом + if self.__checkVarsValue(vals)==False: + msg='The variable: %s in the module: %s is incorrectly\ + described' % (idnt, modname) + raise CLError(msg) + del(self) + return + #добавляем имя переменной в общий список + if vals.has_key('value'): + if isinstance(vals['value'], types.ListType) or\ + isinstance(vals['value'], types.DictType): + vtype=type(vals['value']) + else: + vtype=None + else: + vtype=None + self.__varslist[idnt]={} + for p,val in vals.items(): + if p=='value': + continue + self.__varslist[idnt][p]=val + self.__varslist[idnt]['vtype']=vtype + #оформляем значение переменной в виде объекта + objparam=objValue(**vals) + #инициализируем переменную + exec ("""self.%s=objparam"""%(idnt)) + else: + print "Unsupported module vars: %s"%(modname) + sys.exit(0) + + #проверить импортируемое значение + def __checkVarsValue(self, pvar): + if pvar is None: + return False + #обязательные значения полей для переменной + if not isinstance(pvar, types.DictType): + return False + if not pvar.has_key('mode') or not pvar.has_key('type'): + return False + if pvar['mode']==None or pvar['type']==None: + return False + if pvar['mode'].upper() not in ['R','W']: + return false + return True + + #установить значение переменной + def Set(self, vname, val, force=False): + self.__addVarValue(vname, val, force) + + #получить значение переменной + def Get(self, vname, objformat=None): + return self.__getVarValue(vname, objformat) + + def flServer(self, **args): + self.Set('setup_sys_shortname','CDS',True) + self.flGlobal() + if 'Server' in self.__implist: + return + self.__implist.append('Server') + objvar=DataVars("Server") + self.joinVars(objvar) + fillVars(self).fillLDAP(**args) + + def flGlobal(self,**args): + if 'Global' in self.__implist: + return + self.__implist.append('Global') + fillVars(self).fillGlobal(**args) + + def flBuilder(self, **args): + self.Set('setup_pass','builder',True) + self.flGlobal() + if 'Builder' in self.__implist: + return + self.__implist.append('Builder') + objvar=DataVars("Builder") + self.joinVars(objvar) + fillVars(self).fillBuilder(**args) + + def flInstall(self, **args): + self.Set('setup_pass','install',True) + self.flGlobal() + fillVars(self).fillInstall(**args) + + def isRewrite(self, vname): + mode=self.Get(vname,True).v_mode + if mode=='w': + return True + else: + return False + + #Получить значение переменной в чистом виде или в виде объекта + def __getVarValue(self, vname, objformat=None, printval=None): + if not vname in self.__varslist.keys(): + raise VarNameError(vname) + try: + #если переменная уже инициализирована + exec ("val=self.%s"%(vname)) + if objformat is None: + if printval: + return val.v_printval + return val.v_value + else: + return val + except: + #если переменная не инициализирована + if objformat: + #если режим возврата в виде объекта + par=self.__varslist[vname] + objpar=par + #objpar['value']=None + obj=objValue(**objpar) + if obj: + return obj + return False + + #установить значение переменной + #не даем переписывать значения переменной у которых режим "r" + #но можно перезаписать в режиме force=True + def __addVarValue(self, vname, val, force=False, printval=None): + if not self.__varslist.has_key(vname): + raise VarNameError(vname) + valobj=self.__getVarValue(vname, True) + if valobj is False: + return False + if valobj.v_mode=="r" and force==False: + print "Attempt to rewrite a variable for reading: %s"%vname + return False + if self.__varslist[vname]['vtype']: + if type(val)!=self.__varslist[vname]['vtype']: + raise VarValueError(vname) + if 'bool' in valobj.v_type: + if val is None: + val=False + if printval: + valobj.v_printval=val + else: + valobj.v_value=val + exec("self.%s=valobj"%(vname)) + return True + + #проверить входит ли тип в указанный список типов + def __checkType(self, tvar, tcheck): + lvar=len(tcheck) + flag=0 + for i in tvar: + if i in tcheck: + flag+=1 + if flag !=lvar: + return False + return True + + # Вернуть список переменных + # type_names - список типов, переменные которых необходимо вернуть + def getVars(self, type_names=None): + ret={} + for i in self.__varslist.keys(): + try: + val=None + exec ("val=self.%s"%i) + if type_names: + if not self.__checkType(val.v_type,type_names): + continue + ret[i]=val + except: + param=self.__varslist[i] + nobj=objValue(param['mode'],param['type'], None) + if type_names: + if not self.__checkType(val.vtype,type_names): + continue + ret[i]=nobj + 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.v_type))>mlen_type: + mlen_type=len(str(j.v_type)) + vtype=str(type(var[i].v_value)).split(" ")[1][1] + if vtype in ['d','l']: + mode="[%s%s]"%(var[i].v_mode.lower(),vtype) + else: + mode="[%s]"%(var[i].v_mode.lower()) + var[i].v_mode=mode + if len(mode)>mlen_mode: + mlen_mode=len(mode) + plist=var.keys() + plist.sort() + print "The list of variables:" + print "var name".center(mlen_name),\ + "Mode",\ + "Type".center(mlen_type),\ + "Value" + print cl_utils.fillstr("-",mlen_name),\ + cl_utils.fillstr("-",mlen_mode),\ + cl_utils.fillstr("-",mlen_type),\ + cl_utils.fillstr("-",10) + for i in plist: + if var[i].v_value is None and var[i].v_printval is None: + continue + elif var[i].v_printval is None: + p_val=var[i].v_value + else: + p_val=var[i].v_printval + if var[i].v_official: + continue + print i.ljust(mlen_name),\ + var[i].v_mode.lower().ljust(mlen_mode),\ + str(var[i].v_type).ljust(mlen_type),\ + ":",\ + p_val + print cl_utils.fillstr("-",mlen_name),\ + cl_utils.fillstr("-",mlen_mode),\ + cl_utils.fillstr("-",mlen_type),\ + cl_utils.fillstr("-",10) + + #объеденить два объекта с переменными + def joinVars(self, objvars, force=False): + #Принимаем на вход только экземпляры текущего класса + if not isinstance(objvars, DataVars): + raise CLError('The object of incorrect type is transferred') + #Делаем копию текущего объекта и дальше работаем с копией + old_self=copy.deepcopy(self) + for var in objvars.__varslist.keys(): + #если нет режима замещения то вываливаемся + if var in old_self.__dict__.keys(): + if force==False: + raise CLError('Duplication of a variable: %s'% var) + else: + old_self.__varslist[var]=objvars.__varslist[var] + val=objvars.__getVarValue(var, True) + if val: + old_self.__addVarValue(var,val.v_value, True) + #если все успешно тогда вносим изменения + self.__dict__=old_self.__dict__ + + def defined(self, vname): + if vname: + if self.__dict__.has_key(vname): + return True + return False + + #вернуть список переменных с параметрами + def getVarsList(self): + vlist=self.__varslist.keys() + vlist.sort() + return vlist +############################################################################## diff --git a/pym/cl_devices.py b/pym/cl_devices.py new file mode 100644 index 0000000..a8966f1 --- /dev/null +++ b/pym/cl_devices.py @@ -0,0 +1,66 @@ +#-*- coding: utf-8 -*- + +#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re +import glob +import cl_utils + +#класс для работы с видео устройствами +class Video: + #получить список видеокарт + def getcards(self): + card_names=[] + g_path=cl_utils.getpathenv() + s_cmd='%s lspci | grep VGA' % g_path + lines=os.popen(s_cmd).readlines() + if lines: + for line in lines: + re_res=re.search(\ + '[0-9\:\.]+\sVGA compatible controller:(.+)$',line) + if re_res: + card_names.append(re_res.group(1)) + if len(card_names)>0: + return card_names + else: + return None + +#работа с блочными устройствами +class block_dev: + _dev_list=[] + + def __init__(self): + self._getlist() + + def _getlist(self): + devlist=[] + for dev in ('sd','hd'): + devlist+=glob.glob('/sys/block/%s*'%dev) + for dev in devlist: + self._dev_list.append(dev.split('/')[-1]) + + #получить список устройств + def get_listdev(self): + return self._dev_list + + #проверить на извлекаемое устройство + def isremovable(self, dev): + if dev in self.get_listdev(): + res=int(open('/sys/block/%s/removable'%dev).read().split()[0]) + return res + else: + return None + diff --git a/pym/cl_profile.py b/pym/cl_profile.py new file mode 100644 index 0000000..e90c2c4 --- /dev/null +++ b/pym/cl_profile.py @@ -0,0 +1,2815 @@ +#-*- coding: utf-8 -*- + +#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru +# +# 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 cl_base +import stat +import re +import xml.dom.minidom +from xml import xpath +import popen2 + +tr = cl_base.lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +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 + + +class _terms(_error): + """Вычисление условий применяемых в профилях + + """ + def _convertVers(self, verA, verB): + """Конвертирование номеров версий для корректного сравнения + """ + elemA = verA.split(".") + elemB = verB.split(".") + if len(elemA) > len(elemB): + maxElemB = len(elemB)-1 + for i in range(len(elemA)): + if i > maxElemB: + elemB.append("0") + else: + maxElemA = len(elemA)-1 + for i in range(len(elemB)): + if i > maxElemA: + elemA.append("0") + for i in range(len(elemB)): + lenA = len(elemA[i]) + lenB = len(elemB[i]) + if lenA == lenB: + pass + elif lenA > lenB: + res = lenA - lenB + for z in range(res): + elemB[i] = "0" + elemB[i] + elif lenB > lenA: + res = lenB - lenA + for z in range(res): + elemA[i] = "0" + elemA[i] + return (".".join(elemA), ".".join(elemB)) + + def _equalTerm(self, term, textError): + """Вычисление логических выражений для условий + + Для корректной работы в классе который наследует этот класс + должен быть объявлен аттрибут self.objVar + (объект для работы с переменными) + """ + trm = {"&":" and ","||":" or "} + rule = ["==", "!=", ">=", "<=", ">", "<"] + listEqual = [] + for k in trm.keys(): + if k in term: + term = term.replace(k,trm[k]) + trs = term.split(" ") + for t in trs: + flagRule = False + for sepF in rule: + if sepF in t: + flagRule = True + vals = t.split(sepF) + break + + if not flagRule: + flagLog = False + for k in trm.values(): + if k.strip() == t: + flagLog = True + break + if not flagLog: + self.setError (textError) + return False + else: + listEqual.append(k) + else: + if self.objVar.defined(vals[0]): + valVars = self.objVar.Get(vals[0]) + #print valVars + if not valVars: + self.setError (_("empty var: ")+\ + vals[0]) + return False + # Cравниваем номера версий + if "_ver" in vals[0]: + verFile, verVar = self._convertVers(vals[1],valVars) + exec("res=("+"'"+verVar+"'"+sepF+"'"+verFile+"'"+")") + if res: + listEqual.append("1") + else: + listEqual.append("0") + else: + if "int" in str(type(valVars)): + try: + valFile = int(vals[1]) + except: + self.setError (textError) + return False + valVar = valVars + exec("res=(%d%s%d)"%(valVar,sepF,valFile)) + if res: + listEqual.append("1") + else: + listEqual.append("0") + else: + if sepF == "!=" or sepF == "==": + valFile = vals[1] + valVar = valVars + exec("res=("+"'"+valVar+"'"+sepF+"'"+valFile+\ + "'"+")") + if res: + listEqual.append("1") + else: + listEqual.append("0") + else: + self.setError (textError) + return False + else: + self.setError (_("not defined Var: ") + vals[0]) + return False + exec("res=(%s)"%("".join(listEqual))) + return res + +class calcHeader(_terms): + """Обработка заголовков профилей и конфигурационных файлов + + """ + def __init__(self, text, comment=False, fileType=False, objVar=False): + # Тип профиля + self.fileType = "" + # Тип вставки профиля + self.typeAppend = "" + # Возможные типы вставки профилей + self._fileAppend = "join", "before", "after", "replace" + self.body = text + # Интерпретатор (#!/bin/bash) (#!/usr/bin/python) + self.execStr = "" + # Символ комментария + self.comment = False + # Выражение для поиска строки интерпретатора + self.reExecStr = re.compile("^#!.+\s*",re.I) + # условные операторы + self.terms = ('>', '<', '==', '!=', '>=', '<=') + # параметры без значения + self.listParNotVal = ("symbolic", "force") + # Объект с переменными + self.objVar=objVar + # Результат вычисления условия в заголовке + self.headerTerm = True + # Параметры описанные в заголовке файла профиля + self.params = {} + + # Удаление Заголовка Calculate + if comment: + reCalcHeader =\ + re.compile("\s*%s\-+\s+%s.+Calculate.+\s+%s.+\s+%s\-+\s*"%(\ + comment, + comment, + comment, + comment, + ), + re.M|re.I) + reS = reCalcHeader.search(text) + if reS: + self.body = text[reS.end():] + + if fileType != False: + if fileType=="bin": + self.params["type"] = fileType + self.fileType = self._getType() + self.typeAppend = self._getAppend() + else: + textLines = text.splitlines() + if textLines: + textLine = textLines[0] + rePar = re.compile("\s*#\s*calculate\s+",re.I) + reP = rePar.search(textLine) + if reP: + reL = False + reLns = re.compile(r"\A([^\\\n]*\\\n)+[^\n]*\n*",re.M) + reLs = reLns.search(text) + if reLs: + reL = reLs + paramLine = text[:reLs.end()] + paramLine = paramLine.replace("\\"," ") + else: + reLn = re.compile("\n") + reL = reLn.search(text) + paramLine = textLine[reP.end():] + if reL: + self.body = text[reL.end():] + else: + self.body = "" + + paramList = re.split("\s+",paramLine) + if paramList: + for i in paramList: + for term in self.terms: + if term in i: + if not self._equalTerm(i,\ + _("content profile not valid: ") + \ + i): + self.headerTerm = False + break + else: + par = i.split("=") + if len(par) == 1: + for parNotVal in \ + self.listParNotVal: + if i == parNotVal: + self.params[parNotVal] =\ + "True" + elif len(par) == 2: + self.params[par[0]] = par[1] + + self.fileType = self._getType() + self.typeAppend = self._getAppend() + self.comment = self._getComment() + reExecRes = self.reExecStr.search(self.body) + if reExecRes: + self.execStr = self.body[reExecRes.start():reExecRes.end()] + self.body = self.body[reExecRes.end():] + + def _getType(self): + """Выдать тип файла""" + if self.params.has_key("type"): + return self.params["type"] + else: + return "raw" + + def _getAppend(self): + """Выдать тип добавления файла""" + if self.params.has_key("append") and self.params["append"] in\ + self._fileAppend: + return self.params["append"] + else: + if self.fileType != "raw" and self.fileType != "bin" and\ + self.fileType != "": + self.params["append"] = "join" + return "join" + self.params["append"] = "replace" + return "replace" + + def _getComment(self): + """Выдать символ комментария файла""" + if self.params.has_key("comment"): + return self.params["comment"] + else: + return False + + + +class objShare: + """Общий клас для объектов, наследуем + + """ + def getConfig(self): + """Выдает конфигурационный файл""" + listConfigTxt = [] + childNodes = self.docObj.getNodeBody().childNodes + for node in childNodes: + if node.nodeType == node.ELEMENT_NODE: + if node.tagName == "field": + listConfigTxt.append(self.docObj.getQuoteField(node)) + elif node.tagName == "area": + self.docObj.xmlToText([node], listConfigTxt) + return "".join(listConfigTxt) + + + def splitToFields(self, txtBloc): + """Разбиваем текст на поля (объекты) которые имеют следующие аттрибуты + + self.name - если переменная то имя переменной + self.value - если у переменной есть значение то значение переменной + self.comment - если комментарий то текст комментария + self.br - если перевод строки то текст перед переводом из пробелов + + Результат список объектов полей + """ + finBloc = "\n" + if txtBloc[-1] != "\n": + finBloc = "" + + linesBlocTmp = txtBloc.splitlines() + linesBloc = [] + brBloc = [] + z = 0 + lenLines = len(linesBlocTmp) + for i in linesBlocTmp: + + if self.reComment.split(i)[0]: + findCooment = self.reComment.search(i) + comment = False + par = i + if findCooment: + par = i[:findCooment.start()] + comment = i[findCooment.start():] + fields = self.reSepFields.split(par) + lenFields = len(fields) + + if lenFields>1: + for fi in range(lenFields-1): + linesBloc.append(fields[fi]+ self.sepFields) + if fi == lenFields-2: + if comment: + brBloc.append("") + else: + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + else: + brBloc.append("") + if comment: + linesBloc.append(comment) + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + else: + linesBloc.append(i) + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + else: + linesBloc.append(i) + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + z +=1 + fields = self.setDataField(linesBloc, brBloc) + return fields + +class xmlShare: + """Общий класс для объектов XML, наследуем + + """ + def _createElement(self, doc, tagName, text="", attributes={}): + """Создание нового XML элемента""" + element = doc.createElement(tagName) + if text: + txtNode = doc.createTextNode(self._toUNICODE(text)) + element.appendChild(txtNode) + for attr in attributes.keys(): + attribute = doc.createAttribute(attr) + attribute.nodeValue = attributes[attr] + element.setAttributeNode(attribute) + return element + + def _toUNICODE(self,val): + """перевод текста в юникод""" + if 'unicode' in "%s" %(type(val)): + return val + else: + return val.decode('UTF-8') + + +class xmlNode(xmlShare): + """Класс для создания нод без аттрибутов + + """ + def __init__(self): + self.node = False + + + def createNode(self, doc, tagName, text=""): + """Создает XML элемент без аттрибутов""" + self.node=self._createElement(doc, tagName, text) + return self.node + + def getNode(self): + return self.node + + +class xmlCaption: + """Класс XML заголовок + + """ + def __init__(self): + #Заголовок области XML нода + self.caption = False + + + def createCaption(self, doc, name, quotes, action=False): + """Создание заголовка области""" + tmpNode = xmlNode() + self.caption = tmpNode.createNode(doc, "caption") + nameNode = tmpNode.createNode(doc, "name",name) + self.caption.appendChild(nameNode) + if action: + actNode = tmpNode.createNode(doc, "action", action) + self.caption.appendChild(actNode) + for q in quotes: + quoteNode = tmpNode.createNode(doc, "quote", q) + self.caption.appendChild(quoteNode) + return self.caption + + def getCaption(self): + """Выдает XML ноду заголовка области""" + return self.caption + +class xmlField(xmlShare): + """Класс для работы с XML полем + + """ + def __init__(self): + # XML нода поле + self.field = False + + + def createField(self, doc, typeField, quotes, name="", + values=[],action=False): + """Cоздание XML ноды поле""" + self.field = self._createElement(doc, "field", "", {"type":typeField}) + if name: + nameNode = self._createElement(doc, "name", name) + self.field.appendChild(nameNode) + for v in values: + valueNode = self._createElement(doc, "value", v) + self.field.appendChild(valueNode) + if action: + actNode = self._createElement(doc, "action", action) + self.field.appendChild(actNode) + for q in quotes: + quoteNode = self._createElement(doc, "quote", q) + self.field.appendChild(quoteNode) + return self.field + + + +class xmlFields: + """Класс, в котором находится список ХМL нод field + + """ + def __init__(self): + self.fields = [] + + def appendField(self, field): + """Добавить XML ноду field""" + self.fields.append(field) + return self.fields + + def getFields(self): + """Выдать список XML нод""" + return self.fields + + +class xmlArea: + """Класс для работы с XML областью + + """ + def __init__(self): + # Область + self.area = False + + def createArea(self, doc, xmlCaption, xmlFields): + """Создание XML области""" + tmpNode = xmlNode() + self.area = tmpNode.createNode(doc, "area") + if xmlCaption and xmlCaption.getCaption(): + self.area.appendChild(xmlCaption.getCaption()) + if xmlFields: + fields = xmlFields.getFields() + for field in fields: + self.area.appendChild(field) + return self.area + +class xmlDoc: + """Класс для работы с XML документом + + """ + def __init__(self): + # документ + self.doc = False + # главная нода + self.root = False + # тело документа + self.body = False + # Заголовок области - временный (в реальности один объект заголовок) + self.tmpCaption = False + # Поля - временные (в реальности один объект поля) + self.tmpFields = False + # Разделитель областей - по умолчанию перевод строки "\n" + self.sepAreas = False + + + def createDoc(self, typeDoc, version): + """Создание нового документа новый документ""" + docTxt = '' + docTxt += '%s'% version + docTxt += '%s' % typeDoc + docTxt += '' + self.doc = xml.dom.minidom.parseString(docTxt) + self.root = self.doc.documentElement + self.body = xpath.Evaluate('child::body',self.root)[0] + # установка разделителя областей + self.sepAreas = self.createField("br",[],"",[],False,False) + return self.doc + + def addField(self, field): + """Добавляет поле во временный список + + Из этого списка в дальнейшем формируется XML область + """ + if not self.tmpFields: + self.tmpFields = xmlFields() + self.tmpFields.appendField(field) + + def createCaption(self, name, quotes, action=False): + """Cоздает заголовок области + + Помещает заголовок в временный артибут + Используется при создании области + """ + self.tmpCaption = xmlCaption() + return self.tmpCaption.createCaption(self.doc, name, quotes, action) + + def createField(self, typeField, quotes=[], name="", + values=[] ,action=False,addTmpField=True): + """Cоздает поле + + Если установлена переменнная addTmpField + добавляет поле во временный список + """ + fieldObj = xmlField() + field = fieldObj.createField(self.doc, typeField, quotes, name, + values, action) + if addTmpField: + self.addField(field) + return field + + def clearTmpFields(self): + """Очищает временный список""" + self.tmpFields = False + + def createArea(self): + """Cоздает область + + Область создается на основании временного атрибута и временного списка + """ + areaObj = xmlArea() + area = areaObj.createArea(self.doc, self.tmpCaption, self.tmpFields) + self.clearTmpCaptionAndFields() + return area + + def clearTmpCaptionAndFields(self): + """Очищает временный аттрибут и временный список""" + self.tmpCaption = False + self.tmpFields = False + + def getNodeRoot(self): + """Выдает корневую ноду""" + return self.root + + def getNodeBody(self): + """Выдает ноду body""" + return self.body + + def setActionField(self, xmlField, actionTxt): + """Устанавливает свойство action для XML поля""" + xmlActions = xpath.Evaluate('child::action',xmlField) + if xmlActions and xmlActions[0].firstChild: + xmlActions[0].firstChild.nodeValue = actionTxt + else: + nodeObj = xmlNode() + newNode = nodeObj.createNode(self.doc, "action", actionTxt) + xmlField.appendChild(newNode) + + + def setActionArea(self, xmlArea, actionTxt): + """Устанавливает свойство action для XML области""" + xmlActions = xpath.Evaluate('child::caption/action',xmlArea) + xmlCaptions = xpath.Evaluate('child::caption',xmlArea) + if xmlActions and xmlActions[0].firstChild: + xmlActions[0].firstChild.nodeValue = actionTxt + else: + if xmlCaptions: + nodeObj = xmlNode() + newNode = nodeObj.createNode(self.doc, "action", actionTxt) + xmlCaptions[0].appendChild(newNode) + + + def joinField(self, xmlArea, xmlNewField): + """Объединяет XML ноду область и XML ноду поле""" + fieldNames = xpath.Evaluate('child::field/name',xmlArea) + newFieldNames = xpath.Evaluate('child::name',xmlNewField) + newFieldVals = xpath.Evaluate('child::value',xmlNewField) + newFieldsAction = self.getActionField(xmlNewField) + flagFindField = False + if not newFieldNames: + return False + newNameField = newFieldNames[0].firstChild.nodeValue + for fn in fieldNames: + # если найден параметр, изменяем его значение + # в том случае если значения разные + if newNameField == fn.firstChild.nodeValue: + flagFindField = True + # Удаление полей + if newFieldsAction: + print "newFieldsAction =", newFieldsAction + if newFieldsAction == "drop": + if (fn.parentNode.nextSibling): + if self.getTypeField\ + (fn.parentNode.nextSibling) == "br": + xmlArea.removeChild(fn.parentNode.nextSibling) + xmlArea.removeChild(fn.parentNode) + continue + fieldsVal = xpath.Evaluate('child::value',fn.parentNode) + z =0 + flagNoCompareFields = False + for val in fieldsVal: + if not val.firstChild or not newFieldVals[z].firstChild: + flagNoCompareFields = True + break + if val.firstChild.nodeValue != \ + newFieldVals[z].firstChild.nodeValue: + flagNoCompareFields = True + break + z += 1 + if flagNoCompareFields or\ + self.getTypeField(xmlNewField) == "seplist": + #Действия при добавлении распределенного списка + if self.getTypeField(xmlNewField) == "seplist": + xmlOldField = fn.parentNode + seplistNewXML = self.getSepListToField(xmlNewField) + seplistOldXML = self.getSepListToField(xmlOldField) + #print seplistOldXML + if not seplistOldXML: + seplistOldXML = [xmlOldField,] + xmlAreaOld = xmlOldField.parentNode + for nodeSeplistRemove in seplistOldXML[:-1]: + if nodeSeplistRemove.nextSibling and\ + self.getTypeField(\ + nodeSeplistRemove.nextSibling) == "br": + xmlAreaOld.removeChild(\ + nodeSeplistRemove.nextSibling) + if self.getTypeField(nodeSeplistRemove) == "var": + xmlAreaOld.removeChild(nodeSeplistRemove) + + for nodeSeplist in seplistNewXML: + if self.getActionField(nodeSeplist) != "drop": + #xmlArea.appendChild(nodeSeplist.cloneNode(\ + #True)) + xmlArea.insertBefore(nodeSeplist.cloneNode(\ + True), seplistOldXML[-1]) + + parentNode = nodeSeplist.parentNode + parentNode.removeChild(nodeSeplist) + if seplistNewXML: + parentNode = seplistOldXML[-1].parentNode + if parentNode: + parentNode.removeChild(seplistOldXML[-1]) + #print "--------------------------------------" + #print nodeSeplist.toprettyxml() + #xmlArea.removeChild(fn.parentNode) + else: + #print xmlNewField.toprettyxml() + # Устанавливаем action=replace + self.setActionField(xmlNewField, "replace") + # Если параметры поля не сходятся заменяем поле + xmlArea.replaceChild(xmlNewField.cloneNode(True), + fn.parentNode) + # Если поле не найдено добавляем его + + if not flagFindField: + if self.getActionField(xmlNewField) != "drop": + self.setActionField(xmlNewField, "append") + xmlArea.appendChild(xmlNewField) + return True + + def getSepListToField(self, xmlField): + """Выдает элементы распределенного массива + + Область предок поля, в этой области ищутся + элементы распределенного массива + """ + nameField = self.getNameField(xmlField) + if not nameField: + return [] + parentNode = xmlField.parentNode + #print parentNode.toprettyxml() + if parentNode: + fieldsVal = xpath.Evaluate(\ + "child::field[attribute::type='seplist'][child::name='%s'] "\ + %(nameField), parentNode) + #print nameField + return fieldsVal + else: + return [] + + def removeComment(self, xmlArea): + """Удаляет комментарии в XML области""" + fieldNodes = xpath.Evaluate('descendant::field',xmlArea) + for fieldNode in fieldNodes: + if fieldNode.hasAttribute("type"): + if fieldNode.getAttribute("type") == "comment" or\ + fieldNode.getAttribute("type") == "br": + parentNode = fieldNode.parentNode + parentNode.removeChild(fieldNode) + else: + print self.getActionField(fieldNode) + if self.getActionField(fieldNode) != "drop" or\ + self.getActionField(fieldNode) != "join": + self.setActionField(fieldNode,"append") + + + + def joinBody(self, baseBody, newBody): + """Объединяет две области Body""" + newFields = xpath.Evaluate('child::field',newBody) + xmlNewAreas = xpath.Evaluate('child::area',newBody) + for xmlNewArea in xmlNewAreas: + self.joinArea(baseBody,xmlNewArea) + + removeNodesDict = {} + notSepListField = [] + sepListField = [] + for nField in newFields: + if self.getRemoveNodeSepList(removeNodesDict, baseBody, + nField): + sepListField.append(nField) + else: + if self.getNameField(nField): + notSepListField.append(nField) + + for removeNodes in removeNodesDict.values(): + if removeNodes: + if self.getTypeField(removeNodes[-1]) == "seplist": + removeNodes = removeNodes[:-1] + else: + removeNodes = removeNodes[:-2] + + for removeNode in removeNodes: + baseBody.removeChild(removeNode) + + for node in sepListField: + #if removeNodes: + node.setAttribute("type", "seplist") + self.setActionField(node,"insert") + self.joinField(baseBody, node) + #else: + #self.setActionField(node, "append") + #baseBody.appendChild(node) + for node in notSepListField: + if self.getTypeField(node) == "seplist": + #if removeNodesDict.has_key(self.getNameField(node)): + #print removeNodesDict[self.getNameField(node)] + self.setActionField(node, "append") + baseBody.appendChild(node) + else: + self.joinField(baseBody, node) + + + def getRemoveNodeSepList(self, removeNodesDict, baseNode, xmNewlField): + """Находит элементы разделенного списка + + Параметры: + removeNodesDict - Cловарь удаляемых полей разделенного списка + формируется программой + baseNode - Нода в которой идет поиск + xmNewlField - Нода field которая проверяется на принадлежность + к разделенному списку + """ + flagNewNodeSeplist = False + if self.getTypeField(xmNewlField) == "seplist": + flagNewNodeSeplist = True + nameNewField = self.getNameField(xmNewlField) + if nameNewField: + if removeNodesDict.has_key(nameNewField): + return removeNodesDict[nameNewField] + else: + oldFields = xpath.Evaluate('child::field', baseNode) + removeNodes = [] + lenOldFields = len(oldFields) + for i in range(lenOldFields): + oldNode = oldFields[i] + flagSep = self.getTypeField(oldNode) == "seplist" + if flagNewNodeSeplist: + flagSep = True + if flagSep and\ + nameNewField == self.getNameField(oldNode): + removeNodes.append(oldNode) + if i+1 1: + for node in listNodes: + node.setAttribute("type", "seplist") + + def insertBRtoBody(self, xmlArea): + """Добавляет необходимые переводы строк + """ + # Потомки + childNodes = self.getFieldsArea(xmlArea) + # нода BR + fieldXMLBr = self.createField("br",[],"",[],False, False) + # разделитель поля + fieldSplit = False + # Предыдущая нода + lastNode = False + # Cледующая нода + nextNode = False + lenChildNodes = len(childNodes) + for i in range(lenChildNodes): + node = childNodes[i] + lastTmpNode = node + # Нода area + if node.tagName == "area": + if self.getActionArea(node) == "append": + self.delActionNodeArea(node) + if lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "br" or\ + lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "comment": + indNext = i + 1 + if indNext == lenChildNodes: + xmlArea.appendChild(fieldXMLBr.cloneNode(True)) + else: + nextNode = childNodes[indNext] + lastTmpNode = xmlArea.insertBefore(\ + fieldXMLBr.cloneNode(True), + nextNode) + else: + xmlArea.insertBefore(fieldXMLBr.cloneNode(True), + node) + self.insertBRtoBody(node) + # Нода field + else: + if self.getActionField(node) == "append": + self.delActionNodeField(node) + if lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "br" or\ + lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "comment": + indNext = i + 1 + if indNext == lenChildNodes: + xmlArea.appendChild(fieldXMLBr.cloneNode(True)) + else: + nextNode = childNodes[indNext] + lastTmpNode = xmlArea.insertBefore(\ + fieldXMLBr.cloneNode(True), + nextNode) + else: + xmlArea.insertBefore(fieldXMLBr.cloneNode(True), + node) + lastNode = lastTmpNode + + + + def postParserList(self): + """Находит подходящие XML области и делаем из них поля-массивы""" + xmlAreas = xpath.Evaluate('descendant::area', self.body) + for xmlArea in xmlAreas: + flagListXml = True + fieldValues = [] + xmlFields = xpath.Evaluate('child::field',xmlArea) + if not xmlFields: + flagListXml = False + lenXmlFields = len(xmlFields) + lenBrArea = 0 + for xmlField in xmlFields: + xmlNames = xpath.Evaluate('child::name',xmlField) + xmlVals = xpath.Evaluate('child::value',xmlField) + if xmlField.hasAttribute("type") and\ + xmlField.getAttribute("type") == "br": + lenBrArea += 1 + continue + if not xmlNames and not xmlVals: + flagListXml = False + break + if xmlNames and xmlNames[0].firstChild and\ + xmlNames[0].firstChild.nodeValue: + flagListXml = False + break + if not (xmlVals and xmlVals[0].firstChild and\ + xmlVals[0].firstChild.nodeValue): + flagListXml = False + break + else: + fieldValues.append(xmlVals[0].firstChild.nodeValue) + + if lenXmlFields == lenBrArea: + flagListXml = False + if flagListXml: + nameNode = xpath.Evaluate('child::caption/name',xmlArea)[0] + fieldName = "" + if nameNode.firstChild: + fieldName = nameNode.firstChild.nodeValue + listArea = [] + self.xmlToText([xmlArea],listArea) + fieldQuote = "".join(listArea) + fieldXMLBr = False + if fieldQuote and fieldQuote[-1] == "\n": + fieldQuote = fieldQuote[:-1] + fieldXMLBr = self.createField("br",[],"",[],False, False) + fieldXML = self.createField("list", + [fieldQuote], + fieldName, fieldValues, + False, False) + areaAction = self.getActionArea(xmlArea) + if areaAction: + self.setActionField(fieldXML, areaAction) + parentNode = xmlArea.parentNode + parentNode.insertBefore(fieldXML,xmlArea) + if fieldXMLBr: + parentNode.insertBefore(fieldXMLBr,xmlArea) + parentNode.removeChild(xmlArea) + + +class blocText: + """Разбиваем текст на блоки""" + + def splitTxtToBloc(self, text ,openTxtBloc,closeTxtBloc, + commentTxtBloc, sepField): + """Делит текст на блоки (без заголовков) + + openTxtBloc - регулярное выражение для начала блока + closeTxtBloc - регулярное выражение для конца блока + commentTxtBloc - регулярное выражение - комментарий + возвращает блоки текста + """ + blocs = [] + level = 0 + # Нахождение нескольких блоков в строке + # разделители линий, разделителями могут быть ("","\n") + sepsLines = [] + # линии + txtLines = [] + # Исходные строки + txtLinesSrc = text.splitlines() + for line in txtLinesSrc: + lineBR = "" + lineTmpA = line + closeBl = False + txtLinesTmp = [] + commentSpl = commentTxtBloc.split(line) + flagCommentLine = False + if commentSpl[0].strip(): + closeBl = True + if len(commentSpl) > 1: + commentBl = commentTxtBloc.search(line) + textLine =commentSpl[0] + commentLine = line[commentBl.start(0):] + lineTmpA = textLine + flagCommentLine = True + + while (closeBl): + closeBl = sepField.search(lineTmpA) + if closeBl: + lineTmpB = lineTmpA[closeBl.end(0):] + txtLinesTmp.append(lineTmpA[:closeBl.end(0)]) + lineTmpA = lineTmpB + if lineTmpA.strip(): + txtLinesTmp.append(lineTmpA) + # Если есть значение и комментарий в строке + if flagCommentLine: + for l in txtLinesTmp: + txtLines.append(l) + sepsLines.append("") + if not txtLinesTmp: + txtLines.append(textLine) + sepsLines.append("") + txtLines.append(commentLine) + sepsLines.append("\n") + # Если есть несколько блоков в строке + elif len(txtLinesTmp)>1 and txtLinesTmp[1].strip(): + lenTmpLines = len(txtLinesTmp) + for l in range(lenTmpLines): + txtLines.append(txtLinesTmp[l]) + if l == lenTmpLines-1: + sepsLines.append("\n") + else: + sepsLines.append("") + # Cтрока не преобразована + else: + txtLines.append(line) + sepsLines.append("\n") + + # разбивание на блоки + z = 0 + bl = "" + for i in txtLines: + if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(i): + level += len(openTxtBloc.split(i)) - 1 + if commentTxtBloc.split(i)[0].strip() and closeTxtBloc.search(i): + level -= len(closeTxtBloc.split(i)) - 1 + bl += i + sepsLines[z] + if level == 0: + if bl: + blocs.append(bl) + bl = "" + z += 1 + # cоздание блоков с элементами не входящими в блоки + realBlocs = [] + z = 0 + bl = "" + for i in blocs: + txtLines = i.splitlines() + if len(txtLines) > 0: + line = txtLines[0] + else: + line = i + if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(line): + if bl: + realBlocs.append(bl) + bl = "" + realBlocs.append(i) + else: + bl += i + z += 1 + if bl: + realBlocs.append(bl) + bl = "" + if level == 0: + if text and text[-1] != "\n": + tmpBlocs = realBlocs.pop() + tmpBlocs = tmpBlocs[:-1] + realBlocs.append(tmpBlocs) + return realBlocs + else: + return [] + + + def findBloc(self, text, captionTxtBloc, bodyTxtBloc): + """ Делит текст на блоки (с заголовками) + + captionTxtBloc - регулярное выражение для заголовка блока + bodyTxtBloc - регулярное выражение для тела блока + возвращает два списка: первый - заголовки, второй - тела блоков + """ + headersTxt = [] + # Заголовки блоков + blocsTxt = [] + # Тексты блоков + r = captionTxtBloc.search(text) + if r: + headersTxt.append(r.group(0)) + txtSpl = text.split(r.group(0)) + blocTxt = txtSpl[0] + txtWr = txtSpl[1] + rb = bodyTxtBloc.search(blocTxt) + if not blocTxt: + blocsTxt.append(blocTxt) + if rb: + blocsTxt.append(rb.group(0)) + while (r): + r = captionTxtBloc.search(txtWr) + if r: + headersTxt.append(r.group(0)) + txtSpl = txtWr.split(r.group(0)) + blocTxt = txtSpl[0] + txtWr = txtSpl[1] + rb = bodyTxtBloc.search(blocTxt) + if rb: + blocsTxt.append(rb.group(0)) + else: + blocsTxt.append(txtWr) + if headersTxt and blocsTxt: + if len(headersTxt)>len(blocsTxt): + blocsTxt.insert(0,"") + elif len(headersTxt)= 0: + for i in range(lenListDirMv,-1,-1): + z = i + 2 + dirTest = baseDirMv + "/".join(listDirMv[:z]) + if os.access(dirTest, os.F_OK): + break + start = i + end = lenListDirMv + 1 + for k in range(start,end): + z = k + 2 + dirTest = "/".join(listDirMv[:z]) + dirTestMv = baseDirMv + dirTest + if not os.access(dirTestMv, os.F_OK): + dirTestProf = baseProfDir + dirTest + try: + mode,uid,gid = self.getModeFile(dirTestProf) + except OSError: + self.setError (_("not access dir:" ) + profDir) + return False + os.mkdir(dirTestMv, mode) + os.chown(dirTestMv, uid,gid) + + def scanDirs(self, profilesDirs): + """Сканирует дерево каталогов выдает два списка: директории, файлы""" + dirs = [] + class dirProf: + def __init__(self): + self.baseDir = False + self.dirs = [] + self.files = [] + def getFilesDir(dirP, dirname,names): + for nameFile in names: + absNameFile = dirname + "/" + nameFile + if os.path.isfile(absNameFile): + dirP.files.append(absNameFile) + elif os.path.isdir(absNameFile): + dirP.dirs.append(absNameFile) + for profileDir in profilesDirs: + if profileDir: + dirP = dirProf() + dirP.baseDir = profileDir + dirs.append(dirP) + os.path.walk(profileDir,getFilesDir, dirP) + return dirs + + def __absFileName(self, nameFile): + """Вычисление пути к файлу""" + pathList = nameFile.split("/") + chortNameFile = pathList.pop() + absPath = os.path.abspath("/".join(pathList)) + File = absPath + "/" + chortNameFile + return File + + def saveOldFile(self): + """Записать конфигурацию""" + if self.FO: + self.FO.truncate(0) + self.FO.seek(0) + if not self.oldProfile: + self.oldProfile = self.newProfile + try: + self.FO.write(self.oldProfile) + except: + self.setError (_("not open file:" ) + self.nameFileOld) + return False + return True + + def getModeFile(self, 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 __openNewFile(self, nameFileNew): + """Открыть файл профиля""" + FN = False + try: + FN = open (nameFileNew, "r") + self._mode,self._uid,self._gid = self.getModeFile(nameFileNew) + except: + self.setError (_("not open file:" ) + nameFileNew) + return False + return FN + + def __closeNewFile(self): + if self.FN: + self.FN.close() + self.FN = False + + def __closeOldFile(self): + if self.FO: + self.FO.close() + self.FO = False + + def __openOldFile(self, nameFileOld, mode, uid, gid): + """Октрыть конфигурационный файл""" + FO = False + try: + if os.path.islink(nameFileOld): + # если ссылка то удаляем её + os.unlink(nameFileOld) + FO = open (nameFileOld, "r+") + except: + try: + fd = os.open(nameFileOld, os.O_CREAT) + os.close(fd) + os.chmod(nameFileOld, mode) + os.chown(nameFileOld,uid,gid) + FO = open(nameFileOld, "r+") + except: + self.setError (_("not open file:" ) + nameFileOld) + return False + return FO + + + def openFiles(self, nameFileNew, nameFileOld): + """Открывает два профайла новый и старый""" + self.oldProfile = "" + self.newProfile = "" + self.closeFiles() + self.FN = False + self.FO = False + self.nameFileOld = self.__absFileName(nameFileOld) + self.nameFileNew = self.__absFileName(nameFileNew) + self.FN = self.__openNewFile(self.nameFileNew) + self.FO = self.__openOldFile(self.nameFileOld, + self._mode, self._uid, self._gid) + if self.FN and self.FO: + self.newProfile = self.FN.read() + self.oldProfile = self.FO.read() + + def __del__(self): + self.closeFiles() + + def closeFiles(self): + """Закрытие файлов""" + self.__closeNewFile() + self.__closeOldFile() + +class profile(_file, _terms): + """Класс для работы с профилями + + На вход 2 параметра: объект хранения переменных, имя сервиса - не + обязательный параметр + + """ + def __init__(self, objVar, servDir=False): + _file.__init__(self) + # Заголовок title + self.__titleHead = "--------------------------------------\ +----------------------------------------" + self._titleBody = "" + self._titleList = (_("Modified"), _("File of a profile")) + + # Метки + varStart = "#-" + varEnd = "-#" + self._reVar = re.compile(("%s[a-zA-Z0-9_-]+%s")%(varStart,varEnd),re.M) + self._deltVarStart = len(varStart) + self._deltVarEnd = len(varEnd) + # Условия + self._reTermBloc = re.compile("#\?(?P[a-zA-Z0-9\-_]+)\ +(?P[\>\<\=\!\&\|]+\ +[a-zA-Z0-9\>\<\=\!\|\&\-_\.]+)#\ +\n*(?P.+?)\n*#(?P=rTerm)#(?P\s+)",re.M|re.S) + # Объект с переменными + self.objVar = objVar + # Базовая директория переноса профилей "/mnt/calculate" или "/" и.т.д + baseDir = self.objVar.Get("setup_path_install") + self._baseDir = os.path.split(baseDir)[0] + if self._baseDir == "/": + self._baseDir = "" + # Последняя часть директории профиля (имя сервиса: samba, mail) + self._servDir = servDir + if self._servDir: + if self._servDir[0] != "/": + self._servDir = "/" + self._servDir + if self._servDir[-1] != "/": + self._servDir += "/" + self._servDir = os.path.split(self._servDir)[0] + + # Преобразование восьмеричного в целое (ввод строка, вывод число) + def __octToInt(self, strOct): + if strOct: + try: + exec("res =" + "0" + strOct) + except: + self.setError (_("Not valid oct value: ") + str(strOct)) + return False + return res + else: + self.setError (_("Empty oct value")) + return False + + def applyVarsProfile(self, textProfile): + """ Заменяет переменные на их значения + """ + resS = self._reVar.search(textProfile) + textProfileTmp = textProfile + while resS: + mark = textProfileTmp[resS.start():resS.end()] + varName = mark[self._deltVarStart:-self._deltVarEnd] + varValue = "" + if self.objVar.defined(varName): + varValue = self.objVar.Get(varName) + textProfileTmp = textProfileTmp.replace(mark, varValue) + resS = self._reVar.search(textProfileTmp) + return textProfileTmp + + def applyTermsProfile(self, textProfile, nameProfile): + """ Применяет условия, к условным блокам текста + """ + textTerm = "" + resS = self._reTermBloc.search(textProfile) + textProfileTmp = textProfile + while resS: + mark = resS.group(0) + body = resS.group("body") + end = resS.group("end") + term = resS.group("rTerm") + resS.group("lTerm") + if self._equalTerm(term, _("content profile not valid: ")+\ + nameProfile): + textProfileTmp = textProfileTmp.replace(mark, body+end) + else: + textProfileTmp = textProfileTmp.replace(mark, "") + resS = self._reTermBloc.search(textProfileTmp) + return textProfileTmp + + def getNeedProfile(self, fileProfile): + """Применяем правила к названию файла""" + dirP,fileP = os.path.split(fileProfile) + if fileP: + spFile = fileP.split("?") + realFileName = spFile[0] + if len(spFile)>1: + flagTrue = False + for term in spFile[1:]: + if self._equalTerm(term, _("name profile not valid: ")+\ + fileProfile): + flagTrue = True + break + if flagTrue: + return True + else: + return False + else: + return True + else: + self.setError (_("name profile not valid: ")+ str(fileProfile)) + return False + + def getTitle(self, comment, commentList): + """Выдает заголовок профиля ( версия и.т.д)""" + if comment: + self._titleBody = comment + self.__titleHead + "\n" + z = 0 + lenCommentList = len(commentList) - 1 + for com in self._titleList: + if lenCommentList < z: + self._titleBody += comment + " " + com + "\n" + else: + self._titleBody += comment+ " " + com +\ + " " + commentList[z] + "\n" + z += 1 + + self._titleBody += comment + self.__titleHead + "\n" + return self._titleBody + else: + return "" + + def applyProfiles(self): + """Применяет профили к конфигурационным файлам""" + if not self.objVar.defined("setup_path_profinstall"): + self.setError (_("not defined Var: ") + "setup_path_profinstall") + return False + dirsProfInstall = self.objVar.Get("setup_path_profinstall") + if not self.objVar.defined("setup_path_profuser"): + self.setError (_("not defined Var: ") + "setup_path_profuser") + return False + dirsProfUser = self.objVar.Get("setup_path_profuser") + # Получаем директории профиля + if dirsProfInstall and dirsProfUser: + dirsProfiles = dirsProfInstall + dirsProfUser + elif dirsProfInstall: + dirsProfiles = dirsProfInstall + elif dirsProfUser: + dirsProfiles = dirsProfUser + if self._servDir: + tmpDirsProfiles = [] + for dirP in dirsProfiles: + dirProf = dirP + self._servDir + if os.access(dirProf, os.F_OK): + # Если директория существует + tmpDirsProfiles.append(dirProf) + else: + tmpDirsProfiles.append(False) + dirsProfiles = tmpDirsProfiles + dirObjs = self.scanDirs(dirsProfiles) + for dirObj in dirObjs: + for dirProfile in dirObj.dirs: + self.createDir(dirObj.baseDir, dirProfile, self._baseDir) + for fileProfile in dirObj.files: + if self.getNeedProfile(fileProfile): + if self.getError(): + print self.getError() + return False + oldFile = fileProfile.split(dirObj.baseDir)[1] + dirName,fileName = os.path.split(oldFile) + fileName = fileName.split("?")[0] + if dirName == "/": + dirName = "" + oldFile = self._baseDir + dirName + "/" + fileName + listProfTitle = dirObj.baseDir.split("/")[-2:] + profTitle = '"' + "/".join(listProfTitle) + '"' + self.join(fileProfile, oldFile, + (self.objVar.Get("setup_name"),profTitle)) + else: + if self.getError(): + print self.getError() + return False + + def __getApplyHeadProfile(self ,newFile, oldFile): + self.nameFileNew = self.absFileName(newFile) + self.closeNewFile() + self.FN = self.openNewFile(self.nameFileNew) + self.newProfile = self.FN.read() + objHeadNew = calcHeader(self.newProfile, False, + self.getFileType(),objVar=self.objVar) + if not objHeadNew.headerTerm: + return False + flagSymlink = False + flagForce = False + # Если есть указатель на файл профиля (link) + if objHeadNew.params.has_key("link"): + prevOldFile = oldFile + oldFile = objHeadNew.params['link'] + # Если символическая ссылка + if objHeadNew.params.has_key("symbolic"): + flagSymlink = True + + oldFileExists = os.path.exists(oldFile) + # В случае force + if objHeadNew.params.has_key("force") and oldFileExists: + FO = self.openNewFile(oldFile) + buff = FO.read() + FO.close() + os.remove(oldFile) + fd = os.open(oldFile, os.O_CREAT) + os.close(fd) + os.chmod(oldFile, self._mode) + os.chown(oldFile, self._uid, self._gid) + FON = open (oldFile, "r+") + FON.write(buff) + FON.close() + + # chmod - если файла нет изменяем права + if objHeadNew.params.has_key("chmod") and not oldFileExists: + mode = self.__octToInt(objHeadNew.params['chmod']) + if mode: + if not os.path.exists(oldFile): + fd = os.open(oldFile, os.O_CREAT) + os.close(fd) + os.chmod(oldFile, mode) + else: + self.setError (_("False value 'chmod' in profile: " ) +\ + newFile) + return False + + # chown - если файла нет изменяем владельца и группу + if objHeadNew.params.has_key("chown") and not oldFileExists: + owner = objHeadNew.params['chown'] + if owner: + if ":" in owner: + strUid, strGid = owner.split(":") + import pwd + try: + uid = pwd.getpwnam(strUid)[2] + except: + self.setError (_("Not user in this system: ") + strUid) + self.setError (_("False value 'chown' in profile: " )+\ + newFile) + return False + try: + import grp + gid = grp.getgrnam(strGid)[2] + except: + self.setError (_("Not group in this system: ")+strGid) + self.setError (_("False value 'chown' in profile: " )+\ + newFile) + return False + + if not os.path.exists(oldFile): + FO = self.openNewFile(newFile) + FO.close() + fd = os.open(oldFile, os.O_CREAT) + os.close(fd) + os.chmod(oldFile, self._mode) + os.chown(oldFile, uid, gid) + else: + self.setError (_("False value 'chown' in profile: " ) +\ + newFile) + return False + else: + self.setError (_("False value 'chown' in profile: " ) +\ + newFile) + return False + + self.openFiles(newFile, oldFile) + if flagSymlink: + if os.path.exists(prevOldFile): + os.remove(prevOldFile) + os.symlink(oldFile, prevOldFile) + return objHeadNew + + def join(self, newFile, oldFile, ListOptTitle): + """Объединения профиля и конф. файла + + join(newFile, oldFile, ListOptTitle) + Объединение профиля newFile и конф. файла oldFile, + ListOptTitle - список строк которые добавятся в заголовок + """ + + objHeadNew = self.__getApplyHeadProfile(newFile, oldFile) + if not objHeadNew: + return True + + self.newProfile = objHeadNew.body + if objHeadNew.fileType != "bin": + self.newProfile = self.applyTermsProfile(self.newProfile, + newFile) + self.newProfile = self.applyVarsProfile(self.newProfile) + + # Титл конфигурационного файла + title = "" + if ListOptTitle: + title = self.getTitle(objHeadNew.comment, + ListOptTitle) + title = title.encode("UTF-8") + + objHeadOld = False + if objHeadNew.comment: + objHeadOld = calcHeader(self.oldProfile, objHeadNew.comment) + # Тестирование + print self.nameFileOld + print objHeadNew.typeAppend + + if objHeadNew.fileType: + # Замена + if objHeadNew.typeAppend == "replace": + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr+title+self.newProfile + else: + self.oldProfile = title + self.newProfile + self.saveOldFile() + return True + # Впереди + elif objHeadNew.typeAppend == "before": + if objHeadOld and objHeadOld.body: + self.oldProfile = objHeadOld.body + if self.newProfile[-1] == "\n": + tmpProfile = self.newProfile + self.oldProfile + else: + tmpProfile = self.newProfile + "\n" + self.oldProfile + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + title + tmpProfile + elif objHeadOld.execStr: + self.oldProfile = objHeadOld.execStr + title + tmpProfile + else: + self.oldProfile = title + tmpProfile + + #print self.oldProfile + self.saveOldFile() + return True + # Cзади + elif objHeadNew.typeAppend == "after": + if objHeadOld and objHeadOld.body: + self.oldProfile = objHeadOld.body + if self.newProfile[-1] == "\n": + tmpProfile = self.oldProfile + self.newProfile + else: + tmpProfile = self.oldProfile + "\n" + self.newProfile + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + title + tmpProfile + elif objHeadOld.execStr: + self.oldProfile = objHeadOld.execStr + title + tmpProfile + else: + self.oldProfile = title + tmpProfile + self.saveOldFile() + return True + # Объединение + elif objHeadNew.typeAppend == "join": + try: + exec ("objProfNew=%s(self.newProfile)"%\ + (objHeadNew.fileType)) + except NameError: + self.setError (_("False join profile for type profile: " )\ + + objHeadNew.fileType + " : " +\ + newFile) + return False + + # Титл для объединения + if ListOptTitle: + title = self.getTitle(objProfNew._comment, + ListOptTitle) + title = title.encode("UTF-8") + + # В случае пустого конфигурационного файла + reNoClean = re.compile("[^\s]",re.M) + if not self.oldProfile or\ + not reNoClean.search(self.oldProfile): + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + \ + title + self.newProfile + else: + self.oldProfile = title + self.newProfile + self.saveOldFile() + return True + + objHeadOld = calcHeader(self.oldProfile, objProfNew._comment) + if objHeadOld.body: + self.oldProfile = objHeadOld.body + exec ("objProfOld=%s(self.oldProfile)"%\ + (objHeadNew.fileType)) + + #print "#%s#" %(objProfOld.docObj.body.toprettyxml()) + #print "#%s#" %(objProfNew.docObj.body.toprettyxml()) + objProfOld.join(objProfNew) + + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + title +\ + objProfOld.getConfig().encode("UTF-8") + elif objHeadOld.execStr: + self.oldProfile = objHeadOld.execStr + title +\ + objProfOld.getConfig().encode("UTF-8") + else: + self.oldProfile = title +\ + objProfOld.getConfig().encode("UTF-8") + self.saveOldFile() + return True + else: + self.setError (_("False (type append) profile: " ) +\ + objHeadNew.typeAppend) + return False + else: + self.setError (_("Type profile not found: ") + newFile) + return False + +class samba(objShare): + """Класс для обработки конфигурационного файла типа samba + + """ + _comment = "#" + configName = "samba" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*|\s*;.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # XML документ + self.doc = self.docObj.doc + + def join(self, sambaObj): + """Объединяем конфигурации""" + if isinstance(sambaObj, samba): + self.docObj.joinDoc(sambaObj.doc) + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def splitCleanBloc(self, txtBloc): + """Делим блок на две части (переменные, пустые строки в конце)""" + txtLines = txtBloc.split("\n") + firstBloc = [] + nextBloc = [] + txtLines.reverse() + z = 0 + for txtLine in txtLines: + if not txtLine.strip(): + nextBloc.append(txtLine) + else: + break + z += 1 + txtLines.reverse() + firstBloc = txtLines[:-z] + nextBloc.reverse() + if nextBloc: + firstBloc.append("") + if nextBloc and "\n".join(nextBloc): + return ("\n".join(firstBloc), "\n".join(nextBloc)) + else: + return False + + def getFullAreas(self, blocs): + """Делит текст на области, (Заголовок, тело) + + Возвращает два списка: заголовки, тела + """ + headsAreas = [] + bodyAreas = [] + if not blocs: + return [] + lenBlocs = len(blocs[0]) + for i in range(lenBlocs): + txtBloc = blocs[1][i] + clean = self.splitCleanBloc(txtBloc) + if clean: + headsAreas.append(blocs[0][i]) + bodyAreas.append(clean[0]) + headsAreas.append("") + bodyAreas.append(clean[1]) + else: + headsAreas.append(blocs[0][i]) + bodyAreas.append(blocs[1][i]) + return (headsAreas, bodyAreas) + + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + # у пустой области обязателен заголовок + #docObj.createCaption("", ["",""]) + #area = docObj.createArea() + #rootNode.appendChild(area) + return docObj + + for h in blocs[0]: + listfinH = h.split("]") + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + headers.append(finH.replace("[","").replace("]","").strip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Удаляемое в дальнейшем поле + if f.name[0] == "!": + xmlField = docObj.createField("var", + [f.br.replace("\n","")], + f.name[1:], [f.value]) + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + + +class named(objShare): + """Класс для обработки конфигурационного файла типа named + + """ + _comment = "//" + configName = "named" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*;[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # XML документ + self.doc = self.docObj.doc + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = text[:openBl.start()] + startBloc = text[openBl.start():openBl.end()] + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = first + start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + return areas + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createFieldTerm(self, name, value, quote, docObj): + """Создание поля переменная - значение + + при создании поля проверяется первый символ названия переменной + и добавляется тег action + "!" - drop + """ + fieldAction = False + if name: + if name[0] == "!" or name[0] == "-" or name[0] == "+": + fieldXML = docObj.createField("var", + [quote.replace("\n","")[1:]], + name[1:], [value], + False, False) + if name[0] == "!": + fieldAction = "drop" + elif name[0] == "+": + fieldAction = "join" + else: + fieldXML = docObj.createField("var", + [quote.replace("\n","")], + name, [value], + False, False) + else: + fieldXML = docObj.createField("var", + [quote.replace("\n","")], + name, [value], + False, False) + if fieldAction: + docObj.setActionField(fieldXML, fieldAction) + return fieldXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header and i.start: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + else: + areaXML = rootNode + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header and f.start: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + areaXML.appendChild(fieldXML) + + if i.header and i.start: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + def textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + # у пустой области обязателен заголовок + docObj.createCaption("", ["",""]) + area = docObj.createArea() + docObj.getNodeBody().appendChild(area) + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def join(self, namedObj): + """Объединяем конфигурации""" + if isinstance(namedObj, named): + self.docObj.joinDoc(namedObj.doc) + +class apache(named): + """Класс для обработки конфигурационного файла типа apache + + """ + _comment = "#" + configName = "apache" + configVersion = "0.1" + __headerArea = "[^\<\> \t]+[ \t]+[^\<\> \t]+" + __openArea = "[ \t]*\<%s\>"%(__headerArea) + __closeArea = "[ \t]*\<\/[^\<\>]+\>" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + reHeader = re.compile(__headerArea) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, apacheObj): + """Объединяем конфигурации""" + if isinstance(apacheObj, apache): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(apacheObj.doc) + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment, reHeader): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = "" + startBloc = text[openBl.start():openBl.end()] + headBl = reHeader.search(startBloc) + if headBl: + firstBloc = headBl.group(0) + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields, reHeader): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment, reHeader) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields, reHeader) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) == 3: + valueList = nameValue[2:] + nameValue =["".join(nameValue[:2])," ".join(valueList)] + + if len(nameValue) > 3: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def textToXML(self): + """Преобразуем тект в XML""" + areas = [] + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields, + self.reHeader) + docObj = xmlDoc() + + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + # у пустой области обязателен заголовок + docObj.createCaption("", ["",""]) + area = docObj.createArea() + docObj.getNodeBody().appendChild(area) + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + +class postfix(apache): + """Класс для обработки конфигурационного файла типа postfix + + """ + _comment = "#" + configName = "postfix" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + def __init__(self,text): + self.text = text + # Объект документ + self.docObj = self.textToXML() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, postfixObj): + """Объединяем конфигурации""" + if isinstance(postfixObj, postfix): + self.docObj.joinDoc(postfixObj.doc) + + def textToXML(self): + """Преобразуем текст в XML""" + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + areas = [] + oneArea = area() + oneArea.header = "" + oneArea.start = "" + oneArea.fields = [self.text] + oneArea.end = "" + areas.append(oneArea) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + # у пустой области обязателен заголовок + docObj.createCaption("", ["",""]) + area = docObj.createArea() + docObj.getNodeBody().appendChild(area) + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def setDataField(self, txtLines, endtxtLines): + """Cоздаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + +class proftpd(apache): + """Класс для обработки конфигурационного файла типа proftpd + + """ + configName = "proftpd" + configVersion = "0.1" + + def join(self, namedObj): + """Объединяем конфигурации""" + if isinstance(namedObj, proftpd): + self.docObj.joinDoc(namedObj.doc) \ No newline at end of file diff --git a/pym/cl_utils.py b/pym/cl_utils.py new file mode 100644 index 0000000..db5cce5 --- /dev/null +++ b/pym/cl_utils.py @@ -0,0 +1,182 @@ +#-*- coding: utf-8 -*- + +#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru +# +# 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 filecmp +import string +from random import choice +from re import search +import os + +def getdirlist(s_path): + #Получить список директорий по указаному пути + fdir=filecmp.dircmp(s_path, s_path) + dir_list=fdir.common_dirs + return dir_list + + #вернуть случайный пароль +def genpassword(): + res=''.join([choice(string.ascii_letters+string.digits)\ + for i in xrange(9)]) + return res + + #заполнить строку указанным количеством символов +def fillstr(char, width): + tstr="" + nstr=string.zfill(tstr,width) + nstr=nstr.replace("0",char) + return nstr + + #вернуть пути для запуска утилит +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 "PATH="+":".join(lpath)+" && " + +#класс для работы с установленными пакетами +class pakages: + #путь к директории установленнх пакетов + pkgdir="/var/db/pkg/" + #список установленных пакетов + pkglist={} + #Объект содержащий параметры пакета + class pakage(object): + #имя пакета с версией + fullname="" + #имя пакета + name="" + #версия + ver="" + #тип пакета в портижах + portdir="" + def __init__(self, **args): + for atname,atvalue in args.items(): + setattr(self,atname, atvalue) + + def __init__(self): + self.pkglist=self.__getpkglist() + + #разбить имя пакета на тип и имя + def __getpkgver(self, fname): + res=search('^(.+)\-([0-9]+.*)$',fname) + if res: + return res.groups() + else: + return (None,None) + + #собрать установленные в системе пакеты + def __getpkglist(self): + portageDirs=[] + instaledPkg={} + #проверим на существование директории с установленными пакетами + if os.path.exists(self.pkgdir): + #получим список типов пакетов + portageDirs=getdirlist(self.pkgdir) + if len(portageDirs)>0: + #обрабатываем содержимое каждого из типов + for portageDir in portageDirs: + pkgList=getdirlist(self.pkgdir+portageDir) + for pkg in pkgList: + fullname=pkg + pkgName,pkgVer= self.__getpkgver(pkg) + pobj=self.pakage(fullname=fullname, + name=pkgName, \ + ver=pkgVer,\ + portdir=portageDir) + fpkg=portageDir+"/"+pkgName + if instaledPkg.has_key(fpkg): + instaledPkg[fpkg].append(pobj) + else: + instaledPkg[fpkg]=[pobj] + return instaledPkg + + #разбить pkgname на составляющие имени пакета + def __partname(self, pkgname): + if not pkgname.strip(): + return False + res=search('^(.+\/)?(.+)',pkgname) + tname=None + if res.group(1): + tname=res.group(1) + if res.group(2): + res2=search('^(.+)(\-[0-9]+.+$)',res.group(2)) + if res2: + name=res2.group(1) + ver=res2.group(2) + else: + name=res.group(2) + ver=None + if res: + if name and name[-1:]=='-': + name=name[:-1] + if tname and tname[-1:]=='/': + tname=tname[:-1] + if ver and ver[0]=='-': + ver=ver[1:] + return [tname, name, ver] + + + #проверить установленн ли пакет + #isinstalled('dev-db/postgresql') + def isinstalled(self, pkgname): + res=self.getinstpkg(pkgname) + if len(res)>0: + return True + else: + return False + + #вернуть список объектов pakage() соответствующих pkgname + #getinstpkg('dev-db/postgresql') + #в случае отсутствия пакетов возвращает пустой список + def getinstpkg(self, pkgname): + pinfo=self.__partname(pkgname) + if pinfo: + ret=[] + if pinfo[0] and pinfo[1] and pinfo[2]: + if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]): + return [] + fpkg=self.pkglist[pinfo[0]+'/'+pinfo[1]] + ret=[] + for i in fpkg: + if i.ver==pinfo[2]: + ret.append(i) + return ret + elif pinfo[0] and pinfo[1]: + if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]): + return [] + return self.pkglist[pinfo[0]+'/'+pinfo[1]] + elif pinfo[1] and pinfo[2]: + for i in self.pkglist.keys(): + if search('^.+\/%s$'%(pinfo[1]),i): + for el in self.pkglist[i]: + if el.ver==pinfo[2]: + ret.append(el) + return ret + elif pinfo[1]: + for i in self.pkglist.keys(): + if search('^.+\/%s$'%(pinfo[1]),i): + ret+=self.pkglist[i] + return ret + return [] + + def getListPkg(self): + return self.pkglist \ No newline at end of file diff --git a/pym/cl_vars.py b/pym/cl_vars.py new file mode 100644 index 0000000..067bceb --- /dev/null +++ b/pym/cl_vars.py @@ -0,0 +1,381 @@ +#-*- coding: utf-8 -*- + +#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru +# +# 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. + +#Допустимые ключи значений +# mode - режим переменной r-не переназначается из командной строки, +# w-переназначается из командной строки +# type - тип переменной состоит из двух элементов(что это и для чего +# это) +# value - дефоултное значение переменной +# select - список допустимых значений переменной +# official - флаг того, что данная переменная служебная и не отображается +# при печати списка значений переменных +# printval - печатное значение переменной(значение выводимое при печати +# списка значений переменных) + +class Data: + #старое значение файла grub_device.map + boot_devicemap_old={'mode':"r", + 'type':('conf','boot'), + } + #новое значение файла device.map + boot_devicemap= {'mode':"w", + 'type':('conf','boot'), + } + #альтернативные строчки загрузчика grub + boot_grub_another= {'mode':"r", + 'type':('conf','boot'), + } + #диск с которого загружена система в формате загрузчика grub + boot_grub_loaddev= {'mode':"r", + 'type':('param','boot'), + } + #раздел с которого загружена система в формате загрузчика grub + boot_grub_loadnum= {'mode':"r", + 'type':('param','boot'), + } + #диск для установки системы в формате загрузчика grub + boot_grub_setupdev={'mode':"r", + 'type':('param','boot'), + } + #раздел для установки системы в формате загрузчика grub + boot_grub_setupnum={'mode':"r", + 'type':('param','boot'), + } + #модель материнской платы + hrd_board_model= {'mode':"r", + 'type':('info','matherboard'), + } + #производитель материнской платы + hrd_board_vendor= {'mode':"r", + 'type':('info','matherboard'), + } + #количество процессоров на ПК + hrd_cpu_num= {'mode':"w", + 'type':('param','cpu'), + } + #количество процессоров на ПК + hrd_laptop_model= {'mode':"r", + 'type':('info','hardware'), + } + #производитель ноутбука + hrd_laptop_vendor= {'mode':"r", + 'type':('info','hardware'), + } + #обозначение видео карты для настроек OpenGL + hrd_opengl_set= {'mode':"r", + 'type':('param','video'), + } + #описание материнской платы + hrd_pc_vendor= {'mode':"r", + 'type':('info','matherboard'), + } + #видео драйвер + hrd_video_drv= {'mode':"w", + 'type':('param','video'), + } + #видео карта + hrd_video_name= {'mode':"r", + 'type':('info','video'), + } + #разрешение экрана + hrd_video_res= {'mode':"w", + 'type':('param','screen'), + } + #DNS имя сервера CDS + net_cds= {'mode':"w", + 'type':('param','lan'), + } + #сетевые настройки файла /etc/conf.d/net + net_conf= {'mode':"r", + 'type':('conf','lan'), + } + #сетевое устройство, через которое пакеты попадают на шлюз + net_gw_dev= {'mode':"r", + 'type':('param','network'), + } + #адрес шлюза + net_gw= {'mode':"w", + 'type':('param','lan'), + } + #имя компьютера + net_host= {'mode':"w", + 'type':('param','lan'), + } + #разрешенная сеть + net_hosts_allow= {'mode':"r", + 'type':('param','lan'), + } + #активное сетевое устройство (выбириется в порядке приоритета eth0-9, + #wlan0-9) + net_lan= {'mode':"r", + 'type':('param','lan'), + } + #текущая сеть + net_networks= {'mode':"r", + 'type':('conf','lan'), + } + #DNS имя NFS сервера + net_nfs= {'mode':"w", + 'type':('conf','lan'), + } + #DNS имя сервера времени + net_ntp= {'mode':"w", + 'type':('conf','lan'), + } + #порт proxy сервера + net_proxy_port= {'mode':"w", + 'type':('param','lan'), + } + #строка для настройки proxy сервера + net_proxy_url= {'mode':"r", + 'type':('param','lan'), + } + #DNS имя proxy сервера + net_proxy= {'mode':"w", + 'type':('param','lan'), + } + #файловая система root раздела + setup_formatfs= {'mode':"w", + 'type':('param','install'), + } + #диск и раздел для установки + setup_installdev= {'mode':"w", + 'type':('param','install'), + } + #имя ISO-файла для создания загружаемого образа системы + setup_iso_name= {'mode':"r", + 'type':('param','install'), + } + #версия системы (в зависимости от операции - обновление установка, сборка) + setup_linux_ver= {'mode':"w", + 'type':('param','system'), + } + #имя tar-архива с образом устанавливаемой системы + setup_linuxpkg= {'mode':"r", + 'type':('param','install'), + } + #установлено в ram в случае, если установка профиля производится в RAM-диск + #(настройка сеанса CDROM) + setup_location= {'mode':"r", + 'type':('param','system'), + 'value':""} + #значение флага -j команды make + setup_makeopts= {'mode':"w", + 'type':('param','install'), + } + #архитектура процессора (в зависимости от операции - обновление установка, + #сборка) + setup_march= {'mode':"w", + 'type':('param','system'), + } + #имя и версия программы + setup_name= {'mode':"r", + 'type':('info','install'), + } + #название системы из-под которой запущена программа + setup_os_current= {'mode':"r", + 'type':('info','system'), + } + #этап запуска (установка, сборка или обновление) + setup_pass= {'mode':"r", + 'type':('param','install'), + } + #путь к директории с профилями + setup_path_profuser={'mode':"r", + 'type':('path','install'), + } + #путь к спискам пакетов для сборки новой системы + setup_path_constpkg={'mode':"r", + 'type':('path','install'), + } + #директория сборки новой системы + setup_path_constructor={'mode':"w", + 'type':('path','install'), + 'value':'/mnt/calculate'} + #путь к исходным файлам пакетов программ + setup_path_distfiles={'mode':"r", + 'type':('path','install'), + } + #пути переменной окружения PATH (изменяются в случае запуска программы на + #сильно измененной Linux системе) + setup_path_env= {'mode':"r", + 'type':('cmd','install'), + } + #путь к директории точки монтирования для установки новой системы + setup_path_install={'mode':"w", + 'type':('path','install'), + 'value':'/mnt/install'} + #путь к дистрибутивам + setup_path_linuxpkg={'mode':"r", + 'type':('path','install'), + } + #директория к патчам (выполняемым скриптам, содержащим определенные + #инструкции для внесения изменений в систему) + setup_path_patch= {'mode':"w", + 'type':('path','install'), + } + #путь к бинарным пакетам программ + setup_path_pkg= {'mode':"r", + 'type':('path','install'), + } + #путь к портежам + setup_path_portage={'mode':"r", + 'type':('path','install'), + } + #директория временных файлов + setup_path_tmp= {'mode':"w", + 'type':('path','install'), + } + #список накладываемых профилей при установке, наложении профилей + setup_path_profinstall= {'mode':"r", + 'type':('path','install'), + } + #флаг установки MBR записи для загрузки системы (по умолчанию установлен) + setup_set_mbr= {'mode':"w", + 'type':('bool','install'), + } + #имя сборки + setup_sys_fullname={'mode':"r", + 'type':('info','system'), + } + #короткое название системы (CLD) + setup_sys_shortname={'mode':"r", + 'type':('param','system'), + } + #emerge флаг размаскировки пакетов (меняется при установке, сборке, + #обновлении) + setup_unmask= {'mode':"r", + 'type':('param','install'), + } + #номер версии программы + setup_ver= {'mode':"r", + 'type':('param','install'), + 'value':"0.0.1 alpha 1"} + #версия загруженной системы + sys_current_ver= {'mode':"r", + 'type':('param','system'), + } + #домен + sys_domain= {'mode':"w", + 'type':('param','system'), + } + #тип операционной системы + sys_linux_type= {'mode':"r", + 'type':('param','system'), + } + #носитель в значение ram в случае загрузки с CD_DVD + sys_load= {'mode':"r", + 'type':('param','system'), + } + #монтируемые диски (в т.ч. сетевые) в системе + sys_mount_dev= {'mode':"r", + 'type':('param','system'), + } + #дополнительные настройки монтируемых дисков файла /etc/fstab + sys_mount_line= {'mode':"r", + 'type':('param','system'), + } + #флаг переноса подключений из fstab + sys_set_movefstab= {'mode':"w", + 'type':('bool','system'), + 'value':True} + #раздел подкачки (swap) + sys_swap_dev= {'mode':"w", + 'type':('param','system'), + } + #строка инициализации swap-раздела файла /etc/fstab + sys_swap_line= {'mode':"r", + 'type':('conf','system'), + } + #----------------------------------------------------- + #Служебные переменные + #----------------------------------------------------- + #хэш доступных grub-у дисков + boot_grub_map= {'mode':"r", + 'type':('param','install'), + 'official':True} + #путь к программе host + cmd_exec_host= {'mode':"r", + 'type':('cmd','install'), + 'official':True} + #путь к программе nmap + cmd_exec_nmap= {'mode':"r", + 'type':('cmd','install'), + 'official':True} + #строка запуска emerge при сборке новой системы + cmd_run_emerge1= {'mode':"r", + 'type':('cmd','install'), + 'official':True} + #строка запуска emerge при сборке новой системы + cmd_run_emerge2= {'mode':"r", + 'type':('cmd','install'), + 'official':True} + #команда форматирования диска + cmd_run_format= {'mode':"r", + 'type':('cmd','install'), + 'official':True} + #команда инициализации OpenGL + cmd_run_opengl= {'mode':"r", + 'type':('cmd','install'), + 'official':True} + #хэш конвертации устаревшего формата hda на sda + setup_ata= {'mode':"r", + 'type':('param','install'), + 'official':False} + #версии сборок с сокращенным и полным именем + setup_dist_ver= {'mode':"r", + 'type':('param','system'), + 'value':{'CLD':"Calculate Linux Desktop", + 'CDS':"Calculate Directory Server", + 'CWS':"Calculate Web Server", + 'AcoolA':"Calculate Web Server"}, + 'official':True} + #версии сборок с сокращенным и полным именем + setup_path_other= {'mode':"r", + 'type':('param','system'), + 'value':{'profile':["/usr/calculate2/profile/", + "/usr/share/calculate/profile/"] + }, + 'official':True} + #разделительная линия в комментариях + setup_mesline= {'mode':"w", + 'type':('info','install'), + 'official':True} + #текст заголовка изменяемых конфигурационных файлов + setup_mestitle= {'mode':"w", + 'type':('info','install'), + 'official':True} + #символ разделителя версии и патча + setup_ospatchsplit={'mode':"r", + 'type':('param','install'), + 'official':True} + #список доступных патчей + setup_patches= {'mode':"r", + 'type':('param','system'), + 'official':True} + #номер патча текущей системы + sys_patchcur= {'mode':"r", + 'type':('param','system'), + 'official':True} + #номер последнего установленного патча + sys_patchlast= {'mode':"r", + 'type':('param','system'), + 'official':True} + #директории точек монтирования в системе + sys_path_mounts= {'mode':"r", + 'type':('path','system'), + 'official':True} diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..b121954 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[install] +install-purelib=/usr/lib/calculate +install-platlib=/usr/lib/calculate \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..7aa88db --- /dev/null +++ b/setup.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# setup.py --- Setup script for calculate-server + +#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru +# +# 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 distutils.core import setup + + +setup( + name = 'calculate-lib', + version = "0.0.1", + description = "The library for Calculate 2", + author = "Calculate Pack", + author_email = "support@calculate.ru", + url = "http://calculate-linux.ru", + license = "http://www.apache.org/licenses/LICENSE-2.0", + package_dir = {'calculate-lib': "."}, + packages = ['calculate-lib.pym'], + data_files = [("/usr/share/calculate/i18n",['i18n/cl_lib_ru.mo'])], +)