#-*- coding: utf-8 -*- # Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from contextlib import contextmanager import sys import os import stat import re import errno import xml.dom.minidom as minidom import xml import importlib if hasattr(xml,"use_pyxml"): xml.use_pyxml() from xml import xpath import subprocess import types import random import string import time import glob import hashlib import fcntl from itertools import * # < <= == != >= > from operator import lt, le, eq, ne, ge, gt from utils.common import _error, _warning,getTupleVersion,getPortageUidGid from utils.text import _toUNICODE, convertStrListDict from utils.portage import isPkgInstalled,reVerSplitToPV from utils.content import PkgContents,checkContents,getCfgFiles,fillContents from utils.files import (getModeFile, listDirectory,removeDir, typeFile, scanDirectory,timeout, pathJoin,readFile,readLinesFile,process,STDOUT) from datavars import DataVarsError from calculate.lib.cl_lang import setLocalTranslate, RegexpLocalization setLocalTranslate('cl_lib3',sys.modules[__name__]) PORTAGEUID,PORTAGEGID = getPortageUidGid() class TemplatesInterrupt(Exception): """ Interrupt templates appling """ EXIT,ABORT=1,2 def __init__(self,*args): self.message = args[0] self.args = args def __str__(self): return str(self.message) def isExit(self): return self.isInterrupt() and self.args[1] == self.EXIT def isAbort(self): return self.isInterrupt() and self.args[1] == self.ABORT def status(self): return self.args[1] if self.isInterrupt() else 0 def isInterrupt(self): return len(self.args)>1 class TemplatesError(TemplatesInterrupt): """ Error on templates appling """ class _shareTermsFunction: """Общие аттрибуты для классов _terms и templateFunctions""" # Символы допустимые в скобках функции шаблона _reFunctionArgvInSquareBrackets = "a-zA-Z0-9_:;\-\+\,\*\/\.\'\"~\\\\ " _reFunctionArgvText = "[%s]"%_reFunctionArgvInSquareBrackets # регулярное выражение для поиска функции в шаблоне _reFunctionText = "([a-zA-Z0-9\_\-]+)\((%s+|)\)" %_reFunctionArgvText class _terms(_error, _shareTermsFunction): """Вычисление условий применяемых в шаблонах """ # регулярное выражение для поиска функции в шаблоне _reFunction = re.compile(_shareTermsFunction._reFunctionText) # регулярное выражение для не версии _re_not_Version = re.compile("[^0-9\.]") # регулярное выражение не номер _re_not_Number = re.compile("[^0-9]") _suffixDict = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1} _lenSuffixDict = len(_suffixDict) # Регулярное выражение для названия переменной _reRightName = re.compile("^(?:[a-z\_\-]+\.)?(?:[a-zA-Z0-9\_\-]+)$") # Регулярное выражение для сравниваемого значения _reDenyValue = re.compile("[^0-9a-zA-Z_/\.-]") # латинские буквы в нижнем регистре _letters = list(string.ascii_lowercase) def _splitVersion(self, strVersion): """ Split version. Version, addition letter, list suffixes with version, revision. Examples: 3.0.0_beta2 ("3.0.0_beta2","",[],"") 3.0.0_beta2-r1 ("3.0.0_beta2","",[],"r1") 3.0.0_beta2a-r1 ("3.0.0_beta2","a",[],"r1") 3.0.0_beta2a_rc1-r1 ("3.0.0_beta2","a",[("rc","1")],"r1") 3.0.0_beta2a_rc1_p20111212-r1 ("3.0.0_beta2","a",[("rc1","1"),("p","20111212")],"r1") """ # get revision from version strWorkVersion, spl, rVersion = strVersion.rpartition("-") if rVersion == strVersion: strWorkVersion = rVersion rVersion = "" suffixes = [] # get suffixes from version while "_" in strWorkVersion: # 2.3_p45 ('2.3','_','p43') # 2.3_rc4_p45 ('2.3_rc4','_','p43') strWorkVersion, spl, suffix = strWorkVersion.rpartition("_") suffSplList = filter(lambda x: suffix.startswith(x), self._suffixDict.keys()) if suffSplList: suffSpl = suffSplList[0] lenSuffSpl = len(suffSpl) suffixVersion = suffix[lenSuffSpl:] suffixes.append((suffSpl,suffixVersion)) letters = "" numberVersion = strWorkVersion if numberVersion and numberVersion[-1:] in self._letters: letters = numberVersion[-1:] numberVersion = numberVersion[:-1] return numberVersion, letters, suffixes, rVersion def _notVersion(self, strVersion): """strVersion is not version - True""" numberVersion, letters, suffixes, rVersion =\ self._splitVersion(strVersion) if not numberVersion.strip(): return True if self._re_not_Version.search(numberVersion): return True if letters and not letters in self._letters: return True for suffix,suffixVersion in suffixes: if suffixVersion and self._re_not_Number.search(suffixVersion): return True if rVersion: if rVersion[0] != "r" or len(rVersion) == 1: return True if self._re_not_Number.search(rVersion[1:]): return True return False def _convertVers(self, verA, verB): """Конвертирование номеров версий для корректного сравнения """ def fillZero(elemA, elemB): #elemA, elemB = elemA[], elemB[] 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] def fillSuffix(elemA,elemB,sA,svA,sB,svB): if str(sA) or str(sB): svA, svB = map(lambda x: [x] if x else ['0'], (svA, svB)) fillZero(svA, svB) sA, sB = map(lambda x: x if x else 0, (sA, sB)) elemA.append(str(self._lenSuffixDict + sA)) elemA.extend(svA) elemB.append(str(self._lenSuffixDict + sB)) elemB.extend(svB) #Version, letters, suffix, suffixVersion, rVersion vA, lA, ssA, rvA = self._splitVersion(verA) vB, lB, ssB, rvB = self._splitVersion(verB) elemA = vA.split(".") elemB = vB.split(".") fillZero(elemA, elemB) if lA or lB: lA, lB = map(lambda x: x if x else '0', (lA, lB)) elemA.append(lA) elemB.append(lB) # dereferencing suffix in suffixes list ssA = map(lambda x:(self._suffixDict.get(x[0],0),x[1]),ssA) ssB = map(lambda x:(self._suffixDict.get(x[0],0),x[1]),ssB) for suffix,sufVer in reversed(ssA): if ssB: sB,svB = ssB.pop() else: sB,svB = "","" fillSuffix(elemA,elemB,suffix,sufVer,sB,svB) while ssB: sB,svB = ssB.pop() fillSuffix(elemA,elemB,"","",sB,svB) if rvA or rvB: rvA, rvB = map(lambda x: [x[1:]], (rvA, rvB)) fillZero(rvA, rvB) elemA += rvA elemB += rvB return (".".join(elemA), ".".join(elemB)) def _equalTerm(self, term, textError, function=False): """Вычисление логических выражений для условий Для корректной работы в классе который наследует этот класс должен быть объявлен аттрибут self.objVar (объект для работы с переменными) function - функция для для обработки функций в заголовке блока """ rpl = lambda x: x.replace("@@"," ") trm = {"&&":"@@and@@","||":"@@or@@"} dictRuleFunc = {"==":eq, "!=":ne, ">=":ge, "<=":le, ">":gt, "<":lt} rule = dictRuleFunc.keys() listEqual = [] for k in trm.keys(): if k in term: term = term.replace(k,trm[k]) trs = term.split("@@") listSplitOr = [] if "or" in trs: lst = [] for t in trs: if t != "or": lst.append(t) else: listSplitOr.append(lst) lst = [] if lst: listSplitOr.append(lst) else: listSplitOr = [trs] for trsAnd in listSplitOr: listEqual = [] for t in trsAnd: flagRule = False for sepF in rule: if sepF in t: flagRule = True vals = t.split(sepF) break if flagRule: #проверка на допустимость названия переменной flagFunction = False if not self._reRightName.search(vals[0]): #проверка на допустимость функции flagError = True if function: searchFunct = self._reFunction.search(vals[0]) if searchFunct: flagError = False flagFunction = True if flagError: self.setError("'%s'"%rpl(term)+" "+ _("incorrect")) self.setError(textError) return False #проверка на допустимость значения if self._reDenyValue.search(vals[1]): self.setError("'%s'"%rpl(term) + " " + _("incorrect")) self.setError(textError) return False flagIntTypeVar = None if flagFunction: valVars = function("#-%s-#"%vals[0]) if valVars is False: self.setError("'%s'"%rpl(term)+" "+ _("incorrect")) self.setError(textError) return False if "load" == searchFunct.group(1) and\ re.search("\(\s*num\s*,",vals[0]): if valVars: try: valVars = int(valVars) except: self.setError("'%s'"%rpl(term) + " " + \ _("incorrect")) self.setError(textError) return False flagIntTypeVar = True else: flagIntTypeVar = False else: if valVars == "" and\ not self._notVersion(vals[1]): valVars = "0" elif vals[1] == "" and\ not self._notVersion(valVars): vals[1] = "0" else: try: valVars = self.objVar.Get(vals[0]) varTable = self.objVar.Get('cl_used_action') varTable.append((vals[0],vals[1])) if not valVars: valVars = "" except DataVarsError, e: raise TemplatesError("{header}\n{body}".format( header=textError,body=str(e))) # Номера версий для ini flagNotIniFunct = True # Два значения не пусты flagNotEmptyVals = not (valVars == "" and vals[1] == "") if flagFunction and flagNotEmptyVals and\ searchFunct.group(1) == "ini": # Проверка значения на версию if not self._notVersion(valVars) and\ not self._notVersion(vals[1]): verFile, verVar = self._convertVers(vals[1],valVars) res = dictRuleFunc[sepF](verVar,verFile) if res: listEqual.append(True) else: listEqual.append(False) break flagNotIniFunct = False # Cравниваем номера версий if flagNotIniFunct: if flagNotEmptyVals and\ ("_ver" in vals[0] or\ (flagFunction and searchFunct.group(1) in \ ("pkg","merge")) or\ (flagFunction and searchFunct.group(1)=="load" and\ re.search("\(\s*ver\s*,",vals[0]))): # Проверка значения на версию if self._notVersion(vals[1]): self.setError("'%s'"%rpl(term)+" "+\ _("incorrect")) self.setError( _("This value is not a version")) return False # Проверка значения функции на версию if self._notVersion(valVars): self.setError("'%s'"%rpl(term)+" "+\ _("incorrect")) self.setError(\ _("The function value is not a version")) return False verFile, verVar = self._convertVers(vals[1],valVars) res = dictRuleFunc[sepF](verVar,verFile) if res: listEqual.append(True) else: listEqual.append(False) break flagNotIniFunct = False else: if flagIntTypeVar is None: flagIntTypeVar = True try: valVars = int(valVars) except: flagIntTypeVar = False if flagIntTypeVar: if not vals[1].strip(): vals[1] = 0 try: valFile = int(vals[1]) valVar = valVars res = dictRuleFunc[sepF](valVar, valFile) if res: listEqual.append(True) else: listEqual.append(False) break except: flagIntTypeVar = False if not flagIntTypeVar: if sepF == "!=" or sepF == "==": if not vals[1].strip(): vals[1] = "" valFile = vals[1] valVar = valVars res = dictRuleFunc[sepF](valVar, valFile) if res: listEqual.append(True) else: listEqual.append(False) break else: if not flagNotEmptyVals: listEqual.append(False) break else: self.setError("'%s'"%rpl(term) + " "\ + _("incorrect")) self.setError(textError) return False else: if t == "and": if listEqual == [] or False in listEqual: listEqual = [False] break else: listEqual = [True] else: self.setError("'%s'"%rpl(term) + " " + _("incorrect")) self.setError(textError) return False if not (listEqual == [] or False in listEqual): break if listEqual == [] or False in listEqual: return False return True def splitParLine(self, linePar): '''Split params line''' def splitQuote(listPar, quoteSymbol): listTerm = map(lambda x: x+quoteSymbol, ("=",">","<")) flagQ = False mass = [] v = "" for i in listPar: if i.count(quoteSymbol)==1: if flagQ and i.endswith(quoteSymbol): v = v + " " + i mass.append(v) v = "" flagQ = False elif filter(lambda x: x in i, listTerm): flagQ = True v = i else: mass.append(i) elif flagQ: v = v + " " + i else: mass.append(i) foundPar = list(set(mass)-set(listPar)) return not flagQ, filter(lambda x: not x in foundPar, mass),foundPar listPar = re.split("\s+",linePar) flagFoundQ = "'" in linePar flagFoundQQ = '"' in linePar if flagFoundQ and flagFoundQQ: flagQ, listSplQPar, listFoundQPar = splitQuote(listPar, "'") if flagQ: flagQQ, listSplQQPar, listFoundQQPar = splitQuote(listSplQPar, '"') if flagQQ: listPar = listSplQQPar + listFoundQPar + listFoundQQPar elif flagFoundQQ: flagQQ, listSplQQPar, listFoundQQPar = splitQuote(listPar, '"') if flagQQ: listPar = listSplQQPar + listFoundQQPar elif flagFoundQ: flagQ, listSplQPar, listFoundQPar = splitQuote(listPar, "'") if flagQ: listPar = listSplQPar + listFoundQPar if flagFoundQ: listQPar = [] for par in listPar: if par.endswith("'") and par.count("'")>1: listQPar.append(par[:-1].replace("='","=")) else: listQPar.append(par) listPar = listQPar if flagFoundQQ: listQQPar = [] for par in listPar: if par.endswith('"') and par.count('"')>1: listQQPar.append(par[:-1].replace('="','=')) else: listQQPar.append(par) listPar = listQQPar return listPar class fileHeader(_terms): """Обработка заголовков шаблонов и конфигурационных файлов """ # Допустимые параметры заголовка allowParam = ["format","dotall","multiline", "comment", "append", "force", "link", "mirror", "symbolic", "chmod", "chown", "name", "path", "autoupdate","protected", "run","exec","merge", "module","env","postmerge"] # Тип шаблона fileType = "" # Тип вставки шаблона typeAppend = "" # Возможные типы вставки шаблонов _fileAppend = "join", "before", "after", "replace", "remove", "skip",\ "patch", "clear" # Интерпретатор (#!/bin/bash) (#!/usr/bin/python) execStr = "" # Символ комментария comment = False # Выражение для поиска строки интерпретатора reExecStr = re.compile("^(#!.+\s)",re.M) # условные операторы terms = ('>', '<', '==', '!=', '>=', '<=') # параметры без значения listParNotVal = ("multiline", "dotall", "symbolic", "force", "mirror", "autoupdate", "protected") # Результат вычисления условия в заголовке headerTerm = True def __init__(self, templateName, text, comment=False, fileType=False, objVar=False, function=False, templateObj=None): self.body = text # Объект с переменными self.objVar=objVar # Параметры описанные в заголовке файла шаблона self.params = {} # некорректные параметры incorrectParams = [] # Поиск строки запустка (#!/bin/bash и.т. д) if comment or fileType!="bin": reExecRes = self.reExecStr.search(self.body) if reExecRes: self.execStr = self.body[reExecRes.start():reExecRes.end()] self.body = self.body[:reExecRes.start()] +\ self.body[reExecRes.end():] # Удаление Заголовка Calculate if comment: titleFirst = "Modified" # В случае текста XML if type(comment) == types.TupleType and len(comment) == 2: reCalcHeader =\ re.compile("\s*%s\s+%s.+\s+(.+\n)+%s\s?"\ %(comment[0], titleFirst, comment[1]),re.M|re.I) reS = reCalcHeader.search(self.body) if reS: self.body = self.body[:reS.start()]+self.body[reS.end():] else: reCalcHeader = re.compile(\ "\s*%s\-+\s+%s\s+%s.+\s+(%s.+\s+)+%s\-+\s?"\ %(comment, comment, titleFirst ,comment,comment), re.M|re.I) reS = reCalcHeader.search(self.body) if reS: self.body = self.body[reS.end():] if fileType != False: if fileType=="bin": self.params["format"] = fileType self.fileType = self._getType() self.typeAppend = self._getAppend() else: textLines = self.body.splitlines() if textLines: textLine = textLines[0] rePar = re.compile(\ "\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$", re.I) reP = rePar.search(textLine) if reP: reL = False reLns = re.compile(r"\A([^\\\n]*\\\n)+[^\n]*\n*",re.M) reLs = reLns.search(self.body) if reLs: reL = reLs paramLine = self.body[reP.end():reLs.end()] paramLine = paramLine.replace("\\"," ") else: reLn = re.compile("\n") reL = reLn.search(self.body) paramLine = textLine[reP.end():] if reL: self.body = self.body[reL.end():] else: self.body = "" paramLine = templateObj.applyFuncTemplate(paramLine,templateName) paramList = self.splitParLine(paramLine) if paramList: for i in paramList: foundTerm = False for term in self.terms: if term in i: foundTerm = True errorMsg = _("Incorrect template") +\ ": "+ templateName +"\n"+\ _("template header not valid")+\ ": "+ i if function: rezTerm = self._equalTerm(i, errorMsg, function) else: rezTerm = self._equalTerm(i, errorMsg) if not rezTerm: self.headerTerm = False break if not foundTerm: par = i.split("=") if len(par) == 1: if i in self.listParNotVal: self.params[i] = "True" else: if i.strip(): incorrectParams = set([i]) elif len(par) == 2: self.params[par[0]] = par[1] if par[0] == "env": try: varModule = \ importlib.import_module( "calculate.%s.variables" %par[1]) except (ImportError, AttributeError),e: self.headerTerm = False self.comment = self._getComment() self.fileType = self._getType() typeAppend = self._getAppend() if typeAppend: self.typeAppend = typeAppend else: self.headerTerm = False self.setError(_("incorrect header parameter: '%s'")\ %"append=%s"%self.params["append"]) if any(x in self.params for x in ('exec','run')): if 'exec' in self.params: self.execStr = "#!%s\n"%self.params['exec'] if 'run' in self.params: self.execStr = "#!%s\n"%self.params['run'] if "python" in self.execStr: self.execStr += "# -*- coding: utf-8 -*-\n" if not incorrectParams and self.params: incorrectParams = set(self.params.keys()) - set(self.allowParam) if incorrectParams: self.headerTerm = False self.setError(_("incorrect header parameter: '%s'")\ %" ".join(list(incorrectParams))) def _getType(self): """Выдать тип файла""" if "format" in self.params: return self.params["format"] else: return "raw" def _getAppend(self): """Выдать тип добавления файла""" if self.params.has_key("append"): if self.params["append"] in self._fileAppend: return self.params["append"] else: return False else: if self.fileType != "raw" and self.fileType != "bin" and\ self.fileType != "": if "format" in self.params and self.params["format"] in \ ("patch","diff"): self.params["append"] = "patch" return "patch" self.params["append"] = "join" return "join" self.params["append"] = "replace" return "replace" def _getComment(self): """Выдать символ комментария файла""" if self.params.has_key("comment"): if self.params["comment"] in ("xml", "XML"): return ("") else: return self.params["comment"] else: return False class dirHeader(_terms): """Обработка заголовков шаблонов директорий """ # Допустимые параметры заголовка allowParam = ["append", "chmod", "chown", "name", "path", "autoupdate", "module","env","merge","postmerge"] # Тип вставки шаблона typeAppend = "" # Возможные типы вставки шаблонов _fileAppend = "join", "remove", "skip", "clear" # условные операторы terms = ('>', '<', '==', '!=', '>=', '<=') # параметры без значения listParNotVal = ("symbolic", "force", "autoupdate") # Результат вычисления условия в заголовке headerTerm = True def __init__(self, templateName, text, objVar=False, function=False): self.body = text # Объект с переменными self.objVar=objVar # Параметры описанные в заголовке файла шаблона self.params = {} # некорректные параметры incorrectParams = set([]) textLines = text.splitlines() flagErrorBody = False if textLines: textLine = textLines[0] rePar = re.compile(\ "\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$",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[reP.end():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 = "" if self.body.strip(): self.headerTerm = False self.setError(_("incorrect text in the template: '%s'")\ %self.body) flagErrorBody = True if not flagErrorBody: paramList = self.splitParLine(paramLine) if paramList: for i in paramList: foundTerm = False for term in self.terms: if term in i: foundTerm = True errorMsg = _("Incorrect template") +\ ": "+ templateName +"\n"+\ _("template header not valid")+ ": "+ i if function: rezTerm = self._equalTerm(i, errorMsg, function) else: rezTerm = self._equalTerm(i, errorMsg) if not rezTerm: self.headerTerm = False break if not foundTerm: par = i.split("=") if len(par) == 1: if i in self.listParNotVal: self.params[i] = "True" else: if i.strip(): incorrectParams = set([i]) elif len(par) == 2: self.params[par[0]] = par[1] if par[0] == "env": try: varModule = \ importlib.import_module( "calculate.%s.variables"%par[1]) except (ImportError,AttributeError),e: self.headerTerm = False self.objVar.defaultModule = \ self.params['env'] typeAppend = self._getAppend() if typeAppend: self.typeAppend = typeAppend else: self.headerTerm = False self.setError(_("incorrect header parameter: '%s'")\ %"append=%s"%self.params["append"]) if not flagErrorBody: if not incorrectParams: incorrectParams = set(self.params.keys()) - set(self.allowParam) if incorrectParams: self.headerTerm = False self.setError(_("incorrect header parameter: '%s'")\ %" ".join(list(incorrectParams))) def _getAppend(self): """Выдать тип добавления директории""" if self.params.has_key("append"): if self.params["append"] in self._fileAppend: return self.params["append"] else: return False else: return "join" class objShare: """Общий клас для объектов, наследуем """ def createFieldTerm(self, name, value, quote, docObj): """Создание поля переменная - значение при создании поля проверяется первый символ названия переменной и добавляется тег action "!" - drop удаляет "+" - join добавляет "-" - replace заменяет """ fieldAction = False if name: if name[0] == "!" or name[0] == "-" or name[0] == "+": qnt = self.removeSymbolTerm(quote) fieldXML = docObj.createField("var",[qnt], name[1:], [value], False, False) if name[0] == "!": fieldAction = "drop" elif name[0] == "+": fieldXML.setAttribute("type", "seplist") 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 removeSymbolTerm(self, text): """Удаляет первый символ названия переменной в строке Если первый встречающийся символ с начала строки '+', '-', '!' то он из этой строки будет удален, если перед этим символом были пробельные символы, то они будут сохранены, так-же если в строке есть символ перевода строки он будет удален. """ reTerm = re.compile("^[ \t]*(\!|\+|\-)") textNS = text.replace("\n","") res = reTerm.search(textNS) if res: textNS = textNS[res.start():res.end()-1] + textNS[res.end():] return textNS def getConfig(self,joinChar=""): """Выдает конфигурационный файл""" 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 joinChar.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(_toUNICODE(text)) element.appendChild(txtNode) for attr in attributes.keys(): attribute = doc.createAttribute(attr) attribute.nodeValue = attributes[attr] element.setAttributeNode(attribute) return element 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 # Разделитель разделенных списков - по умолчанию перевод строки "\n" #self.sepSplitFields = False def createDoc(self, typeDoc, version): """Создание нового документа новый документ""" docTxt = '' docTxt += '%s'% version docTxt += '%s' % typeDoc docTxt += '' self.doc = minidom.parseString(docTxt) self.root = self.doc.documentElement self.body = xpath.Evaluate('child::body',self.root)[0] # установка разделителя областей self.sepAreas = self.createField("br",[],"",[],False,False) # установка разделителя областей разделенных списков #self.sepSplitFields = 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 ноду поле""" newNameField = self.getNameField(xmlNewField) if not newNameField or not newNameField.strip(): return False fieldsOldComp = xpath.Evaluate("child::field[child::name='%s']"\ %(newNameField), xmlArea) # Если поле не найдено добавляем его typeNewField = self.getTypeField(xmlNewField) if not fieldsOldComp and typeNewField != "seplist": if self.getActionField(xmlNewField) != "drop": self.setActionField(xmlNewField, "append") xmlArea.appendChild(xmlNewField) return True newFieldsAction = self.getActionField(xmlNewField) newValues = self.getFieldValues(xmlNewField) flagCompare = True for nodeFieldOld in fieldsOldComp: if newFieldsAction == "drop": if nodeFieldOld.nextSibling and\ self.getTypeField(nodeFieldOld.nextSibling) == "br": xmlArea.removeChild(nodeFieldOld.nextSibling) elif nodeFieldOld.previousSibling and\ self.getTypeField(nodeFieldOld.previousSibling) == "br": xmlArea.removeChild(nodeFieldOld.previousSibling) xmlArea.removeChild(nodeFieldOld) continue oldValues = self.getFieldValues(nodeFieldOld) # Сравнение значений переменной шаблона и файла if set(newValues) != set(oldValues): flagCompare = False if self.getActionField(xmlNewField) == "drop": return True appSplLst = [] insSplLst = [] if typeNewField == "seplist": if fieldsOldComp: xmlOldField = fieldsOldComp[-1] else: xmlOldField = False seplistNewXML = self.getSepListToField(xmlNewField) if seplistNewXML: for nodeSeplist in seplistNewXML: if self.getActionField(nodeSeplist) != "drop": if newFieldsAction == "join": flagCompareSeplist = False newValues = self.getFieldValues(nodeSeplist) for nodeFieldOld in fieldsOldComp: oldValues = self.getFieldValues(nodeFieldOld) for newValue in newValues: if newValue in oldValues: flagCompareSeplist = True break if not flagCompareSeplist: nextNode = xmlOldField.nextSibling newInsNode = nodeSeplist.cloneNode(True) self.setActionField(newInsNode,"append") if nextNode: appSplLst.append((newInsNode, nextNode, "insert")) else: appSplLst.append((newInsNode, False, "append")) else: newInsNode = nodeSeplist.cloneNode(True) if self.getActionField(newInsNode) == "join": self.setActionField(newInsNode,"append") if xmlOldField: insSplLst.append((newInsNode, xmlOldField, "insert")) else: insSplLst.append((newInsNode, False, "append")) #xmlArea.insertBefore(\ #nodeSeplist.cloneNode(True), #xmlOldField) parentNode = nodeSeplist.parentNode parentNode.removeChild(nodeSeplist) insNodesRepl = [] for newNode, nxtNode, app in insSplLst: flagCompareSeplist = False newValues = self.getFieldValues(newNode) for nodeRepl, nxtNode, app in insNodesRepl: oldValues = self.getFieldValues(nodeRepl) for newValue in newValues: if newValue in oldValues: flagCompareSeplist = True break if not flagCompareSeplist: if xmlOldField: insNodesRepl.append((newNode, nxtNode, app)) for newNode, nxtNode, app in insNodesRepl: if app == "insert": xmlArea.insertBefore(newNode,nxtNode) elif app == "append": xmlArea.appendChild(newNode) if xmlOldField: parentNode = xmlOldField.parentNode if parentNode and newFieldsAction != "join": parentNode.removeChild(xmlOldField) for newNode, nxtNode, app in appSplLst: if app == "insert": xmlArea.insertBefore(newNode,nxtNode) elif app == "append": xmlArea.appendChild(newNode) if not flagCompare and typeNewField != "seplist": # Устанавливаем action=replace self.setActionField(xmlNewField, "replace") # Если параметры поля не сходятся заменяем поле xmlArea.replaceChild(xmlNewField.cloneNode(True), fieldsOldComp[-1]) if newFieldsAction == "join": fieldsOldRemove = [] else: fieldsOldRemove = fieldsOldComp[:-1] for nodeFieldOld in fieldsOldRemove: actionOldNode = self.getActionField(nodeFieldOld) if actionOldNode == "insert" or actionOldNode == "append": pass else: if nodeFieldOld.nextSibling and\ self.getTypeField(nodeFieldOld.nextSibling) == "br": xmlArea.removeChild(nodeFieldOld.nextSibling) xmlArea.removeChild(nodeFieldOld) return True def getSepListToField(self, xmlField): """Выдает элементы распределенного массива Область предок поля, в этой области ищутся элементы распределенного массива """ nameField = self.getNameField(xmlField) if not nameField: return [] parentNode = xmlField.parentNode if parentNode: fieldsVal = xpath.Evaluate(\ "child::field[attribute::type='seplist'][child::name='%s'] "\ %(nameField), parentNode) 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: if self.getActionField(fieldNode) == "drop": pass elif self.getActionField(fieldNode) == "join": pass else: 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) joinNewFields = xpath.Evaluate("child::field[child::action='join']" ,newBody) self.addNewFielsOldArea(newFields, joinNewFields, baseBody) 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" or\ self.getActionArea(node) == "join": 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" or\ self.getActionField(node) == "join": 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 findArea(self, text, reTextHeader, reTextArea, numGroupArea=0): """ Делит текст на области (с заголовками) reTextHeader - регулярное выражение для заголовка области reTextArea - регулярное выражение для всей области numGroupArea - номер групы результата поиска по регулярному выражению по всей области возвращает два списка: первый - заголовки, второй - тела областей без заголоков """ # Заголовки областей headersArea = [] # Тексты областей без заголовков textBodyArea = [] r = reTextArea.search(text) if not r: headersArea.append("") textBodyArea.append(text) return (headersArea, textBodyArea) txtWr = text while r: textArea = r.group(numGroupArea) txtSpl = txtWr.split(textArea) area = txtSpl[0] txtWr = txtSpl[1] if area: headersArea.append("") textBodyArea.append(area) res = reTextHeader.search(textArea) header = textArea[:res.end()] body = textArea[res.end():] headersArea.append(header) textBodyArea.append(body) if txtWr: r = reTextArea.search(txtWr) else: r = False if txtWr: headersArea.append("") textBodyArea.append(txtWr) return (headersArea, textBodyArea) def findBloc(self, text, captionTxtBloc, bodyTxtBloc): """ Делит текст на блоки (с заголовками) captionTxtBloc - регулярное выражение для заголовка блока bodyTxtBloc - регулярное выражение для тела блока возвращает два списка: первый - заголовки, второй - тела блоков """ # Заголовки блоков headersTxt = [] # Тексты блоков blocsTxt = [] r = captionTxtBloc.search(text) if r: headersTxt.append(r.group(0)) txtSpl = text.partition(r.group(0)) blocTxt = txtSpl[0] txtWr = txtSpl[2] 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.partition(r.group(0)) blocTxt = txtSpl[0] txtWr = txtSpl[2] 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)= 4: l = 4 elif lenSymb >= 3: l = 3 elif lenSymb >= 2: l = 2 else: if symbols[0] == '_ch_': return (True,1) else: return (False,1) result = False for i in range(l): if i == 0 and symbols[i] != '_fb_': break elif i > 0 and symbols[i] != '_nb_': break if lenTail>1 and lenTail != i: return (False,1) if i > 0: result = True return (result, i) def _intToChar(self, x): he = hex(x)[2:] exec("ret = '\\x%s'" %he) return ret def _hexToChar(self, he): exec("ret = '\\x%s'" %he) return ret def encode(self, text): """Кодирует смешанный формат в UTF-8""" ind = 0 l = 0 utf = [] lenUtf = [] indErr = [] i = 0 for ch in text: r, l = self._retUTF(ch) utf.append(r) lenUtf.append(l) i+=1 while 1: if utf[ind] == '_fb_': res, l = self._sumbUtf(utf[ind:],lenUtf[ind]) if res is False: indErr.append(ind) if l>0: ind +=l if ind >= len(utf): break else: if utf[ind] != '_ch_': indErr.append(ind) ind +=1 if ind >= len(utf): break if indErr: lenIndErr = len(indErr) block = [] blocks = [] if lenIndErr > 1: i = 1 while 1: if i == 1: block.append(indErr[i-1]) if indErr[i] - indErr[i-1] == 1: block.append(indErr[i]) else: if block: blocks.append(block) block = [] block.append(indErr[i]) i +=1 if i >= lenIndErr: break else: block.append(indErr[0]) if block: blocks.append(block) listErr = [] for block in blocks: string = "" for elem in block: string += hex(ord(text[elem]))[-2:] listErr.append((block[0],"__hex__?%s?__hex__" %string,elem)) textOut = text deltaInd = 0 for erEl in listErr: startInd = erEl[0] + deltaInd endInd = erEl[2] + 1 + deltaInd textOut = textOut[:startInd] + erEl[1] + textOut[endInd:] deltaInd += len(erEl[1])-(erEl[2]-erEl[0]+1) #if i == 1: #break #i += 1 return textOut def decode(self, text): """Декодирует UTF-8 в смешанный формат""" varStart = "__hex__\?" varEnd = "\?__hex__" # -1 Это экранирование '?' которое тоже считается deltVarStart = len(varStart)-1 deltVarEnd = len(varEnd)-1 reVar = re.compile(("%s[a-f0-9]+%s")%(varStart,varEnd),re.M) resS = reVar.search(text) textTemplateTmp = text while resS: mark = textTemplateTmp[resS.start():resS.end()] hexString = mark[deltVarStart:-deltVarEnd] i = 0 stringInsert = "" hexCode = "" for ch in hexString: if i>=1: hexCode += ch stringInsert += self._hexToChar(hexCode) hexCode = "" i = 0 else: hexCode += ch i += 1 textTemplateTmp = textTemplateTmp.replace(mark, stringInsert) resS = reVar.search(textTemplateTmp) return textTemplateTmp class templateFormat(object): """Методы получения классов и объектов форматов шаблонов""" # Импортированные классы поддерживаемых форматов шаблонов importFormats = {} def getClassObj(self, nameClassTemplate): """Создает класс шаблона по имени""" if nameClassTemplate in self.importFormats: classFormat = self.importFormats[nameClassTemplate] else: try: classFormat = getattr(__import__("calculate.lib.format.%s"% nameClassTemplate, globals(), locals(), [nameClassTemplate]), nameClassTemplate) except (ImportError, AttributeError): #Создаем объект из self.newObjProt с помощью # метаклассов if nameClassTemplate in self.newObjProt: # Прототип класса nameProt = self.newObjProt[nameClassTemplate] if nameProt in self.importFormats: classProt = self.importFormats[nameProt] else: try: classProt = getattr( __import__("calculate.lib.format.%s"%nameProt, globals(), locals(), [nameProt]), nameProt) except (ImportError, AttributeError): return False self.importFormats[nameProt] = classProt classFormat = self.createNewClass(nameClassTemplate, (classProt,)) else: return False self.importFormats[nameClassTemplate] = classFormat return classFormat def getFormatObj(self, formatTemplate, textTemplate): """Создание объекта формата шаблона. Объект создается на основании формата шаблона и текста шаблона""" classFormat = self.getClassObj(formatTemplate) if classFormat: return classFormat(textTemplate) else: return False class _shareTemplate: """Общие аттрибуты для классов шаблонов""" # Метка начала переменной varStart = "#-" # Метка конца переменной varEnd = "-#" _deltVarStart = len(varStart) _deltVarEnd = len(varEnd) def getDataUser(self, groupsInfo=False): """Получить информацию о пользователе""" userName = self.objVar.Get("ur_login") if not userName: userName = "root" import pwd try: pwdObj = pwd.getpwnam(userName) uid = pwdObj.pw_uid gid = pwdObj.pw_gid homeDir = self.objVar.Get('ur_home_path') except: raise TemplatesError(_("User %s not found")%str(userName)) if groupsInfo: import grp try: groupName = grp.getgrgid(gid).gr_name except: raise TemplatesError(_("Group ID %s not found")%str(gid)) groupsNames = map(lambda x: x.gr_name,\ filter(lambda x: userName in x.gr_mem, grp.getgrall())) groupsNames = [groupName] + groupsNames return uid, gid, homeDir, groupsNames return uid, gid, homeDir class templateFunction(_error, _warning, _shareTemplate, _shareTermsFunction): """Класс для функций шаблонов""" # Словарь установленных программ {"имя программы":[версии]} installProg = {} # Cписок просканированных категорий установленных программ installCategory = [] # Флаг сканирования всех установленных программ flagAllPkgScan = False # Список названий функций шаблона namesTemplateFunction = [] # Словарь {название функции шаблона: функция шаблона, ...} templateFunction = {} # Регулярное выражение для сложения sNum = re.compile("\-[^\-\+]+|[^\-\+]+") # Регулярное выражение для умножениея и деления sMD = re.compile("[^\-\+\*\/]+") # директория установленных программ basePkgDir = "/var/db/pkg" # кэш для проверки наличия пакета в портежах cachePortdir = {} # стек глобальных переменных stackGlobalVars = [] # регулярное выражение для поиска версии reFindVer = re.compile("(?<=-)(?:\d+)(?:(?:\.\d+)*)" "(?:[a-z]?)(?:(?:_(?:pre|p|beta|alpha|rc)\d*)*)" "(?:-r\d+)?$") reEmptyLoad = re.compile("^\s*$|^\s*;|^\s*#") # Имя обрабатываемого шаблона nameTemplate = "" # Текст функции шаблона functText = "" # regular for discard sort number and version reData = re.compile(r"^(?:\d+-)?(.+?)(?:-(?:|always|\d+|\d(?:\d|\.|pre|_" "|-always|alpha|beta|pre|rc|[a-z][^a-z])*[a-z]?)(?:" "-r\d+)?)?$",re.S) currentBelong = "" alreadyInformed = [] def __init__(self, objVar): # Если не определен словарь функций шаблона # import services api if not self.templateFunction: # префикс функций шаблона pref = "func" # cписок [(название функции, функция), ...] dictFunc = filter(lambda x: x[0].startswith(pref) and\ hasattr(x[1],"__call__"), self.__class__.__dict__.items()) # удаляем у названия функции префикс и переводим остаток названия # в нижний регистр dictFunc = map(lambda x: (x[0][len(pref):].lower(), x[1]), dictFunc) # Формируем словарь функций шаблона self.templateFunction.update(dictFunc) # Формируем список функций шаблона for nameFunction in self.templateFunction.keys(): self.namesTemplateFunction.append(nameFunction) # Объект хранения переменных self.objVar = objVar self._reFunc = re.compile(("%s%s%s")\ %(self.varStart,self._reFunctionText,self.varEnd),re.M) # Аттрибуты для функции шаблона ini() # Первоначальный словарь переменных для ini() self.prevDictIni = {} # Текущий словарь переменных для ini() self.currDictIni = {} # Время модификации конфигурационного файла для ini() self.timeIni = -1 self.recalculateBaseDir() # Словарь времен модификации env файлов self.timeConfigsIni = {} # Словарь хранения переменых полученных функцией env() из env файлов self.valuesVarEnv = {} # Словарь хранения опций для функции info() self.optionsInfo = {} # файл параметров сервисов envFile = self.objVar.Get("cl_env_server_path") # объект конвертирования из старого remote env файла self.convObj = False if os.access(envFile, os.R_OK): self.convObj = False elif os.access("/var/calculate/remote/calculate.env", os.R_OK): from convertenv import convertEnv self.convObj = convertEnv() def recalculateBaseDir(self): """Recalculate basedir and homedir""" # Директория другой системы self._chrootDir = self.objVar.Get("cl_chroot_path") if self._chrootDir != '/': # Изменение директории к базе пакетов self.basePkgDir = pathJoin(self._chrootDir, self.basePkgDir) self.basePkgDir = os.path.normpath(self.basePkgDir) # Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д self._baseDir=pathJoin(self._chrootDir,self.objVar.Get("cl_root_path")) self._baseDir = os.path.normpath(self._baseDir) self.uid, self.gid, self.homeDir, self.groups =\ self.getDataUser(groupsInfo=True) # Домашняя директория, плюс базовая директория self.homeDir = pathJoin(self._baseDir, self.homeDir) # path to configuration file for ini() function # if action is desktop configuration, then path in user directory # else config file place in /etc/calculate if self.objVar.Get('cl_action') == "desktop": self.pathConfigIni = os.path.join(self.homeDir, ".calculate") self.modeConfigIni = 0640 else: self.pathConfigIni = pathJoin(self._chrootDir,'/etc/calculate') self.modeConfigIni = 0644 self.fileConfigIni = os.path.join(self.pathConfigIni,"ini.env") def equalTerm(self, term, localVars): """Метод для вычисления выражения""" terms = self.sNum.findall(term) if terms: strNumers = [] for n in terms: strNum = n.strip() if "*" in strNum or "/" in strNum: strNum = self.multAndDiv(strNum,localVars) try: num = int(strNum) except: minus = False if strNum[:1] == "-": minus = True strNum = strNum[1:] if localVars.has_key(strNum): try: num = int(localVars[strNum]) except: self.raiseErrTemplate( _("error: variable %s is not integer")% str(strNum)) elif self.objVar.exists(strNum): try: num = int(self.objVar.Get(strNum)) except: self.raiseErrTemplate( _("error: variable %s is not integer")% str(strNum)) else: self.raiseErrTemplate( _("error: local variable %s not defined")% str(strNum)) if minus: num = -num strNumers.append(num) return sum(strNumers) self.raiseErrTemplate(_("error: template term %s, incorrect data")\ %str(term)) def multAndDiv(self, term, localVars): """Метод для умножения и деления""" termTmp = term varsLocal = self.sMD.findall(term) for var in varsLocal: flagVarTxt = True try: int(var) except: flagVarTxt = False if flagVarTxt: continue varReplace = str(self.equalTerm(var, localVars)) termTmp = termTmp.replace(var,varReplace) ret = eval(termTmp) return ret def funcSum(self, funArgv, resS, localVars, textTemplateTmp,nameTemp): """Функция шаблона, вычисляет функцию sum()""" terms = funArgv.replace(" ","").split(",") # Название локальной переменной nameLocVar = terms[0] if not localVars.has_key(nameLocVar): localVars[nameLocVar] = 0 if len(terms) == 2: if terms[1].strip(): localVars[nameLocVar] = self.equalTerm(terms[1], localVars) replace = str(localVars[nameLocVar]) else: replace = "" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] elif len(terms) == 3: if terms[1].strip(): replaceInt = self.equalTerm(terms[1], localVars) replace = str(replaceInt) else: replace = "" localVars[nameLocVar] = self.equalTerm(terms[2], localVars) textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] else: self.raiseErrTemplate() return textTemplateTmp def funcExists(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона exists(), проверяет существование файла, если существует выдает '1' если второй параметр root, то проверка осуществляется от корня. """ terms = map(lambda x: x.strip(), funArgv.split(",")) if len(terms) > 2: self.raiseErrTemplate() fileName = terms[0] flagNotRootFS = True if len(terms) == 2: if terms[1] == "root": flagNotRootFS = False else: self.raiseErrTemplate( _("The second argument of the function is not 'root'")) if fileName[0] == "~": # Получаем директорию пользователя fileName = os.path.join(self.homeDir, fileName.partition("/")[2],"")[:-1] elif fileName[0] != "/": self.raiseErrTemplate(_("wrong path '%s'")%fileName) else: if flagNotRootFS: fileName = pathJoin(self._baseDir, fileName) replace = "" if os.path.exists(fileName): replace = "1" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcLoad(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона load(), если файл существует читает из файла локальную переменную если один параметр - выводит значение локальной переменной """ terms = funArgv.split(",") if terms: lenTerms = len(terms) if not terms[0].strip() or\ (lenTerms==2 and not terms[1].strip()) or\ (lenTerms==3 and not terms[2].strip()) or\ lenTerms>3: self.raiseErrTemplate() else: self.raiseErrTemplate() flagNotRootFS = True if lenTerms == 3: if terms[2] =="root": flagNotRootFS = False else: self.raiseErrTemplate( _("The third argument of the function is not 'root'")) if lenTerms >= 2: if not terms[0] in ["ver","num","char","key","empty"]: self.raiseErrTemplate( _("the first argument of the function is neither 'ver'" " or 'num' or 'char' or 'empty'")) if lenTerms == 1: fileName = terms[0].strip() else: fileName = terms[1].strip() # Если домашняя директория if fileName[0] == "~": # Получаем директорию пользователя fileName = os.path.join(self.homeDir, fileName.partition("/")[2],"")[:-1] elif fileName[0] != "/": self.raiseErrTemplate(_("wrong path '%s'")%fileName) else: if flagNotRootFS: fileName = pathJoin(self._baseDir,fileName) replace = "" if os.path.exists(fileName): replace = readFile(fileName).strip() if replace and lenTerms >= 2 and terms[0] == "empty": replace ="\n".join(filter(lambda x: not self.reEmptyLoad.search(x), replace.split("\n"))) if not replace and lenTerms >= 2 and terms[0] in ["ver","num"]: replace = "0" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def sharePkg(self,pkgs,force=False): """ Update packages from pkgs list """ for pkgname,category,ver,slot in pkgs: fullname = "%s/%s"%(category,pkgname) if not fullname in self.installProg or \ type(self.installProg[fullname]) != dict: self.installProg[fullname] = self.installProg[pkgname] = {} if force or not slot in self.installProg[fullname]: self.installProg[fullname][slot] = ver return self.installProg def getInstallPkgGentoo(self,category=""): pkgs = [] filterFunc = "SLOT".__eq__ def getFilesDir(pkgs, dirname, names): for nameFile in filter(filterFunc,names): absNameFile = os.path.join(dirname,nameFile) category,spl,pkgname = dirname.rpartition('/') dbpkg,spl,category = category.rpartition('/') slot = open(absNameFile,'r').read().strip() pkgname,spl,rev = pkgname.rpartition("-") if rev.startswith('r'): pkgname, spl, ver = pkgname.rpartition("-") ver = "%s-%s"%(ver,rev) else: ver = rev pkgs.append((pkgname,category,ver,slot)) return True os.path.walk(os.path.join(self.basePkgDir,category), getFilesDir, pkgs) return self.sharePkg(pkgs) def pkg(self,nameProg,slot=None): if len(self.installProg) > 0: if type(self.installProg.values()[0]) != dict: self.installProg.clear() self.getInstallPkgGentoo() if nameProg in self.installProg: versions = self.installProg[nameProg] if slot: return versions.get(slot,"") if len(versions) == 1: return versions.values()[0] else: return versions[max(versions.keys(),key=getTupleVersion)] else: return "" def funcPkg(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона pkg(), выдает номер версии программы""" # Название программы nameProg = funArgv.replace(" ","") if not nameProg: fileName = os.path.split(self.nameTemplate)[1] if fileName == '.calculate_directory': parentDir = os.path.dirname(self.nameTemplate) parentDir, pkgName = os.path.split(parentDir) else: parentDir, pkgName = os.path.split(self.nameTemplate) category = os.path.split(parentDir)[1] # reg for discard version and sort number pkgName = self.reData.search(pkgName).group(1) category = self.reData.search(category).group(1) nameProg = "%s/%s"%(category,pkgName) # Замена функции в тексте шаблона replace = "" if "/" in nameProg: category, spl, nameProg = nameProg.partition("/") nameProg, spl, slot = nameProg.partition(":") if not category in self.installCategory: self.getInstallPkgGentoo(category=category) self.installCategory.append(category) replace = self.pkg(nameProg, slot=slot or None) else: if not self.flagAllPkgScan: self.getInstallPkgGentoo() templateFunction.flagAllPkgScan = True nameProg,spl,slot = nameProg.partition(":") replace = self.pkg(nameProg, slot=slot) textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcRnd(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона rnd(), выдает строку случайных символов первый аргумент: 'num' - числа, 'pas' - цифры и буквы 'uuid' - цифры и строчные буквы a-f второй аргумент: количество символов """ terms = funArgv.replace(" ","").split(",") if not terms[0].strip() or\ (len(terms)==2 and not terms[1].strip()) or\ len(terms)!=2: self.raiseErrTemplate() fArgvNames = {'num':string.digits, 'pas':string.ascii_letters + string.digits, 'uuid':string.ascii_lowercase[:6] + string.digits} if not terms[0] in fArgvNames: self.raiseErrTemplate( _("the first argument of the function must " "be 'num', 'pas' or 'uuid'")) try: lenStr = int(terms[1]) except: self.raiseErrTemplate( _("the second argument of the function is not a number")) choiceStr = fArgvNames[terms[0]] replace = ''.join([random.choice(choiceStr) for i in xrange(lenStr)]) textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcCase(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона case(), выдает переменную в определенном регистре первый аргумент: 'upper' - верхний регистр, 'lower' - нижний регистр, 'capitalize' - первая буква в верхнем регистре второй аргумент: название переменной """ terms = funArgv.replace(" ","").split(",") if not terms[0].strip() or\ (len(terms)==2 and not terms[1].strip()) or len(terms)!=2: self.raiseErrTemplate() fArgvNames = ['upper','lower','capitalize'] if not terms[0] in fArgvNames: self.raiseErrTemplate(_("the first argument of the function" " is neither 'upper' or 'lower' or" " 'capitalize'")) try: strValue = self.objVar.Get(terms[1]) if not strValue: strValue = "" else: strValue = str(strValue) except: raise TemplatesError( _("error in template %s")%self.nameTemplate + "\n" + _("error: variable %s not found")%str(terms[1])) replace = "" strValue = _toUNICODE(strValue) if terms[0] == 'upper': replace = strValue.upper() elif terms[0] == 'lower': replace = strValue.lower() elif terms[0] == 'capitalize': replace = strValue.capitalize() if replace: replace = replace.encode("UTF-8") textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcIn(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Function in for check value in variable """ terms = funArgv.replace(" ", "").split(",") # Название локальной переменной nameLocVar = terms[0] flagFoundVar = False try: value = self.objVar.Get(nameLocVar) flagFoundVar = True except: pass if flagFoundVar: if type(value) in (list, tuple): terms = terms[1:] if any(x in terms for x in value): replace = "1" else: replace = "" else: if value in terms[1:]: replace = "1" else: replace = "" else: self.raiseErrTemplate(_("error: variable %s does not exist") \ % str(nameLocVar)) textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \ textTemplateTmp[resS.end():] return textTemplateTmp def funcPush(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """локальная функция записывает значение переменной в стек глобальных переменных """ terms = funArgv.replace(" ","").split(",") # Название локальной переменной nameLocVar = terms[0] flagFoundVar = False if nameLocVar in localVars.keys(): flagFoundVar = True value = localVars[nameLocVar] else: try: value = self.objVar.Get(nameLocVar) flagFoundVar = True except: pass if flagFoundVar: # Если переменная существует if len(terms) == 1: self.stackGlobalVars.append(str(value)) else: self.raiseErrTemplate(_("error: variable %s exists")\ %str(nameLocVar)) else: # Если переменная не существует if len(terms) == 1: self.raiseErrTemplate(_("error: variable %s does not exist")\ %str(nameLocVar)) elif len(terms) == 2: value = terms[1].strip() self.stackGlobalVars.append(str(value)) localVars[nameLocVar] = value else: self.raiseErrTemplate() replace = "" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcPop(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """локальная функция получает значение из стека глобальных переменных и присваивает локальной переменной """ terms = funArgv.replace(" ","").split(",") # Название локальной переменной nameLocVar = terms[0] if len(terms) == 1: if self.stackGlobalVars: localVars[nameLocVar] = self.stackGlobalVars.pop() else: self.raiseErrTemplate( _("error: global variables stack empty")) else: self.raiseErrTemplate() replace = "" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcPrint(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Вывод успешного сообщения """ self.printSUCCESS(_(funArgv)) textTemplateTmp = textTemplateTmp[:resS.start()] + \ textTemplateTmp[resS.end():] return textTemplateTmp def funcWarning(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Вывод сообщения с предупреждением """ self.printWARNING(_(funArgv)) textTemplateTmp = textTemplateTmp[:resS.start()] + \ textTemplateTmp[resS.end():] return textTemplateTmp def funcError(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Вывод сообщения с ошибкой """ self.printERROR(_(funArgv)) textTemplateTmp = textTemplateTmp[:resS.start()] + \ textTemplateTmp[resS.end():] return textTemplateTmp def funcExit(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Good exit """ # TODO: need remove return textTemplateTmp self.printSUCCESS(_(funArgv)) raise TemplatesInterrupt(_("Execution of templates was stopped"), TemplatesInterrupt.EXIT) def funcBreak(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Bad exit """ # TODO: need remove return textTemplateTmp self.printERROR(_(funArgv)) raise TemplatesInterrupt(_("Execution of templates was " "interrupted with an error"), TemplatesInterrupt.ABORT) def getElogTimestamp(self): # Получаем время модификации конфигурационного файла curTime = self.getTimeFile(self.fileConfigIni) nameLocVar = "update.timestamp" if self.timeIni != curTime: # читаем переменные из файла self.prevDictIni = self.loadVarsIni(self.fileConfigIni) self.currDictIni= {} self.currDictIni.update(self.prevDictIni) self.timeIni = self.getTimeFile(self.fileConfigIni) if nameLocVar in self.currDictIni.keys(): if self.currDictIni[nameLocVar] is None: return 0 else: val = self.currDictIni[nameLocVar].encode("UTF-8") if val.isdigit(): return int(val) return 0 elogFile = '/var/log/emerge.log' @classmethod def getLastElog(cls): # get last timestamp data,cr,lastStr = readFile(cls.elogFile,250).rstrip().rpartition('\n') return lastStr.partition(':')[0] def funcElog(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): """Function for work with emerge.log""" funArgv = funArgv.strip() rePkg = re.compile(r'completed emerge \(\d+ of \d+\) (\S+)\s',re.S) replace = "" if funArgv: lastTimestamp = self.getElogTimestamp() skip = True for line in reversed(list(readLinesFile(self.elogFile))): timestamp,op,info = line.partition(':') if timestamp.isdigit() and lastTimestamp and \ int(timestamp) < lastTimestamp: break match = rePkg.search(info) if match and match.group(1).startswith(funArgv): pkgInfo = reVerSplitToPV(match.group(1)) if "{CATEGORY}/{PN}".format(**pkgInfo) == funArgv: replace = pkgInfo['PVR'] break else: replace = self.getLastElog() textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \ textTemplateTmp[resS.end():] return textTemplateTmp def funcConfirm(self,funArgv, resS, localVars, textTemplateTmp, nameTemp): # TODO: need remove return textTemplateTmp res = self.askConfirm(_(funArgv)) textTemplateTmp = textTemplateTmp[:resS.start()] + res + \ textTemplateTmp[resS.end():] return textTemplateTmp def loadVarsIni(self, iniFileName): """ Читает файл fileName создает и заполняет переменные на основе этого файла Используеться совместно c funcIni """ localVarsIni = {} # получить объект ini файла config = iniParser(iniFileName) # получаем все секции из конфигурационного файла allsect = config.getAllSectionNames() if not allsect: if self.getError(): # Очистка ошибки _error.error = [] return localVarsIni # Заполняем переменные для funcIni for sect in allsect: sectVars = config.getAreaVars(sect) for name in sectVars.keys(): nameVar = "%s.%s"%(sect,name) valueVar = sectVars[name] localVarsIni[nameVar] = valueVar return localVarsIni def getTimeFile(self, fileName): # Получаем время модификации файла nameEnvFile = os.path.split(fileName)[1] if nameEnvFile in self.timeConfigsIni: return self.timeConfigsIni[nameEnvFile] return 0 def funcIni(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """локальная функция записывает и считывает значение переменной из ini файла ~./calculate/ini.env """ # Создаем директорию if not os.path.exists(self.pathConfigIni): os.makedirs(self.pathConfigIni) os.chown(self.pathConfigIni, self.uid, self.gid) termsRaw = funArgv.split(",") flagFirst = True terms = [] for term in termsRaw: if flagFirst: terms.append(term.replace(" ","")) flagFirst = False else: val = term.strip() # Флаг (не найдены кавычки) flagNotFoundQuote = True for el in ('"',"'"): if val.startswith(el) and val.endswith(el): terms.append(val[1:-1]) flagNotFoundQuote = False break if flagNotFoundQuote: if not val: terms.append(None) else: terms.append(val) # Название локальной переменной nameLocVar = terms[0] namesVar = nameLocVar.split(".") if len(namesVar) == 1: nameLocVar = "main.%s"%nameLocVar elif len(namesVar)>2: self.raiseErrTemplate() replace = "" # Получаем время модификации конфигурационного файла curTime = self.getTimeFile(self.fileConfigIni) if len(terms) == 1: if self.timeIni != curTime: # читаем переменные из файла self.prevDictIni = self.loadVarsIni(self.fileConfigIni) self.currDictIni= {} self.currDictIni.update(self.prevDictIni) self.timeIni = self.getTimeFile(self.fileConfigIni) if nameLocVar in self.currDictIni.keys(): if self.currDictIni[nameLocVar] is None: replace = "" else: replace = self.currDictIni[nameLocVar].encode("UTF-8") elif len(terms) == 2: if self.timeIni != curTime: # читаем переменные из файла self.prevDictIni = self.loadVarsIni(self.fileConfigIni) self.currDictIni= {} self.currDictIni.update(self.prevDictIni) self.timeIni = self.getTimeFile(self.fileConfigIni) # Значение локальной переменной valueLocVar = terms[1] self.currDictIni[nameLocVar] = valueLocVar elif len(terms) == 3: if not terms[2] in ['url','purl','unicode']: self.raiseErrTemplate( _("the third argument of the function is neither " "'url' or 'purl' or 'unicode'")) if terms[1]: self.raiseErrTemplate() if self.timeIni != curTime: # читаем переменные из файла self.prevDictIni = self.loadVarsIni(self.fileConfigIni) self.currDictIni= {} self.currDictIni.update(self.prevDictIni) self.timeIni = self.getTimeFile(self.fileConfigIni) if nameLocVar in self.currDictIni.keys(): unicodeValue = self.currDictIni[nameLocVar] if unicodeValue is None: unicodeValue = "" if terms[2] in ('url', 'purl'): replace = unicodeValue.encode("UTF-8").\ __repr__()[1:-1].replace('\\x','%').\ replace(' ','%20') if terms[2] == 'purl': replace = replace.replace('/','%2f') elif terms[2] == 'unicode': replace = unicodeValue.__repr__()[2:-1] else: self.raiseErrTemplate() textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return (textTemplateTmp) def funcReplace(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """локальная функция заменяет в значении переменной old на new replace(old, new, name_var_template) одинарные и двойные кавычки должны быть обязательно использованы в первых двух аргументах old и new "test\ntest" - преобразование строки (строка с переводом) 'test\ntest' - без преобразования (одна строка) """ def getStrArgv(terms): """Определяет в двойных или одинарных кавычках параметры Результат [(тип, аргумент),...] [("double", arg1).] """ listArgv = [] for term in terms: if term.startswith('"') and term.endswith('"'): replTerms = [(r"\'", "'"), (r'\"', '"'), (r'\n', '\n'), (r'\r', '\r'), (r'\t', '\t'), (r"\\", "\\")] textArgv = term[1:-1] for replTerm in replTerms: textArgv = textArgv.replace(*replTerm) listArgv.append(textArgv) elif term.startswith("'") and term.endswith("'"): listArgv.append(term[1:-1]) else: self.raiseErrTemplate() return listArgv terms = map(lambda x: x.strip(), funArgv.split(",")) if len(terms) != 3: self.raiseErrTemplate() listArgv = getStrArgv(terms[:2]) old = listArgv[0] new = listArgv[1] nameVar = terms[2] # Получаем значение переменной if nameVar in localVars: value = str(localVars[nameVar]) else: try: value = str(self.objVar.Get(nameVar)) except: self.raiseErrTemplate(_("template variable '%s' not found")\ %str(nameVar)) replace = value.replace(old,new) textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcEnv(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона env(), выдает значение переменной из env файлов """ terms = funArgv.replace(" ","").split(",") if len(terms) != 1: self.raiseErrTemplate() nameVar = terms[0] replace = '' if nameVar in self.valuesVarEnv: replace = self.valuesVarEnv[nameVar] else: # Получаем значение из env файлов value = self.objVar.getIniVar(nameVar) if value is False: self.raiseErrTemplate(self.getError()) self.valuesVarEnv[nameVar] = value replace = value textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcServer(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона info(), выдает значение опций сервиса из /var/calculate/remote/calculate.env """ terms = funArgv.replace(" ","").split(",") if len(terms)==0 or len(terms)>2: self.raiseErrTemplate() nameLocalVar = "" if len(terms)==2: if not terms[1]: self.raiseErrTemplate() nameLocalVar = terms[1] textLine = terms[0] vals = textLine.split(".") if len(vals)!= 2: self.raiseErrTemplate() if filter(lambda x: not x.strip(), vals): self.raiseErrTemplate() service, option = vals if not service or not option: self.raiseErrTemplate() if not self.optionsInfo: # файл /var/calculate/remote/server.env envFile = self.objVar.Get("cl_env_server_path") # получаем словарь всех информационных переменных if self.convObj: optInfo = self.convObj.convert() else: optInfo = self.objVar.getRemoteInfo(envFile) if optInfo is False: self.raiseErrTemplate() if optInfo: self.optionsInfo = optInfo replace = '' if service in self.optionsInfo and option in self.optionsInfo[service]: value = self.optionsInfo[service][option] if nameLocalVar: localVars[nameLocalVar] = value else: replace = value elif nameLocalVar: localVars[nameLocalVar] = "" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcGroups(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона groups(), проверяет нахождение пользователя в группах, если находится выдает '1' """ terms = map(lambda x: x.strip(), funArgv.split(",")) groupNames = set(terms) userGroups = set(self.groups) replace = "" if groupNames & userGroups: replace = "1" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcBelong(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): self.printWARNING(_("Function '{funcname}' used by {template} " "is deprecated and will be removed in the future" ).format(funcname="belong",template=nameTemp)) replace = "" return textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] def checkCorrectPkgName(self,package): """ Check pkg name (in /var/db/pkg,/usr/portage,/var/lib/layman/calculate and other overlays) """ category,spl,pkgname = package.partition('/') pkgname, spl, slot = pkgname.partition(":") # check installed package in system if not category in self.installCategory: self.getInstallPkgGentoo(category=category) self.installCategory.append(category) if self.pkg("%s/%s"%(category,pkgname), slot=slot or None): return True # check package in portage and calculate,overlay def queuePortdir(): yield "/usr/portage" calculate = "/var/lib/layman/calculate" yield calculate for portdir in filter(lambda x:x!=calculate, self.objVar.Get('cl_portdir_overlay')): yield portdir for portdir in queuePortdir(): if not portdir in self.cachePortdir: lPortdir = len(portdir)+1 self.cachePortdir[portdir] = list(set( map(lambda x:x[lPortdir:].rpartition('/')[0], glob.glob("%s/*/*/*.ebuild"%portdir)))) if package in self.cachePortdir[portdir]: return True else: if portdir == "/usr/portage" and \ len(self.cachePortdir[portdir]) < 30: #raise TemplatesError(_("Ebuild files are not found in portage")) return True if not "portage/update" in self.cachePortdir: updateFiles = sorted(listDirectory('/usr/portage/profiles/updates'), key=lambda x:tuple(reversed(x.split('Q-'))),reverse=True) if updateFiles: self.cachePortdir["portage/update"] = \ map(lambda x:x.split(' ')[1], filter(lambda x:x.startswith('move '), readLinesFile(os.path.join('/usr/portage/profiles/updates', updateFiles[0])))) if "portage/update" in self.cachePortdir and \ package in self.cachePortdir["portage/update"]: self.printWARNING(_("Package {frompkg} was renamed").format(frompkg=package)) return True return False def funcMerge(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """ Belong function use value in first arg and compare it for all values in cl_merge_pkg. If cl_merge_pkg empty or first arg <=cl_belogn_pkg then "1" else "" """ terms = funArgv.replace(" ","").split(",") if len(terms) != 1: self.raiseErrTemplate() funcPkg = terms[0] if not funcPkg: fileName = os.path.split(self.nameTemplate)[1] if fileName == '.calculate_directory': parentDir = os.path.dirname(self.nameTemplate) parentDir, pkgName = os.path.split(parentDir) else: parentDir, pkgName = os.path.split(self.nameTemplate) category = os.path.split(parentDir)[1] # reg for discard version and sort number pkgName = self.reData.search(pkgName).group(1) category = self.reData.search(category).group(1) funcPkg = "%s/%s"%(category,pkgName) try: if not self.checkCorrectPkgName(funcPkg): message = _("Package {pkgName} specified in the merge() into " "{templName} template is not found").format( pkgName=funcPkg,templName=self.nameTemplate) hashMessage = hashlib.md5(message).digest() if not hashMessage in self.alreadyInformed: self.printWARNING(message) self.alreadyInformed.append(hashMessage) return textTemplateTmp[:resS.start()] + \ textTemplateTmp[resS.end():] except TemplatesError as e: self.printWARNING(str(e)) self.currentBelong = funcPkg if self.objVar.Get('cl_action') == 'patch': if funcPkg == "%s/%s"%(self.objVar.Get('core.cl_core_pkg_category'), self.objVar.Get('core.cl_core_pkg_name')): replace = self.objVar.Get('core.cl_core_pkg_version') else: replace = "" else: pkg = self.objVar.Get("cl_merge_pkg") replace = "" if pkg: if funcPkg in pkg: replace = "1" else: replace = "1" textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcList(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона list(). Если первый аргумент является именем локальной или глобальной переменной и значение переменной является списком, выдает элемент списка по второму аргументу индексу. Первый элемент имеет индекс 0 """ terms = funArgv.replace(" ","").split(",") # У функции должно быть два аргумента if len(terms) != 2: self.raiseErrTemplate() # Название локальной или глобальной переменной nameLocVar = terms[0] strIndex = terms[1] try: intIndex = int(strIndex) except: self.raiseErrTemplate(_("'%s' is not a number")%strIndex) flagFoundVar = False if nameLocVar in localVars.keys(): flagFoundVar = True value = localVars[nameLocVar] else: try: value = self.objVar.Get(nameLocVar) flagFoundVar = True except: pass if not flagFoundVar: # Если переменная не существует self.raiseErrTemplate(_("error: variable %s does not exist")\ %str(nameLocVar)) if not type(value) in (list,tuple): # Значение переменной не список или кортеж self.raiseErrTemplate(_("value of %s is not a list or a tuple")\ %str(nameLocVar)) try: if len(value) > intIndex: replace = str(value[intIndex]) else: replace = "" except: self.raiseErrTemplate(_("wrong %s")%strIndex) textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcDisk(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона disk(). Первый аргумент ищется в значении переменной os_disk_install (значение os_install_disk_mount - список точек монтирования при установке) второй аргумент используется для поиска в переменной os_disk_второй_аргумент (значение os_disk_второй_аргумент - список) В os_install_disk_mount ищется первый аргумент, находим его индекс результат - элемент cписка из os_disk_второй_аргумент с этим индексом """ terms = funArgv.replace(" ","").split(",") # У функции должно быть два аргумента if len(terms) != 2: self.raiseErrTemplate() # Название глобальной переменной mountPoint = terms[0] lastElementVar = terms[1] if not mountPoint or mountPoint[:1] !="/": self.raiseErrTemplate(_("wrong %s")%lastElementVar) nameVar = "install.os_install_disk_mount" try: valueVar = self.objVar.Get(nameVar) except: # Если переменная не существует self.raiseErrTemplate( _("error: variable %s does not exist")%nameVar) nameElementVar = "install.os_install_disk_%s"%lastElementVar try: valueElementVar = self.objVar.Get(nameElementVar) except: # Если переменная не существует nameElementVar = "install.os_disk_%s"%lastElementVar try: valueElementVar = self.objVar.Get(nameElementVar) except: self.raiseErrTemplate(_("wrong %s")%lastElementVar+"\n"+ _("error: variable %s does not exist")\ %nameElementVar) if not type(valueVar) in (list,tuple): # Значение переменной не список или кортеж self.raiseErrTemplate(_("value of %s is not a list or a tuple")\ %nameVar) if not type(valueElementVar) in (list,tuple): # Значение переменной не список или кортеж self.raiseErrTemplate(_("value of %s is not a list or a tuple")\ %nameElementVar) if len(valueVar) != len(valueElementVar): self.raiseErrTemplate( _("%(name)s does not match %(nameElement)s in size")\ %{'name':nameVar, 'nameElement':nameElementVar}) index = None for num, mPoint in enumerate(valueVar): if mountPoint == mPoint: index = num break if index is None: for num, mPoint in enumerate(valueVar): if "/" == mPoint: index = num break if index is None: self.raiseErrTemplate(_("mount point '/' or '/%s' not found " " in the value of variable os_disk_install") %mountPoint) replace = valueElementVar[index] textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\ textTemplateTmp[resS.end():] return textTemplateTmp def funcModule(self, funArgv, resS, localVars, textTemplateTmp, nameTemp): """Функция шаблона module(), выдает значение аттрибута api. аргумент: путь_к_атрибуту - путь к аттрибуту возможные пути: имя_пакета.var.имя_переменной - получаем значение переменной имя_пакета.имя_метода_api - выполнение метода, получение результата all.имя_метода_api - выполнение метода для всех пакетов с api """ return textTemplateTmp def raiseErrTemplate(self,message=""): """Возвращает ошибки при обработке функций шаблона""" if message: message = "%s\n"%message else: message = "" raise TemplatesError( _("error in template %s")%self.nameTemplate + "\n" + \ _("error, template term '%s'")%str(self.functText) + \ message) def applyFuncTemplate(self, textTemplate, nameTemplate): """Применяет функции к тексту шаблона""" # Локальные переменные localVars = {} # Имя обрабатываемого шаблона self.nameTemplate = nameTemplate # Регулярное выражение для поиска функции в шаблоне reFunc = self._reFunc resS = reFunc.search(textTemplate) textTemplateTmp = textTemplate flagIniFunc = False writeIniFunc = False while resS: mark = textTemplateTmp[resS.start():resS.end()] self.functText = mark[self._deltVarStart:-self._deltVarEnd] funcName, spl, funcEnd = self.functText.partition("(") if funcName in self.namesTemplateFunction: # аргументы функции - '(' аргументы ')' funArgv = funcEnd.rpartition(")")[0] # вызов функции шаблона textTemplateTmp = self.templateFunction[funcName](self, funArgv, resS, localVars, textTemplateTmp, nameTemplate) resS = reFunc.search(textTemplateTmp) if funcName == "ini": if "," in funArgv: writeIniFunc = True flagIniFunc = True else: self.raiseErrTemplate( _("function of templates '%s' not found")\ %str(self.functText)) if flagIniFunc: # Очистка файла в случае его ошибочного чтения if not self.prevDictIni and os.path.exists(self.fileConfigIni): FD = open(self.fileConfigIni, "r+") FD.truncate(0) FD.seek(0) FD.close() # Если конф. файл модифицирован шаблоном curTime = self.getTimeFile(self.fileConfigIni) if curTime != self.timeIni: # Считаем переменные из конф. файла self.prevDictIni = self.loadVarsIni(self.fileConfigIni) self.currDictIni.update(self.prevDictIni) self.timeIni = curTime # Если словари переменных не совпадают if self.prevDictIni != self.currDictIni: # Запишем переменные в конфигурационный файл # Создание объекта парсера config = iniParser(self.fileConfigIni) # set specified mode for ini file config.setMode(self.modeConfigIni) # секции будущего конфигурационного файла sects = list(set(map(lambda x: x.split(".")[0],\ self.currDictIni.keys()))) # запись переменных в файл for sect in sects: dictVar = {} for varName in self.currDictIni.keys(): if varName.startswith("%s."%sect): nameVar = varName.rpartition(".")[2] valueVar = self.currDictIni[varName] if valueVar is None: config.delVar(sect,nameVar) else: dictVar[nameVar] = valueVar if dictVar: # Запись переменных в секцию config.setVar(sect, dictVar) # читаем переменные из файла self.prevDictIni = self.loadVarsIni(self.fileConfigIni) self.currDictIni.update(self.prevDictIni) self.timeConfigsIni[self.fileConfigIni] = float(time.time()) self.timeIni = self.getTimeFile(self.fileConfigIni) # Меняем владельца в случае необходимости if writeIniFunc and os.path.exists(self.fileConfigIni): uid, gid = getModeFile(self.fileConfigIni, "owner") if not self.uid in (uid,PORTAGEUID) or \ not self.gid in (gid,PORTAGEGID): try: os.chown(self.fileConfigIni, self.uid, self.gid) except OSError as e: self.setError(_("error") + " " +\ "'chown %d:%d %s'"%(self.uid, self.gid, self.fileConfigIni)) return textTemplateTmp class ChangedFiles: """ Object which contains modified files and package """ FILE_MODIFIED,FILE_REMOVED,DIR_CREATED,DIR_REMOVED,DIR_EXISTS = 0,1,2,3,4 def __init__(self): self.data = {} self.pkgs = set() def _createEntry(self,fn): if not fn in self.data: self.data[fn] = [] def addObj(self,filename,action,pkg): self._createEntry(filename) self.data[filename].append((pkg,action)) self.pkgs.add(pkg) def getPkgs(self): return self.pkgs def getPkgFiles(self,pkg): return map(lambda x:(x[0],x[1][0][1]), filter(lambda x:x[1], map(lambda x:(x[0],filter(lambda x:x[0] == pkg,x[1])),self.data.items()))) # modes work with configuration file # T_ORIGIN - work with original config file # T_CFG - work with last ._cfg file # T_NEWCFG - new content has difference with (create new ._cfg file) T_ORIGIN, T_CFG, T_NEWCFG = 0,1,2 class Template(_file,_terms,_warning,xmlShare,templateFormat,_shareTemplate): """Класс для работы с шаблонами На вход 2 параметра: объект хранения переменных, имя сервиса - не обязательный параметр """ # Название файла шаблона директории templDirNameFile = ".calculate_directory" titleEnd = "For modify this file, create %(conf_path)s.clt template." protectPaths = [] allContents = {} if "CONFIG_PROTECT" in os.environ: protectPaths = ["/etc"] + filter(lambda x: x.strip(), os.environ["CONFIG_PROTECT"].split(" ")) protectPaths = map(lambda x: os.path.normpath(x), protectPaths) def hasError(self): return self.getError() or self.bHasError def __init__(self, objVar, servDir=False, dirsFilter=[], filesFilter=[], cltObj=True, cltFilter=True, printWarning=True, printSUCCESS=lambda x:x,printWARNING=lambda x:x, printERROR=lambda x:x,askConfirm=lambda x:x, userProfile=False,dispatchConf=None): # совместимость с вызовами из модулей предыдущих версий self.translator = RegexpLocalization("cl_templates3") if userProfile and objVar: objVar.Set('cl_protect_use_set','off',force=True) self.protectPaths = objVar.Get('cl_config_protect') self.dispatchConf = dispatchConf self.changedFiles = ChangedFiles() self.printSUCCESS = printSUCCESS self.printERROR = printERROR self.postmergePkgs = [] self.postmergeFile = "/var/lib/calculate/-postmerge" self.bHasError = False if printERROR: def newSetError(s): self.printERROR(s) self.bHasError = True self.setError = newSetError self.printWARNING = printWARNING self.askConfirm = askConfirm self.stop = 0 self.cltObj = None self.functObj = None # Предупреждения #self.warning = [] # Печатать ли предупреждения о корневых шаблонах без cl_name==pkg self.printWarning = printWarning # Необрабатываемые директории self.dirsFilter = dirsFilter # Необрабатываемые файлы self.filesFilter = filesFilter _file.__init__(self) # Словарь для создания объектов новых классов по образцу self.newObjProt = {'proftpd':'apache'} # Заголовок title self.__titleHead = "--------------------------------------\ ----------------------------------------" self._titleBody = "" self._titleList = ("Modified", "Processed template files" + ":") self._reVar = re.compile(("%s(?:[a-z0-9_]+\.)?[a-zA-Z0-9_-]+%s")%(self.varStart, self.varEnd),re.M) # Условия self._reTermBloc = re.compile( "#\?(?P(?:[a-z0-9_]+\.)?[a-zA-Z0-9\-_]+)\ (?P\((%s+|)\))?\ (?P[\>\<\=\!\&\|]+\ [\>\<\=\!\|\&\(\)%s]*)#\ \n*(?P.+?)\n*#(?P=rTerm)#(?P[ ,\t]*\n?)"\ %(self._reFunctionArgvText, self._reFunctionArgvInSquareBrackets), re.M|re.S) # Объект с переменными self.objVar = objVar self.recalculateBaseDir() # Последняя часть директории шаблона (имя сервиса: 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] # Созданные директории self.createdDirs = [] # Примененные файлы self.filesApply = [] # номер обрабатываемого файла self.numberProcessTempl = 0 # имя текущей программы _nameProgram = self.objVar.Get("cl_name").capitalize() # версия текущей программы _versionProgram = self.objVar.Get("cl_ver") # имя и версия текущей программы self.programVersion = "%s %s"%(_nameProgram, _versionProgram) # Словарь директорий с количеством файлов шаблонов self.dictTemplates = {} # Общее количество шаблонов self.allTemplates = 0 # Объект функций шаблона self.functObj = templateFunction(self.objVar) self.functObj.printSUCCESS = self.printSUCCESS self.functObj.printWARNING = self.printWARNING self.functObj.printERROR = self.printERROR if self.printERROR: self.functObj.setError = self.printERROR self.functObj.askConfirm = self.askConfirm # Метод применения функций к шаблонам self.applyFuncTemplate = self.functObj.applyFuncTemplate # Объект для определения типа файла шаблона self.typeFileObj = typeFile() # Глобальный словарь обработанных шаблонов файлов # {путь к конф. файлу:[имена шаблонов] ...} self.dictProcessedTemplates = {} if cltObj is True: # Объект templateClt self.cltObj = templateClt(self.objVar,self.postmergePkgs, printSUCCESS=self.printSUCCESS, printERROR=self.printERROR, printWARNING=self.printWARNING, askConfirm=self.askConfirm) elif cltObj: # Объект templateClt self.cltObj = cltObj else: # Объект templateClt self.cltObj = False # Фильтровать ли шаблоны clt по конфигурационным файлам обычных шаблонов self.cltFilter = cltFilter # autoupdate файлы self.autoUpdateFiles = [] self.autoUpdateDirs = [] self.protectedFiles = self.objVar.Get('main.cl_env_path') + \ [self.functObj.fileConfigIni] # список выполненных файлов self.executedFiles = [] def recalculateBaseDir(self): """Recalculate basedir and homedir""" # Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д self._baseDir = pathJoin(self.objVar.Get("cl_chroot_path"), self.objVar.Get("cl_root_path")) self._baseDir = os.path.normpath(self._baseDir) self.uid, self.gid, self.homeDir = self.getDataUser() # Домашняя директория, плюс базовая директория self.homeDir = pathJoin(self._baseDir, self.homeDir) if self.cltObj: self.cltObj.recalculateBaseDir() if self.functObj: self.functObj.recalculateBaseDir() def _addFile(self,filesApl,pkg=None): """ Add files to ChangedFiles """ for fn in filesApl: if os.path.exists(fn): self.changedFiles.addObj(fn,ChangedFiles.FILE_MODIFIED, pkg or self.functObj.currentBelong) else: self.changedFiles.addObj(fn,ChangedFiles.FILE_REMOVED, pkg or self.functObj.currentBelong) def executeTemplate(self, code, execPath): """Execute template""" p = process(execPath,lang=self.objVar.Get('os_locale_locale'), envdict=dict(os.environ)) if "/bin/bash" in code.partition('\n')[0]: p.write("""function translate() { gettext -d cl_template "$*" } """) p.write(code) p.pipe.stdin.close() for line in p.readByLine(): if line: line = self.translator.translate(line) self.printSUCCESS(line.strip()) p.pipe.wait() if p.success(): self.executedFiles.append((code,execPath)) if p.readerr(): for line in p.readerr().split('\n'): if line: line = self.translator.translate(line) self.printWARNING(line.strip()) return True else: if p.readerr(): for line in p.readerr().split('\n'): if line: line = self.translator.translate(line) self.printERROR(line.strip()) return False def __octToInt(self, strOct): """Преобразование восьмеричного в целое (ввод строка, вывод число)""" if strOct: try: res = string.atoi(strOct, 8) except ValueError: self.setError(_("Invalid oct value: ") + str(strOct)) return False return res else: self.setError(_("Empty oct value")) return False def getTemplateType(self): """выдать тип шаблона (text, bin)""" return self.getFileType(self.nameFileTemplate) def getFileType(self, fileName): """выдать тип файла (text, bin)""" isBin = self.typeFileObj.isBinary(fileName) typeTemplate = "bin" if isBin is True: typeTemplate = "bin" elif isBin is False: typeTemplate = "text" else: self.setError(_("ERROR") + ": getFileType()") self.setError(isBin) return False return typeTemplate def createDir(self, dirName, mode=False, uid=False, gid=False): """Создает директорию""" if os.access(dirName, os.F_OK): return True else: dMode = False prevDir, tmpSubdir = os.path.split(dirName) createDirs = [] while not os.access(prevDir, os.F_OK) and prevDir: createDirs.append(prevDir) prevDir = os.path.split(prevDir)[0] try: dUid,dGid = getModeFile(prevDir,"owner") except OSError: self.setError(_("No access to the directory: ") + prevDir) return False if not mode is False: dMode = mode if not uid is False: dUid = uid if not gid is False: dGid = gid createDirs.reverse() for nameDir in createDirs: try: if dMode: os.mkdir(nameDir, dMode) os.chmod(nameDir, dMode) else: os.mkdir(nameDir) os.chown(nameDir, dUid, dGid) except: self.setError(_("Failed to create the directory: ") + nameDir) return False try: if dMode: os.mkdir(dirName, dMode) os.chmod(dirName, dMode) else: os.mkdir(dirName) os.chown(dirName, dUid, dGid) createDirs.append(dirName) except: self.setError(_("Failed to create the directory: ") + dirName) return False return createDirs def applyVarsTemplate(self, textTemplate, nameTemplate): """ Заменяет переменные на их значения """ resS = self._reVar.search(textTemplate) textTemplateTmp = textTemplate while resS: mark = textTemplateTmp[resS.start():resS.end()] varName = mark[self._deltVarStart:-self._deltVarEnd] varValue = "" try: varValue = self.objVar.Get(varName) if not varValue: varValue = "" else: varValue = str(varValue) except DataVarsError, e: raise TemplatesError(_("error in template %s")%nameTemplate + "\n" + str(e)) textTemplateTmp = textTemplateTmp.replace(mark, varValue) resS = self._reVar.search(textTemplateTmp) return textTemplateTmp def applyTermsTemplate(self,textTemplate,nameTemplate): """ Применяет условия, к условным блокам текста """ def function(text): """Функция обработки функций в заголовке""" return self.applyFuncTemplate(text, nameTemplate) textTerm = "" resS = self._reTermBloc.search(textTemplate) textTemplateTmp = textTemplate while resS: mark = resS.group(0) body = resS.group("body") end = resS.group("end") parent = resS.group("func") if not parent: parent = "" term = resS.group("rTerm") + parent +\ resS.group("lTerm") if self._equalTerm(term, _("invalid template content: ")+\ nameTemplate, function): textTemplateTmp = textTemplateTmp.replace(mark, body+end) else: textTemplateTmp = textTemplateTmp.replace(mark, "") resS = self._reTermBloc.search(textTemplateTmp) return textTemplateTmp def getNeedTemplate(self, fileTemplate): """Применяем правила к названию файла""" dirP,fileP = os.path.split(fileTemplate) if fileP: spFile = fileP.split("?") realFileName = spFile[0] if len(spFile)>1: flagTrue = False for term in spFile[1:]: if self._equalTerm(term, _("invalid template name: ")+\ fileTemplate): flagTrue = True break if flagTrue: return True else: return False else: return True else: self.setError(_("invalid template name: ")+ str(fileTemplate)) return False def getTitle(self, comment, commentList, configPath=""): """Выдает заголовок шаблона ( версия и.т.д)""" if configPath and self.protectPaths: flagFoundPath = False for protectPath in self.protectPaths: if self._baseDir != "/": lenBaseDir = len(self._baseDir) if len(configPath)>lenBaseDir and\ configPath[:lenBaseDir] == self._baseDir: configPath = configPath[lenBaseDir:] if configPath.startswith(protectPath + "/"): flagFoundPath = True break if flagFoundPath: origConfigPath = PkgContents.reCfg.sub("/",configPath) commentList = commentList +\ [self.titleEnd%{'conf_path':origConfigPath}] if comment: commentFirst = comment commentInsert = comment commentLast = comment flagList = False # В случае открывающего и закрывающего комментария if type(comment) == types.TupleType and len(comment) == 2: commentFirst = comment[0] commentInsert = "" commentLast = comment[1] flagList = True if flagList: self._titleBody = commentFirst + "\n" else: self._titleBody = commentFirst + self.__titleHead + "\n" z = 0 flagFirst = True for com in list(self._titleList) + [""]*(len(commentList)): if com: if flagFirst: self._titleBody += commentInsert + " " + com + " "+\ self.programVersion + "\n" flagFirst = False else: self._titleBody += commentInsert + " " + com + "\n" else: self._titleBody += commentInsert + " " +\ commentList[z] + "\n" z += 1 if flagList: self._titleBody += commentLast +"\n" else: self._titleBody += commentLast + self.__titleHead + "\n" return self._titleBody else: return "" def changeMergePackage(self,package): return True def numberAllTemplates(self, number): """Количество шаблонов Вызов происходит перед наложением шаблонов в момент вызова в number находится количество обрабатываемых файлов Наследуемая функция Используется для отображения прогресса при наложениии шаблонов """ return True def numberProcessTemplates(self, number): """Номер текущего обрабатываемого шаблона Вызов происходит при наложении шаблона в момент вызова в number находится номер обрабатываемого шаблона Наследуемая функция Используется для отображения прогресса при наложениии шаблонов """ return True def templateModify(self): """ Files which created by apping templates """ return True def fixNameFileConfig(self,origfilename): """Support ._cfg0000 files for postinst""" #if self.objVar.Get('cl_ebuild_phase') != 'postinst': # return origfilename directory,filename = os.path.split(origfilename) i = 0 for i in range(0,9999): if not os.path.exists(os.path.join(directory, "._cfg%04d_%s"%(i,filename))): if i: filename = os.path.join(directory, "._cfg%04d_%s"%(i-1,filename)) if not os.path.exists(origfilename): return origfilename #origstat = os.stat(origfilename)[stat.ST_CTIME] #newstat = os.stat(filename)[stat.ST_CTIME] self.configMode = T_CFG return filename return origfilename return origfilename def getHeaderText(self, text): textLines = text.splitlines() paramLine = "" if textLines: textLine = textLines[0] rePar = re.compile(\ "\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$",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[reP.end():reLs.end()] paramLine = paramLine.replace("\\"," ") else: reLn = re.compile("\n") reL = reLn.search(text) paramLine = textLine[reP.end():] return paramLine def getTemplateDirs(self, dirsTemplates): """Check template variable cl_name in first directories and files""" skipDirs = [] skipTemplates = [] debug = False for dirsTemplate in dirsTemplates: filesAndDirs = map(lambda x: os.path.join(dirsTemplate,x), listDirectory(dirsTemplate)) for dirFile in filesAndDirs: if os.path.isdir(dirFile): flagDir = True templatePath = os.path.join(dirFile,self.templDirNameFile) if not os.path.isfile(templatePath): skipDirs.append(dirFile) continue else: flagDir = False templatePath = dirFile if os.path.isfile(templatePath): if self.getFileType(templatePath) == "bin": skipTemplates.append(dirFile) else: textTemplate = open(templatePath).read() if textTemplate: headerLine = self.getHeaderText(textTemplate) if headerLine: if not debug and \ not "cl_name==" in headerLine and \ not "ac_" in headerLine: if flagDir: skipDirs.append(dirFile) else: skipTemplates.append(dirFile) else: if flagDir: skipDirs.append(dirFile) else: skipTemplates.append(dirFile) else: skipTemplates.append(dirFile) if skipDirs or skipTemplates: # print warning from cl_print import color_print self.printWARNING(_("No conditions for checking the value of " "an action variable")) skipDirTemplates = [] for skipDir in skipDirs: skipTempl = os.path.join(skipDir,self.templDirNameFile) if os.path.isfile(skipTempl): skipDirTemplates.append(skipTempl) if skipTemplates or skipDirTemplates: self.printWARNING(_("Skipped templates:")) for skipTemplate in skipTemplates + skipDirTemplates: self.printWARNING(" "*6 + skipTemplate) if skipDirs: self.printWARNING(_("Skipped directories:")) for skipDir in skipDirs: self.printWARNING(" "*6 + skipDir) self.printWARNING("") self.printWARNING(_("Headers of directory templates and headers " "of files on the first level should include " "an action variable")) self.printWARNING(_("Example:")) self.printWARNING("# Calculate ac_install_merge==on") return skipDirs + skipTemplates def applyTemplates(self,progress=True,rerun=True): """Применяет шаблоны к конфигурационным файлам""" def createDictTemplates(path, prefix, dictTemplates): """Создает словарь {"директория":"кол-во шаблонов" ...} и считает общее количество шаблонов """ # Количество шаблонов self.allTemplates += 1 dirTemplate = os.path.split(path)[0] while(True): if dirTemplate in dictTemplates.keys(): dictTemplates[dirTemplate] += 1 else: dictTemplates[dirTemplate] = 1 if dirTemplate == prefix: break dirTemplate = os.path.split(dirTemplate)[0] return dictTemplates self.clearErrors() self.clearWarnings() if not self.objVar.defined("cl_template_path_use"): self.setError(_("undefined variable: ") + "cl_template_path_use") return False dirsTemplates = self.objVar.Get("cl_template_path_use") # Созданные директории self.createdDirs = [] # Примененные файлы self.filesApply = [] # Номер применяемого шаблона self.numberProcessTempl = 0 # Словарь директорий с количеством файлов шаблонов self.dictTemplates = {} # Количество шаблонов self.allTemplates = 0 # Установка по умолчанию аттрибутов для функциии шаблонов ini() # Время доступа к конфигурационному файлу функции шаблона ini() self.functObj.timeIni = -1 # Первоначальный словарь переменных для ini() self.functObj.prevDictIni = {} # Текущий словарь переменных для ini() self.functObj.currDictIni = {} self.functObj.currentBelong = "" # Словарь времен модификации env файлов для ini() self.functObj.timeConfigsIni = {} if self._servDir: tmpDirsTemplates = [] for dirP in dirsTemplates: dirTempl = dirP + self._servDir if os.access(dirTempl, os.F_OK): # Если директория существует tmpDirsTemplates.append(dirTempl) dirsTemplates = tmpDirsTemplates scanObj = scanDirectory() scanObj.processingFile = lambda x,y: createDictTemplates(x, y,\ self.dictTemplates) # Считаем количество шаблонов dirsTemplatesExists = filter(lambda x: os.path.exists(x), dirsTemplates) if not dirsTemplatesExists and not self.cltObj: return (self.createdDirs, self.filesApply) # check cl_name in first template dirs and files skipTemplates = self.getTemplateDirs(dirsTemplatesExists) #if not os.environ.get("EBUILD_PHASE","") and progress: # for dirTemplate in dirsTemplatesExists: # scanObj.scanningDirectory(dirTemplate) # Считаем количество шаблонов clt if self.cltObj: # Считаем количество шаблонов clt self.cltObj.countsNumberTemplates() # не считать количество файлов в объекте self.cltObj self.cltObj.checkNumberTemplate = False # начальный номер clt шаблона self.cltObj.numberProcessTempl = self.allTemplates # метод показывающий номер clt шаблона self.cltObj.numberProcessTemplates = self.numberProcessTemplates # метод показывающий номер clt шаблона self.cltObj.templateModify = self.templateModify # общее количество шаблонов self.allTemplates += self.cltObj.allTemplates self.cltObj.allTemplates = self.allTemplates self.numberAllTemplates(self.allTemplates) # Обрабатываем шаблоны for dirTemplate in dirsTemplatesExists: if self.scanningTemplates(dirTemplate, skipTemplates=skipTemplates) is False: break if self.cltObj and not self.stop: # Созданные директории self.cltObj.createdDirs = self.createdDirs # Примененные файлы self.cltObj.filesApply = self.filesApply # Словарь директорий с количеством файлов шаблонов self.cltObj.dictTemplates = self.dictTemplates # Количество шаблонов self.cltObj.allTemplates = self.allTemplates # Установка по умолчанию аттрибутов для функциии шаблонов ini() # Время доступа к конфигурационному файлу функции шаблона ini() self.cltObj.functObj = self.functObj self.cltObj.protectedFiles = self.protectedFiles # Метод применения функций к шаблонам self.cltObj.applyFuncTemplate = self.functObj.applyFuncTemplate # Словарь примененных файлов шаблонов self.cltObj.dictProcessedTemplates = self.dictProcessedTemplates self.cltObj.changedFiles = self.changedFiles self.cltObj.autoUpdateFiles = self.autoUpdateFiles self.cltObj.autoUpdateDirs = self.autoUpdateDirs if self.cltFilter: # Шаблоны + .clt которые будут применены self.cltObj.filterApplyTemplates = {} if self.objVar.Get('cl_merge_set') == "on": for pkg in self.objVar.Get('cl_merge_pkg'): if not pkg: continue category = isPkgInstalled(pkg) if category: pkgContents = PkgContents("{CATEGORY}/{PF}".format( **category[0])) for filename in pkgContents.content.keys(): if not filename in self.cltObj.filterApplyTemplates: self.cltObj.filterApplyTemplates[filename] = [] self.cltObj.filterApplyTemplates[filename].append(pkg) for filename,pkgs in self.changedFiles.data.items(): filename = PkgContents.reCfg.sub("/",filename) if not filename in self.cltObj.filterApplyTemplates: self.cltObj.filterApplyTemplates[filename] = [] pkgs = filter(lambda x:not x in \ self.cltObj.filterApplyTemplates[filename], map(lambda x:x[0],pkgs)) self.cltObj.filterApplyTemplates[filename].extend(pkgs) self.cltObj.applyTemplates() self.stop = self.stop or self.cltObj.stop if not self.stop and ((self.objVar.Get('cl_merge_pkg') or \ self.objVar.Get('cl_action') in ("sync","domain","undomain")) and \ self.objVar.Get('cl_merge_pkg_new')): self.objVar.Set('cl_root_path', self.objVar.Get('cl_root_path_next'),force=True) self.recalculateBaseDir() self.objVar.Set('cl_merge_pkg_pass',list( set(self.objVar.Get('cl_merge_pkg_pass'))| set(self.objVar.Get('cl_merge_pkg'))| set(self.objVar.Get('cl_merge_pkg_new'))),force=True) self.objVar.Set('cl_merge_pkg', self.objVar.Get('cl_merge_pkg_new'),force=True) self.objVar.Set('cl_merge_pkg_new',[],force=True) createdDirs = self.createdDirs filesApply = self.filesApply self.changeMergePackage(self.objVar.Get('cl_merge_pkg')) self.applyTemplates(rerun=False) createdDirs.extend(self.createdDirs) filesApply.extend(self.filesApply) self.filesApply = filesApply self.createdDirs = createdDirs if rerun and self.stop != TemplatesInterrupt.ABORT: if self.cltObj: self.queueExecute.extend(self.cltObj.queueExecute) for processor,text,nameTemplate in self.queueExecute: if not self.executeTemplate(text,processor): self.setError(_("Failed to execute") + ": " +\ nameTemplate) return False self.queueExecute = [] self.filesApply = list(set(self.filesApply)) if self.objVar.Get('cl_protect_use_set') == 'on': self.updateProtectedFiles() if self.objVar.Get('cl_verbose_set') == 'on' and \ self.filesApply: self.verboseOutput(self.filesApply) self.objVar.Set('cl_merge_pkg_pass',[],force=True) self.objVar.Set('cl_merge_pkg_new',[],force=True) self.objVar.Set('cl_merge_pkg',[],force=True) return (self.createdDirs, self.filesApply) def verboseOutput(self,filesApply): """ Verbose output applied templates """ self.printWARNING(_("Calculate Utilities have changed files")+":") reGrey = re.compile(r"\._cfg\d{4}_") rootPath = self.objVar.Get('cl_root_path') for fn in sorted(list(set(filesApply))): if rootPath != '/' and self.objVar.Get('cl_action') == 'patch' and \ fn.startswith(rootPath): fn = fn[len(rootPath)+1:] if reGrey.search(fn): self.printSUCCESS(" "*5 + \ "%s"%fn) else: self.printSUCCESS(" "*5 + fn) def updateProtectedFiles(self): """ Update ._cfg0000 files """ if self.objVar.Get('cl_ebuild_phase') == 'compile': return chrootPath = self.objVar.Get('cl_chroot_path') cfgs = getCfgFiles(self.objVar.Get('cl_config_protect'), prefix=chrootPath) autoUpdateDict = {} for pkg in list(set(filter(None,list(self.changedFiles.getPkgs())+ self.objVar.Get('cl_merge_pkg')))): category = isPkgInstalled(pkg,prefix=chrootPath) if category: pkgContents = PkgContents("{CATEGORY}/{PF}".format( **category[0]),prefix=chrootPath) for filename,action in self.changedFiles.getPkgFiles(pkg): if filename in self.protectedFiles: pkgContents.removeObject(filename) continue if action in (ChangedFiles.FILE_MODIFIED, ChangedFiles.DIR_CREATED, ChangedFiles.DIR_EXISTS): pkgContents.addObject(filename) elif action in (ChangedFiles.FILE_REMOVED, ChangedFiles.DIR_REMOVED): pkgContents.removeObject(filename) files = set(map(lambda x:pathJoin(chrootPath,x), pkgContents.content.keys())) if (self.objVar.Get('cl_dispatch_conf') != 'usenew' and self.objVar.Get('cl_autoupdate_set') != "on"): notUpdate = files - set(self.autoUpdateFiles) files &= set(self.autoUpdateFiles) for filename in list(notUpdate&set(cfgs.keys())): if hashlib.md5(readFile(filename)).hexdigest() == \ hashlib.md5(readFile( cfgs[filename][0][1])).hexdigest(): files.add(filename) for filename in list(files&set(cfgs.keys())): # get ctime from orig filename #if os.path.exists(filename): # ctime = os.stat(filename).st_ctime #else: # ctime = 0 # if orig filename older that .cfg cfgs[filename].sort(reverse=True) #if ctime < cfgs[filename][0][0]: try: open(filename,'w').write( readFile(cfgs[filename][0][1])) except Exception as e: self.printERROR(str(e)) self.printWARNING( _("Failed to copy {ffrom} to {fto}").format( ffrom=cfgs[filename][0][1],fto=filename)) continue autoUpdateDict[cfgs[filename][0][1]] = filename for mtime,fn in cfgs[filename]: try: os.unlink(fn) except Exception as e: self.printWARNING(_("Failed to remove %s")%fn) try: pkgContents.writeContents() except IOError as e: self.printWARNING(_("Failed to modify %s contents")%pkg) self.filesApply = map(lambda x:autoUpdateDict.get(x,x),self.filesApply) if filter(lambda x:"._cfg" in x, self.filesApply): if self.objVar.Get('cl_ebuild_phase') != '': self.printWARNING(_("Some config files need updating. " "Perform run dispatch-conf.")) if self.dispatchConf and \ self.objVar.Get('cl_dispatch_conf') == 'dispatch' and \ self.objVar.Get('cl_ebuild_phase') == '': self.dispatchConf(self.filesApply) def scanningTemplates(self, scanDir, prefix=None, flagDir=False, optDir={}, skipTemplates=[]): """Сканирование и обработка шаблонов в директории scanDir""" ret = True if not prefix: prefix = os.path.realpath(scanDir) if not flagDir: # проверка корневой директории retDir = self.processingDirectory(scanDir, scanDir, optDir) if retDir is None: return None elif retDir is False: return False pathDir, objHead = retDir optDir["path"] = pathDir if not objHead is True: if objHead.typeAppend == "skip": # Установка опции пропуска директории optDir["skip"] = True if ("autoupdate" in objHead.params or self.objVar.Get('cl_autoupdate_set') == 'on'): optDir["autoupdate"] = True if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]): if not os.access(scanDir,os.R_OK|os.X_OK): self.printWARNING(_("Failed to read templates directory %s")% scanDir) return False for fileOrDir in sorted(listDirectory(scanDir)): absPath = os.path.join(scanDir,fileOrDir) if skipTemplates and absPath in skipTemplates: continue stInfo = os.lstat(absPath) statInfo = stInfo[stat.ST_MODE] prevModule = self.objVar.defaultModule prevBelong = self.functObj.currentBelong try: if stat.S_ISREG(statInfo): if not self.processingFile(absPath, prefix, optDir): ret = False continue elif stat.S_ISDIR(statInfo): # Обработка директории retDir = self.processingDirectory(absPath, prefix, optDir) if retDir is None: continue elif retDir is False: ret = False break # Опции следующей директории optNextDir = {} pathDir, objHead = retDir optNextDir["path"] = pathDir if not objHead is True: if objHead.typeAppend == "skip": # Установка опции пропуска директории optNextDir["skip"] = True if ("autoupdate" in objHead.params or self.objVar.Get('cl_autoupdate_set') == 'on'): optNextDir["autoupdate"] = True ret = self.scanningTemplates(absPath, prefix, True, optNextDir) if ret is False: break except TemplatesInterrupt as e: if e.isInterrupt(): self.stop = e.status() return False else: self.clearErrors() self.printWARNING(str(e)) finally: self.objVar.defaultModule = prevModule self.functObj.currentBelong = prevBelong return ret def processingFile(self, path, prefix, optFile): """Обработка в случае шаблона файла""" self.numberProcessTempl += 1 self.numberProcessTemplates(self.numberProcessTempl) # Пропуск шаблонов директорий if self.templDirNameFile == os.path.split(path)[1]: return True # Проверка на переменные в названии файла if not self.getNeedTemplate(path): if self.getError(): return False return True if self.getError(): return False nameFileConfig = path.partition(prefix)[2] # файл в системе без условий nameFileConfig = "/".join(map(lambda x:x.split("?")[0],\ nameFileConfig.split("/"))) # Записываем в переменную обрабатываемый файл self.objVar.Set("cl_pass_file",nameFileConfig) filesApl = self.joinTemplate(path, nameFileConfig, optFile) if self.getError(): return False if filesApl: # Настоящее имя конфигурационного файла nameFileConfig = filesApl[0] # Пишем время модификации *.env файлов if nameFileConfig.endswith(".env"): nameEnvFile = os.path.basename(nameFileConfig) self.functObj.timeConfigsIni[nameEnvFile] = float(time.time()) self.filesApply += filesApl if filesApl: self._addFile(filesApl) return True def processingDirectory(self, path, prefix, opt): """Обработка в случае директории если возвращаем None то пропуск дир.""" # Файл шаблона директории if not os.access(path,os.R_OK|os.X_OK): self.printWARNING(_("Failed to read templates directory %s")%path) return None dirInfoFile = os.path.join(path, self.templDirNameFile) newDir = pathJoin(self._baseDir, path.partition(prefix)[2]) newDir = "/".join(map(lambda x:x.split("?")[0], newDir.split("/"))) # Применяем шаблон pathDir, objHeadDir, createdDirs =\ self.getApplyHeadDir(newDir, dirInfoFile, opt) if createdDirs: self.createdDirs += createdDirs if os.path.isfile(pathDir): self.printWARNING(_("{dirpath} is a file").format(dirpath=pathDir)) self.printWARNING(_("templates in {tempath} are skipped" ).format(tempath=path)) return None if objHeadDir: return pathDir, objHeadDir else: if self.getError(): return False # Добавление количества файлов в пропущенной директории if path in self.dictTemplates.keys(): self.numberProcessTempl += self.dictTemplates[path] return None def _processMergePostmerge(self,params, templateDirFile): """Обработка параметров merge= , postmerge=""" if "merge" in params: mergePkgs = params['merge'].split(',') else: mergePkgs = [] if "postmerge" in params: postmergePkgs = params['postmerge'].split(',') else: postmergePkgs = [] if mergePkgs or postmergePkgs: for wrongPkg in (x for x in mergePkgs + postmergePkgs \ if not self.functObj.checkCorrectPkgName(x)): self.printWARNING( _("Wrong package '%s' for 'merge' in the template")% wrongPkg + ": " + templateDirFile) if self.objVar.Get('cl_ebuild_phase') == 'postinst': for pkg in postmergePkgs: if not pkg in self.objVar.Get('cl_merge_pkg_pass'): self.objVar.Get('cl_merge_pkg_pass').append(pkg) if not pkg in self.postmergePkgs: try: with open(self.postmergeFile,"a") as f: f.write("%s\n"%pkg) self.postmergePkgs.append(pkg) except: self.printWARNING( _("Failed to reconfigure package %s")%pkg) else: mergePkgs = mergePkgs + postmergePkgs for pkg in mergePkgs: if not pkg in self.objVar.Get('cl_merge_pkg_new') and \ not pkg in self.objVar.Get('cl_merge_pkg_pass') and \ not pkg in self.objVar.Get('cl_merge_pkg'): self.objVar.Get('cl_merge_pkg_new').append(pkg) def getApplyHeadDir(self, newDir, templateDirFile, optDir): """Применяет шаблон к директории (права, владелец, и.т. д)""" def function(text): """Функция обработки функций в заголовке""" return self.applyFuncTemplate(text, templateDirFile) def chownConfDir(nameDirConfig, uid, gid, nameFileTemplate): """Изменение владельца конфигурационной директории""" try: os.chown(nameDirConfig, uid, gid) except Exception as e: if hasattr(e,'errno') and e.errno == 13 and \ "var/calculate/remote" in nameDirConfig: return True import pwd, grp try: userName = pwd.getpwuid(uid).pw_name except: userName = str(uid) try: groupName = grp.getgrgid(gid).gr_name except: groupName = str(gid) owner = userName + ":" + groupName self.setError(_("Failed to apply template file %s")\ %nameFileTemplate) self.setError(_("error") + " " +\ "'chown %s %s'"%(owner, nameDirConfig)) return False return True applyDir = newDir # Родительская директория if optDir.get("path"): path = optDir["path"] else: if applyDir == self._baseDir: path = os.path.dirname(self._baseDir) else: path = os.path.split(applyDir)[1] path = pathJoin(self._baseDir, path) if not os.path.exists(templateDirFile): if applyDir != self._baseDir: applyDir = os.path.join(path, os.path.split(applyDir)[1]) # Фильтрация шаблонов по названию директории realPath = os.path.join("/",applyDir.partition(self._baseDir)[2]) if realPath in self.dirsFilter: return ("", False, []) # Создаем директорию если необходимо crDirs = self.createDir(applyDir, False, self.uid, self.gid) if not crDirs: return ("", False, []) if "autoupdate" in optDir: self.autoUpdateDirs.append(applyDir) if crDirs is True: return (applyDir, True, []) else: return (applyDir, True, crDirs) try: FD = open(templateDirFile) textTemplate = FD.readline().rstrip() buf = textTemplate while buf and textTemplate.endswith('\\'): buf = FD.readline() textTemplate = "%s %s"%(textTemplate[:-1],buf.rstrip()) FD.close() except: self.setError(_("Failed to open the template") + ": " +\ templateDirFile) return ("", False, []) headerLine = self.getHeaderText(textTemplate) if headerLine: moduleParam = filter(lambda x:x.startswith('env='), headerLine.split()) if moduleParam: self.objVar.defaultModule = moduleParam[0].partition('=')[2] try: varModule = importlib.import_module( "calculate.%s.variables"%self.objVar.defaultModule) except (ImportError, AttributeError),e: return ("", False, []) # Заменяем переменные на их значения textTemplate = self.applyVarsTemplate(textTemplate, templateDirFile) # Заменяем функции на их значения textTemplate = self.applyFuncTemplate(textTemplate, templateDirFile) # Обработка заголовка objHead = dirHeader(templateDirFile,textTemplate,self.objVar,function) # Директория с профилями не будет применена if not objHead.headerTerm: if objHead.getError(): self.setError(_("Incorrect template") + ": " +\ templateDirFile) return ("", False, []) # add packeges for reconfigure self._processMergePostmerge(objHead.params, templateDirFile) # Пропускаем директорию if objHead.typeAppend == "skip": applyDir = path return (applyDir, objHead, []) # Изменяем название родительской директории if "path" in objHead.params: path = objHead.params['path'] if path and path[0] == "~": # Получаем путь с заменой ~ на директорию пользователя path = os.path.join(self.homeDir, path.partition("/")[2],"")[:-1] elif not path or path and path[0] != "/": self.setError(_("Wrong value 'path' in the template") + ": " +\ templateDirFile) return ("", False, []) else: path = pathJoin(self._baseDir, path) # Изменяем название директории if "name" in objHead.params: nameDir = objHead.params['name'] if "/" in nameDir or nameDir == ".." or nameDir == ".": self.setError(_("Wrong value 'name' in the template") + ": " +\ templateDirFile) return ("", False, []) # Новый путь к директории applyDir = pathJoin(path, nameDir) else: applyDir = pathJoin(path, os.path.split(applyDir)[1]) # Фильтрация шаблонов по названию директории realPath = os.path.join("/",applyDir.partition(self._baseDir)[2]) if realPath in self.dirsFilter: return ("", False, []) # Удаляем директорию if objHead.typeAppend == "remove": if os.path.isdir(applyDir): self.changedFiles.addObj(applyDir,ChangedFiles.DIR_REMOVED, self.functObj.currentBelong) # удаляем директорию try: removeDir(applyDir) except: self.setError(_("Failed to delete the directory: ") +\ applyDir) return ("", False, []) # Очищаем директорию if objHead.typeAppend == "clear": if os.path.isdir(applyDir): for rmPath in os.listdir(applyDir): removePath = pathJoin(applyDir, rmPath) if os.path.isdir(removePath): # удаляем директорию try: removeDir(removePath) except: self.setError( _("Failed to delete the directory: ") +\ removePath) else: try: os.unlink(removePath) except: self.setError( _("Failed to delete: ") + removePath) return ("", False, []) # Созданные директории createdDirs = [] # chmod - изменяем права if "chmod" in objHead.params: mode = self.__octToInt(objHead.params['chmod']) if mode: if not os.path.exists(applyDir): crDirs = self.createDir(applyDir, mode, self.uid, self.gid) if not crDirs: return ("", False, []) if not crDirs is True: createdDirs += crDirs else: try: os.chmod(applyDir, mode) except: self.setError(_("Failed to change the mode for " "the directory: ") +applyDir) else: self.setError(_("Wrong value 'chmod' in the template") + ": " +\ templateDirFile) return ("", False, []) # chown - изменяем владельца и группу if "chown" in objHead.params: owner = objHead.params['chown'] if owner: if ":" in owner: strUid, strGid = owner.split(":") import pwd uid = self.getUidFromPasswd(strUid) try: uid = pwd.getpwnam(strUid).pw_uid except: self.setError(_("No such user on the system: ") + strUid) self.setError(_("Wrong value 'chown' in the template")+\ ": " + templateDirFile) return ("", False, []) gid = self.getGidFromGroup(strGid) try: import grp gid = grp.getgrnam(strGid).gr_gid except: self.setError(_("Group not found on the system: ") +strGid) self.setError( _("Wrong value 'chown' in the template") +\ ": "+ templateDirFile) return ("", False, []) if not os.path.exists(applyDir): crDirs = self.createDir(applyDir, False, uid, gid) if not crDirs: return ("", False, []) if not crDirs is True: createdDirs += crDirs else: if not chownConfDir(applyDir, uid, gid, templateDirFile): return ("", False, []) else: self.setError(_("Wrong value 'chown' in the template") + ": " + templateDirFile) return ("", False, []) else: self.setError(_("Wrong value 'chown' in the template") + ": " + templateDirFile) return ("", False, []) else: # Устанавливаем владельцем директории, пользователя по умолчанию # (переменная шаблона ur_login) if os.path.exists(applyDir): self.changedFiles.addObj(applyDir,ChangedFiles.DIR_EXISTS, self.functObj.currentBelong) tUid, tGid = getModeFile(applyDir, mode="owner") if (self.uid, self.gid) != (tUid, tGid): if not chownConfDir(applyDir, self.uid, self.gid, templateDirFile): return ("", False, []) else: self.changedFiles.addObj(applyDir,ChangedFiles.DIR_CREATED, self.functObj.currentBelong) crDirs = self.createDir(applyDir, False, self.uid, self.gid) if not crDirs: return ("", False, []) if not crDirs is True: createdDirs += crDirs if not objHead: applyDir = "" if applyDir: if ("autoupdate" in optDir or "autoupdate" in objHead.params ) and \ not self.objVar.Get('cl_merge_pkg_pass'): self.autoUpdateDirs.append(applyDir) return (applyDir, objHead, createdDirs) def getUidFromPasswd(self,strUid): """Get uid by username from chroot passwd file""" passwdFile = os.path.join(self._baseDir,'etc/passwd') if os.path.exists(passwdFile): mapUid = dict( filter(lambda x:x and len(x)>1 and x[0] and x[1], map(lambda x:x.split(':')[0:3:2], filter(lambda x:not x.startswith('#'), open(passwdFile,'r'))))) if strUid in mapUid: return int(mapUid[strUid]) return None def getGidFromGroup(self,strGid): """Get gid by groupname from chroot group file""" groupFile = os.path.join(self._baseDir,'etc/group') if os.path.exists(groupFile): mapGid = dict( filter(lambda x:x and len(x)>1 and x[0] and x[1], map(lambda x:x.split(':')[0:3:2], filter(lambda x:not x.startswith('#'), open(groupFile,'r'))))) if strGid in mapGid: return int(mapGid[strGid]) return None def checkOnNewConfigName(self,pathFile): """ Check on need update and return pathFile """ # if file in PROTECT_MASK or not in PROTECT chrootPath = self.objVar.Get('cl_chroot_path') if not filter(pathFile.startswith, map(lambda x:pathJoin(chrootPath,x), self.objVar.Get('cl_config_protect'))) or \ filter(pathFile.startswith, map(lambda x:pathJoin(chrootPath,x), self.objVar.Get('cl_config_protect_mask'))): return pathFile # if file was already modified by templates if pathFile in self.changedFiles.data.keys(): return pathFile # if using already created ._cfg file if self.configMode != T_ORIGIN: return pathFile # not current package file pkg = self.functObj.currentBelong if not pkg: if not self.allContents: fillContents(self.allContents, self.objVar.Get('cl_config_protect'), prefix=self.objVar.Get('cl_chroot_path')) origName = pathFile if chrootPath == '/' \ else pathFile[len(chrootPath):] if origName in self.allContents: pkg = self.allContents[origName] else: return pathFile pkg = isPkgInstalled(pkg,sortByVersion=True,prefix=chrootPath) if not pkg: return pathFile if checkContents("{CATEGORY}/{PF}".format(**pkg[-1]), pathFile, prefix=chrootPath, reservedFile='/var/lib/calculate/-CONTENTS-{PN}'.format(**pkg[-1]) \ if self.objVar.Get('cl_ebuild_phase') == 'postinst' \ else None): return pathFile real_filename = os.path.basename(pathFile) real_dirname = os.path.dirname(pathFile) self.configMode = T_NEWCFG return os.path.join(real_dirname,"._cfg0000_%s"%real_filename) def getApplyHeadTemplate(self, nameFileTemplate, nameFileConfig, templateFileType, optFile): """Применяет заголовок к шаблону (права, владелец, и.т. д)""" def function(text): """Функция обработки функций в заголовке""" return self.applyFuncTemplate(text,nameFileTemplate) def preReturn(pathProg): """Действия перед выходом из метода""" if pathProg: os.chdir(pathProg) def chownConfFile(nameFileConfig, uid, gid, nameFileTemplate, checkExists=True): """Изменение владельца конфигурационного файла""" try: if checkExists and not os.path.exists(nameFileConfig): # Создание файла FD = open(nameFileConfig, "w") FD.close() os.lchown(nameFileConfig, uid, gid) except Exception as e: if hasattr(e,'errno') and e.errno == 13 and \ "var/calculate/remote" in nameFileConfig: return True import pwd, grp try: userName = pwd.getpwuid(uid).pw_name except: userName = str(uid) try: groupName = grp.getgrgid(gid).gr_name except: groupName = str(gid) owner = userName + ":" + groupName self.setError(_("Failed to apply template file %s")\ %nameFileTemplate) self.setError(_("error") + " " +\ "'chown %s %s'"%(owner, nameFileConfig)) return False return True def chmodConfFile(nameFileConfig, mode, nameFileTemplate, checkExists=True): """Изменения режима доступа конфигурационного файла""" try: if checkExists and not os.path.exists(pathOldFile): # Создание файла FD = open(nameFileConfig, "w") FD.close() os.chmod(nameFileConfig, mode) except Exception as e: if hasattr(e,'errno') and e.errno == 13 and \ "var/calculate/remote" in nameFileConfig: return True self.setError(_("Failed to apply template file %s")\ %nameFileTemplate) self.setError(\ _("error") + " " +\ "'chmod %s %s'"%(str(oct(mode)), nameFileConfig)) return False return True self.closeFiles() pathProg = "" self.executeType = None # Файлы в системе к которым были применены шаблоны applyFiles = [nameFileConfig] # В случае бинарного типа файла читаем шаблон if templateFileType == "bin": self.nameFileTemplate = os.path.abspath(nameFileTemplate) self.F_TEMPL = self.openTemplFile(self.nameFileTemplate) if not self.F_TEMPL: self.setError(_("Failed to open the template") + ": " +\ self.nameFileTemplate) return False self.textTemplate = self.F_TEMPL.read() self.closeTemplFile() objHeadNew = fileHeader(nameFileTemplate, self.textTemplate, False, templateFileType ,objVar=self.objVar, function=function,templateObj=self) # файл шаблона не будет применен if not objHeadNew.headerTerm: if objHeadNew.getError(): self.setError(_("Incorrect template") + ": " +\ nameFileTemplate) return ([], False) # add packeges for reconfigure self._processMergePostmerge(objHeadNew.params, nameFileTemplate) # Родительская директория path = optFile["path"] # Изменяем название родительской директории if "path" in objHeadNew.params: path = objHeadNew.params['path'] if path and path[0] == "~": # Получаем путь с заменой ~ на директорию пользователя path = os.path.join(self.homeDir,path.partition("/")[2],"")[:-1] elif not path or path and path[0] != "/": self.setError(_("Wrong value 'path' in the template") + ": " +\ nameFileTemplate) return ([], False) else: path = pathJoin(self._baseDir, path) # Путь к оригинальному файлу - pathOldFile # Изменяем путь к оригинальному файлу if objHeadNew.params.has_key("name"): nameFile = objHeadNew.params['name'] if "/" in nameFile or nameFile == ".." or nameFile == ".": self.setError(_("Wrong value 'name' in the template") + ": " +\ nameFileTemplate) return ([], False) # Новый путь к оригинальному файлу pathOldFile = pathJoin(path,nameFile) else: pathOldFile = pathJoin(path,os.path.split(nameFileConfig)[1]) pathOrigFile = pathOldFile if self.objVar.Get('cl_protect_use_set') == 'on': pathOldFile = self.fixNameFileConfig(pathOldFile) pathOldFile = self.checkOnNewConfigName(pathOldFile) # буффер для использование в link= newBuffer = None applyFiles = [pathOldFile] # Фильтрация шаблонов по названию файла realPath = os.path.join("/",pathOldFile.partition(self._baseDir)[2]) if realPath in self.filesFilter: return ([], False) typeAppendTemplate = objHeadNew.typeAppend # Параметр exec if "exec" in objHeadNew.params or "run" in objHeadNew.params: if "exec" in objHeadNew.params: paramName = "exec" else: paramName = "run" self.executeType = paramName execPath = objHeadNew.params[paramName] if not os.access(execPath,os.X_OK): self.setError(_("Wrong value '%s' in the template")%paramName \ + ": " + nameFileTemplate) self.setError(_("Failed to execute %s") %execPath) return ([], False) if typeAppendTemplate == "join": self.setError(_("Wrong value 'append=join' in the template") + ": " +\ nameFileTemplate) return ([], False) # Очищаем оригинальный файл if typeAppendTemplate == "clear": try: open(pathOldFile, "w").truncate(0) newBuffer = "" except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to clear the file") + ": " +\ pathOldFile) return (applyFiles, False) # Удаляем оригинальный файл if typeAppendTemplate == "remove": if objHeadNew.params.has_key("force"): pathOldFile = pathOrigFile if os.path.islink(pathOldFile): # удаляем ссылку try: os.unlink(pathOldFile) return (applyFiles, False) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the link") + ": " +\ pathOldFile) return ([], False) if os.path.isfile(pathOldFile) and self.configMode == T_ORIGIN: # удаляем файл try: os.remove(pathOldFile) return (applyFiles, False) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the file") + ": " +\ pathOldFile) return ([], False) return ([], False) # Пропускаем обработку шаблона elif typeAppendTemplate == "skip": return ([], False) # Создаем директорию для файла если ее нет if not os.path.exists(path): if not self.createDir(path): return ([], False) # В случае force if objHeadNew.params.has_key("force"): if os.path.islink(pathOldFile): # удаляем ссылку try: newBuffer = "" os.unlink(pathOldFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the link") + ": " +\ pathOldFile) return ([], False) if os.path.isfile(pathOldFile): # удаляем файл try: newBuffer = "" os.remove(pathOldFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the file") + ": " +\ pathOldFile) return ([], False) flagSymlink = False flagForce = False # Если есть параметр mirror if objHeadNew.params.has_key("mirror"): if objHeadNew.params.has_key("link"): templateFile = objHeadNew.params['link'] if templateFile and templateFile[0] == "~": # Получаем директорию пользователя templateFile = os.path.join(self.homeDir, templateFile.partition("/")[2],"")[:-1] templateFile = pathJoin(self._baseDir, templateFile) if not os.path.exists(templateFile): if os.path.exists(pathOldFile): try: newBuffer = "" os.remove(pathOldFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the file") + ": " +\ pathOldFile) return ([], False) elif not os.path.exists(pathOldFile): return ([], False) # Если есть указатель на файл шаблона (link) if objHeadNew.params.has_key("link") and\ not objHeadNew.params.has_key("symbolic"): templateFile = objHeadNew.params['link'] if templateFile and templateFile[0] == "~": # Получаем директорию пользователя templateFile = os.path.join(self.homeDir, templateFile.partition("/")[2],"")[:-1] templateFile = pathJoin(self._baseDir, templateFile) foundTemplateFile = os.path.exists(templateFile) if foundTemplateFile: try: F_CONF = self.openTemplFile(templateFile) buff = F_CONF.read() F_CONF.close() fMode, fUid, fGid = getModeFile(templateFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to open the file") + ": " +\ templateFile) return ([], False) if os.path.exists(pathOldFile): try: newBuffer = "" os.remove(pathOldFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the file") + ": " +\ pathOldFile) return ([], False) if foundTemplateFile: try: FD = open(pathOldFile, "w+") newBuffer = buff FD.write(buff) FD.close() except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to create the file") + " '%s'"\ %pathOldFile) return ([], False) oMode = getModeFile(pathOldFile, mode="mode") # Если права не совпадают, меняем права if fMode != oMode: if not chmodConfFile(pathOldFile, fMode, nameFileTemplate, checkExists=False): return ([], False) # Если символическая ссылка if objHeadNew.params.has_key("symbolic"): prevOldFile = pathOldFile pathOldFile = objHeadNew.params['link'] flagSymlink = True if not pathOldFile: raise TemplatesError( _("Missed source link in template '%s'") %str(nameFileTemplate)) if not "/" == pathOldFile[0]: pathLink = os.path.split(os.path.abspath(prevOldFile))[0] pathProg = os.getcwd() try: os.chdir(pathLink) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError( _("Failed to change the current directory to")+\ " " + pathLink) return ([], False) # chmod - изменяем права if objHeadNew.params.has_key("chmod"): mode = self.__octToInt(objHeadNew.params['chmod']) if mode: if not chmodConfFile(pathOldFile, mode, nameFileTemplate): preReturn(pathProg) return ([], False) else: self.setError(_("Wrong value 'chmod' in the template") + ": " +\ nameFileTemplate) preReturn(pathProg) return ([], False) # chown - изменяем владельца и группу if objHeadNew.params.has_key("chown"): owner = objHeadNew.params['chown'] if owner: if ":" in owner: strUid, strGid = owner.split(":") if strUid.isdigit(): uid = int(strUid) else: uid = self.getUidFromPasswd(strUid) import pwd try: if uid is None: uid = pwd.getpwnam(strUid).pw_uid except: self.setError(_("No such user on the system: ") + strUid) self.setError( _("Wrong value 'chown' in the template") + ": "+ nameFileTemplate) preReturn(pathProg) return ([], False) if strGid.isdigit(): gid = int(strGid) else: gid = self.getGidFromGroup(strGid) try: if gid is None: import grp gid = grp.getgrnam(strGid).gr_gid except: self.setError(_("Group not found on the system: ") + strGid) self.setError( _("Wrong value 'chown' in the template") + ": "+ nameFileTemplate) preReturn(pathProg) return ([], False) # Изменяем владельца файла if not chownConfFile(pathOldFile,uid,gid,nameFileTemplate): preReturn(pathProg) return ([], False) else: self.setError(_("Wrong value 'chown' in the template") + ": " + nameFileTemplate) preReturn(pathProg) return ([], False) else: self.setError(_("Wrong value 'chown' in the template") + ": " + nameFileTemplate) preReturn(pathProg) return ([], False) if not flagSymlink: self.openFiles(nameFileTemplate, pathOldFile,objHeadNew.fileType, newBuffer) if self.getError(): return ([], False) if not objHeadNew.params.has_key("chown"): # Устанавливаем владельцем конфигурационного файла, # пользователя по умолчанию (переменная шаблона ur_login) if os.path.exists(pathOldFile): tUid, tGid = getModeFile(pathOldFile, mode="owner") if (self.uid, self.gid) != (tUid, tGid): # Изменяем владельца файла if not chownConfFile(pathOldFile, self.uid, self.gid, nameFileTemplate, checkExists=False): preReturn(pathProg) return ([], False) if flagSymlink: if os.path.exists(prevOldFile) or os.path.islink(prevOldFile): try: if os.path.islink(prevOldFile): # если ссылка то удаляем её os.unlink(prevOldFile) else: # иначе удаляем файл os.remove(prevOldFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to delete the file") + ": " +\ prevOldFile) preReturn(pathProg) return ([], False) if not "/" == pathOldFile[0]: applyFiles = [prevOldFile]#,os.path.join(pathLink,pathOldFile)] else: applyFiles = [prevOldFile]#,pathOldFile] try: os.symlink(pathOldFile, prevOldFile) except: self.setError(_("Template error") + ": " +\ nameFileTemplate) self.setError(_("Failed to create a symbolic link") + " :" +\ "%s -> %s"%(prevOldFile, pathOldFile)) preReturn(pathProg) return ([], False) if not objHeadNew.body.strip(): preReturn(pathProg) if "protected" in objHeadNew.params: self.protectedFiles += applyFiles return (applyFiles, False) else: applyFiles = [pathOldFile] if pathProg: os.chdir(pathProg) # Если файлы заменяются не нужно их обрабатывать дальше if typeAppendTemplate == "replace" and\ not objHeadNew.params.has_key("symbolic") and\ objHeadNew.params.has_key("link"): preReturn(pathProg) return (applyFiles, False) if not pathOldFile in self.dictProcessedTemplates: self.dictProcessedTemplates[pathOldFile] = [] self.dictProcessedTemplates[pathOldFile].append(nameFileTemplate) preReturn(pathProg) if ( "autoupdate" in optFile or "autoupdate" in objHeadNew.params ) \ and not self.objVar.Get('cl_merge_pkg_pass'): reCfg = re.compile(r"/._cfg\d{4}_",re.S) self.autoUpdateFiles += map(lambda x:reCfg.sub('/',x),applyFiles) if "protected" in objHeadNew.params: self.protectedFiles += applyFiles return (applyFiles, objHeadNew) def createNewClass(self, name, bases, attrs={}): """Создает объект нового класса createNewClass(self, name, bases, attrs) name - имя класса - str, bases - cписок наследуемых классов - (tuple), attrs - аттрибуты класса - {dict} """ class newMethod: #Объединяем конфигурации def join(self, newObj): if newObj.__class__.__name__ == self.__class__.__name__: self.docObj.joinDoc(newObj.doc) # Пост обработка if 'postXML' in dir(self): self.postXML() attrsNew = {} attrsNew["configName"] = name if attrs: for key in attrs.keys(): attrsNew[key] = attrs[key] newCl = type(name, bases + (newMethod, object,), attrsNew) return newCl def fileIsUtf(self, fileName): """Проверяет файл на кодировку UTF-8""" if os.path.isfile(fileName): FD = open(os.path.abspath(fileName),'r') newTemplate = FD.read(1)+FD.read() FD.close() try: newTemplate.decode("UTF-8") except: return False return True return False def joinTemplate(self, nameFileTemplate, nameFileConfig, optFile={}): """Объединения шаблона и конф. файла join(nameFileTemplate, nameFileConfig, ListOptTitle) Объединение шаблона nameFileTemplate и конф. файла nameFileConfig, ListOptTitle - список строк которые добавятся в заголовок optFile = опции для шаблона """ # Выполняем условия для блока текста а так-же заменяем переменные self.nameFileTemplate = os.path.abspath(nameFileTemplate) self.F_TEMPL = self.openTemplFile(self.nameFileTemplate) self.textTemplate = self.F_TEMPL.read() self.configMode = T_ORIGIN self.closeTemplFile() # Флаг копирования шаблона в конфигурационный файл flagCopyTemplate = True # Тип шаблона бинарный или текстовый templateFileType = self.getTemplateType() headerLine = self.getHeaderText(self.textTemplate) if headerLine: moduleParam = filter(lambda x:x.startswith('env='), headerLine.split()) if moduleParam: self.objVar.defaultModule = moduleParam[0].partition('=')[2] try: varModule = importlib.import_module( "calculate.%s.variables"%self.objVar.defaultModule) except (ImportError, AttributeError),e: return [] if templateFileType != "bin": # Заменяем переменные на их значения self.textTemplate = self.applyVarsTemplate(self.textTemplate, nameFileTemplate) flagCopyTemplate = False if not optFile: optFile = {"path":os.path.split(nameFileConfig)[0]} filesApply, objHeadNew = self.getApplyHeadTemplate(nameFileTemplate, nameFileConfig, templateFileType, optFile) if not objHeadNew: return filesApply if filesApply and not filter(lambda x:"calculate/ini.env" in x, filesApply): self.templateModify() if templateFileType != "bin": # Вычисляем условные блоки objHeadNew.body = self.applyTermsTemplate(objHeadNew.body, nameFileTemplate) # Вычисляем функции objHeadNew.body = self.applyFuncTemplate(objHeadNew.body, nameFileTemplate) # Настоящее имя конфигурационного файла nameFileConfig = filesApply[0] # Флаг - кодировка с бинарными примесями у файла шаблона включаем при # условии текстового файла и кодировки отличной от UTF-8 flagNotUtf8New = False # Флаг - кодировка с бинарными примесями у оригинального файла flagNotUtf8Old = False if not flagCopyTemplate: # проверяем кодировку шаблона if not self.fileIsUtf(nameFileTemplate): flagNotUtf8New = True if not (objHeadNew.params.has_key("link") and\ objHeadNew.params.has_key("symbolic")): # проверяем кодировку оригинального файла if not self.fileIsUtf(nameFileConfig): flagNotUtf8Old = True self.textTemplate = objHeadNew.body # Список примененных шаблонов ListOptTitle = [] if nameFileConfig in self.dictProcessedTemplates: ListOptTitle = self.dictProcessedTemplates[nameFileConfig] # Титл конфигурационного файла title = "" if ListOptTitle: title = self.getTitle(objHeadNew.comment, ListOptTitle, configPath=nameFileConfig) title = title.encode("UTF-8") objHeadOld = False if objHeadNew.comment: objHeadOld = fileHeader(nameFileConfig, self.textConfig, objHeadNew.comment) elif objHeadNew.fileType and\ objHeadNew.typeAppend in ("before", "after"): configFileType = self.getFileType(nameFileConfig) objHeadOld = fileHeader(nameFileConfig, self.textConfig, fileType=configFileType) # Строка вызова скрипта (#!/bin/bash ...) execStr = "" if objHeadNew.execStr: execStr = objHeadNew.execStr elif objHeadOld and objHeadOld.execStr: execStr = objHeadOld.execStr if objHeadNew.fileType != 'patch': wrongOpt = [x for x in ("multiline","dotall") if objHeadNew.params.has_key(x)] if wrongOpt: self.setError(\ _("Option %s should be used for format=patch only")\ %wrongOpt[0]) return False if objHeadNew.fileType: formatTemplate = objHeadNew.fileType typeAppendTemplate = objHeadNew.typeAppend if formatTemplate in ("patch","diff"): if typeAppendTemplate != "patch": self.setError(\ _("Wrong option append=%(type)s in template %(file)s")\ %{"type":typeAppendTemplate,"file":nameFileTemplate}) return False # создаем объект формата шаблона objTempl = self.getFormatObj(formatTemplate, self.textTemplate) if formatTemplate == 'patch': if objHeadNew.params.has_key("multiline"): objTempl.setMultiline() if objHeadNew.params.has_key("dotall"): objTempl.setDotall() if not objTempl: self.setError(\ _("Incorrect header parameter format=%s " "in the template")\ %formatTemplate + " " + nameFileTemplate) return False if objHeadOld and objHeadOld.body: self.textConfig = objHeadOld.body # обработка конфигурационного файла self.textTemplate = objTempl.processingFile(self.textConfig, self.objVar.Get('cl_root_path')) if objTempl.getError(): raise TemplatesError(_("Failed to use patch ") + \ nameFileTemplate) elif formatTemplate == 'diff': self.printSUCCESS(_("Appling patch")+ " " + \ os.path.basename(nameFileTemplate)) if execStr: self.textConfig = execStr + title + self.textTemplate else: self.textConfig = title + self.textTemplate if formatTemplate in ("diff",): return objTempl.patchFiles else: self.saveConfFile() if 'run' in objHeadNew.params: if not self.executeTemplate(self.textConfig, objHeadNew.params['run']): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) self.setError(_("Failed to execute") + ": " +\ self.nameFileConfig) return False return False return filesApply if not 'exec' in objHeadNew.params else False # Создаем объект в случае параметра format в заголовке if (typeAppendTemplate == "replace" or\ typeAppendTemplate == "before" or\ typeAppendTemplate == "after") and\ not (formatTemplate == "bin" or\ formatTemplate == "raw"): # Преобразовываем бинарные файлы if flagNotUtf8New: objTxtCoder = utfBin() self.textTemplate = objTxtCoder.encode(self.textTemplate) # создаем объект формата шаблона objTemplNew = self.getFormatObj(formatTemplate, self.textTemplate) if not objTemplNew: self.setError(\ _("Incorrect header parameter format=%s " "in the template")\ %formatTemplate + " " + nameFileTemplate) return False if "xml_" in formatTemplate: if objTemplNew.getError(): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) return False # Имя файла внутри xml xfce конфигурационных файлов nameRootNode=nameFileConfig.rpartition("/")[2].split(".")[0] objTemplNew.setNameBodyNode(nameRootNode) # Объект Документ docObj = objTemplNew.docObj # Удаление комментариев из документа docObj.removeComment(docObj.getNodeBody()) # Добавление необходимых переводов строк docObj.insertBRtoBody(docObj.getNodeBody()) # Добавление необходимых разделителей между областями docObj.insertBeforeSepAreas(docObj.getNodeBody()) # Пост обработка if 'postXML' in dir(objTemplNew): objTemplNew.postXML() # Получение текстового файла из XML документа self.textTemplate = objTemplNew.getConfig().encode("UTF-8") # Если не UTF-8 производим преобразование if flagNotUtf8New: self.textTemplate = objTxtCoder.decode(self.textTemplate) # Титл для объединения if ListOptTitle: title = self.getTitle(objTemplNew._comment, ListOptTitle, configPath=nameFileConfig) title = title.encode("UTF-8") # Замена if typeAppendTemplate == "replace": if "xml_" in formatTemplate: data = self.textTemplate.split("\n") data.insert(1,title) self.textConfig = "\n".join(data) else: if objHeadNew.execStr: self.textConfig = objHeadNew.execStr+title+\ self.textTemplate else: self.textConfig = title + self.textTemplate self.saveConfFile() if 'run' in objHeadNew.params: if not self.executeTemplate(self.textConfig, objHeadNew.params['run']): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) self.setError(_("Failed to execute") + ": " +\ self.nameFileConfig) return False return False return filesApply if not 'exec' in objHeadNew.params else False # Вверху elif typeAppendTemplate == "before": if "xml_" in formatTemplate: self.setError(\ _("Wrong option append=before in template %s")\ %nameFileTemplate) return False if objHeadOld and objHeadOld.body: self.textConfig = objHeadOld.body if self.textTemplate and self.textTemplate[-1] == "\n": tmpTemplate = self.textTemplate + self.textConfig else: tmpTemplate = self.textTemplate + "\n" + self.textConfig if execStr: self.textConfig = execStr + title + tmpTemplate else: self.textConfig = title + tmpTemplate self.saveConfFile() if 'run' in objHeadNew.params: if not self.executeTemplate(self.textConfig, objHeadNew.params['run']): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) self.setError(_("Failed to execute") + ": " +\ self.nameFileConfig) return False return False return filesApply if not 'exec' in objHeadNew.params else False # Внизу elif typeAppendTemplate == "after": if "xml_" in formatTemplate: self.setError(\ _("Wrong option append=after in template %s")\ %nameFileTemplate) return False if objHeadOld and objHeadOld.body: self.textConfig = objHeadOld.body if self.textTemplate[-1] == "\n": tmpTemplate = self.textConfig + self.textTemplate else: tmpTemplate = self.textConfig + "\n" + self.textTemplate if execStr: self.textConfig = execStr + title + tmpTemplate else: self.textConfig = title + tmpTemplate self.saveConfFile() if 'run' in objHeadNew.params: if not self.executeTemplate(self.textConfig, objHeadNew.params['run']): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) self.setError(_("Failed to execute") + ": " +\ self.nameFileConfig) return False return False return filesApply if not 'exec' in objHeadNew.params else False # Объединение elif typeAppendTemplate == "join": if flagNotUtf8New: objTxtCoder = utfBin() self.textTemplate = objTxtCoder.encode(self.textTemplate) if formatTemplate =="raw": self.setError(\ _("Incorrect header parameter append=%s " "in the template")\ %typeAppendTemplate + " " + nameFileTemplate) return False # создаем объект формата шаблона objTemplNew = self.getFormatObj(formatTemplate, self.textTemplate) if not objTemplNew: self.setError(\ _("Incorrect header parameter format=%s in " "the template")\ %formatTemplate + " " + nameFileTemplate) return False if "xml_" in formatTemplate: if objTemplNew.getError(): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) return False nameRootNode=nameFileConfig.rpartition("/")[2].split(".")[0] objTemplNew.setNameBodyNode(nameRootNode) # Титл для объединения if ListOptTitle: title = self.getTitle(objTemplNew._comment, ListOptTitle, configPath=nameFileConfig) title = title.encode("UTF-8") # В случае пустого конфигурационного файла reNoClean = re.compile("[^\s]",re.M) if not self.textConfig or\ not reNoClean.search(self.textConfig): self.textConfig = "" objHeadOld = fileHeader(nameFileConfig, self.textConfig, objTemplNew._comment) if objHeadOld.body: self.textConfig = objHeadOld.body else: self.textConfig = "" if flagNotUtf8Old: objTxtCoder = utfBin() self.textConfig = objTxtCoder.encode(self.textConfig) # создаем объект формата шаблона для конфигурационного файла objTemplOld = self.getFormatObj(formatTemplate, self.textConfig) if not objTemplOld: self.setError(_("Error in template %s") %nameFileConfig) return False if "xml_" in formatTemplate: if objTemplOld.getError(): self.setError(_("Wrong template") + ": " +\ nameFileConfig) return False nameRootNode=nameFileConfig.rpartition("/")[2].split(".")[0] objTemplOld.setNameBodyNode(nameRootNode) objTemplOld.join(objTemplNew) if "xml_" in formatTemplate: if objTemplOld.getError(): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) return False data = objTemplOld.getConfig().encode("UTF-8").split("\n") data.insert(1,title) self.textConfig = "\n".join(data) else: if execStr: self.textConfig = execStr + title +\ objTemplOld.getConfig().encode("UTF-8") else: self.textConfig = title +\ objTemplOld.getConfig().encode("UTF-8") # Декодируем если кодировка не UTF-8 if flagNotUtf8New or flagNotUtf8Old: self.textTemplate = objTxtCoder.decode(self.textTemplate) self.textConfig = objTxtCoder.decode(self.textConfig) self.saveConfFile() if 'run' in objHeadNew.params: if not self.executeTemplate(self.textConfig, objHeadNew.params['run']): self.setError(_("Wrong template") + ": " +\ nameFileTemplate) self.setError(_("Failed to execute") + ": " +\ self.nameFileConfig) return False return False return filesApply if not 'exec' in objHeadNew.params else False else: self.setError(_("Wrong template option (type append)") + ": " +\ typeAppendTemplate) return False else: self.setError(_("Template type not found: ") + nameFileTemplate) return False return filesApply class scanDirectoryClt: """Класс для cканирования директорий с файлами .clt""" # Расширение файла шаблона extFileTemplate = ".clt" lenExtFileTemplate = len(extFileTemplate) filterApplyTemplates = [] reHeader = re.compile(r"\s*#\s*calculate\s*", re.I) def __init__(self,objVar=None): if objVar: self.objVar = objVar def processingFile(self, path, prefix): """Обработка в случае файла""" return True def hasBelong(self,filename): """ Change belong function """ f = open(filename,'r') s = f.readline() if self.reHeader.search(s): while s: if "belong(" in s or "merge(" in s: return True if not s.strip().endswith('\\'): break s = f.readline() return False def scanningTemplates(self, scanDir, prefix=None, flagDir=False, objVar=None): """Сканирование и обработка шаблонов в директории scanDir""" ret = True if not objVar: objVar = self.objVar if not prefix: prefix = os.path.realpath(scanDir) if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]): for fileOrDir in sorted(listDirectory(scanDir)): try: absPath = os.path.join(scanDir,fileOrDir) stInfo = os.lstat(absPath) statInfo = stInfo[stat.ST_MODE] if fileOrDir.endswith(self.extFileTemplate) and\ stat.S_ISREG(statInfo): if not self.filterApplyTemplates and \ self.objVar.Get('cl_merge_set') == 'off' or \ self.filterApplyTemplates and \ absPath[:-self.lenExtFileTemplate] in\ self.filterApplyTemplates.keys() or self.hasBelong(absPath): prevDefault = self.objVar.defaultModule if not self.processingFile(absPath, prefix): return False self.objVar.defaultModule = prevDefault elif stat.S_ISDIR(statInfo): if not self.scanningTemplates(absPath, prefix, True): return False except TemplatesInterrupt as e: if e.isInterrupt(): self.stop = e.status() return False else: self.clearErrors() self.printWARNING(str(e)) return True class templateClt(scanDirectoryClt, Template): """Класс для обработки шаблонов c расширением .clt""" def __init__(self, objVar, postmergePkgs, **kwargs): self.checkNumberTemplate = True Template.__init__(self, objVar, cltObj=False,**kwargs) self.postmergePkgs = postmergePkgs applyPackages = ["calculate-core"] self.flagApplyTemplates = False if self.objVar.Get("cl_name") in applyPackages: self.flagApplyTemplates = True # Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д self._chrootDir = os.path.normpath(self.objVar.Get("cl_chroot_path")) def applyTemplate(self, path): """Применение отдельного .clt шаблона""" if not self.flagApplyTemplates: return True return self.processingFile(path, "") def processingFile(self, path, prefix): """Обработка в случае шаблона файла""" self.numberProcessTempl += 1 self.numberProcessTemplates(self.numberProcessTempl) # Пропуск шаблонов директорийscanningTemplates if self.templDirNameFile == os.path.split(path)[1]: return True self.functObj.currentBelong = "" # Проверка на переменные в названии файла if not self.getNeedTemplate(path): if self.getError(): return False return True if self.getError(): return False if prefix and prefix[-1] == "/": prefix = prefix[:-1] if prefix and path.startswith(prefix): nameFileConfig = path.partition(prefix)[2] else: nameFileConfig = path nameFileConfig = nameFileConfig[:-self.lenExtFileTemplate] origFileName = nameFileConfig nameFileConfig = pathJoin(self._baseDir, nameFileConfig) # файл в системе без условий nameFileConfig = "/".join(map(lambda x:x.split("?")[0],\ nameFileConfig.split("/"))) # Записываем в переменную обрабатываемый файл self.objVar.Set("cl_pass_file",nameFileConfig) filesApl = self.joinTemplate(path, nameFileConfig) if self.getError(): return False if filesApl: if self.functObj.currentBelong: self._addFile(filesApl) else: if origFileName in self.filterApplyTemplates: for pkg in self.filterApplyTemplates[origFileName]: self._addFile(filesApl,pkg=pkg) else: if not self.allContents: fillContents(self.allContents, self.objVar.Get('cl_config_protect'), prefix=self.objVar.Get('cl_chroot_path')) for fn in filesApl: fn_orig = PkgContents.reCfg.sub('/',fn) if self.objVar.Get('cl_chroot_path') != '/': fn_orig = \ fn_orig[len(self.objVar.Get('cl_chroot_path')):] if fn_orig in self.allContents: self._addFile([fn],pkg=self.allContents[fn_orig]) # Настоящее имя конфигурационного файла nameFileConfig = filesApl[0] # Пишем время модификации *.env файлов if nameFileConfig.endswith(".env"): nameEnvFile = os.path.basename(nameFileConfig) self.functObj.timeConfigsIni[nameEnvFile] = float(time.time()) self.filesApply += filesApl return nameFileConfig else: return True def countsNumberTemplates(self, dirsTemplates=[]): """Считаем количество шаблонов""" def createDictTemplates(path, prefix, dictTemplates): """Создает словарь {"директория":"кол-во шаблонов" ...} и считает общее количество шаблонов """ # Количество шаблонов self.allTemplates += 1 dirTemplate = os.path.split(path)[0] while(True): if dirTemplate in dictTemplates.keys(): dictTemplates[dirTemplate] += 1 else: dictTemplates[dirTemplate] = 1 if dirTemplate == prefix: break dirTemplate = os.path.split(dirTemplate)[0] return dictTemplates if not dirsTemplates: dirsTemplates = self.objVar.Get("cl_template_clt_path") dirsTemplates.sort() scanObj = scanDirectoryClt(objVar=self.objVar) scanObj.processingFile = lambda x,y: createDictTemplates(x, y,\ self.dictTemplates) # Считаем количество шаблонов for dirTemplate in dirsTemplates: scanObj.scanningTemplates(dirTemplate, "/") def applyTemplates(self,cltPath=None): """Применяет шаблоны к конфигурационным файлам""" if not self.flagApplyTemplates: return ([],[]) if cltPath is None and \ not self.objVar.defined("cl_template_clt_path"): self.setError(_("undefined variable: ") + "cl_template_clt_path") return False if cltPath is None: dirsTemplates = self.objVar.Get("cl_template_clt_path") else: dirsTemplates = cltPath dirsTemplates.sort() if self.checkNumberTemplate: # Созданные директории self.createdDirs = [] # Примененные файлы self.filesApply = [] # Номер применяемого шаблона self.numberProcessTempl = 0 # Словарь директорий с количеством файлов шаблонов self.dictTemplates = {} # Количество шаблонов self.allTemplates = 0 # Установка по умолчанию аттрибутов для функциии шаблонов ini() # Время доступа к конфигурационному файлу функции шаблона ini() self.functObj.timeIni = -1 # Первоначальный словарь переменных для ini() self.functObj.prevDictIni = {} # Текущий словарь переменных для ini() self.functObj.currDictIni = {} # Словарь времен модификации env файлов для ini() self.functObj.timeConfigsIni = {} # Считаем количество шаблонов self.countsNumberTemplates(dirsTemplates=dirsTemplates) self.numberAllTemplates(self.allTemplates) # default module self.defaultModule = "main" # Обрабатываем шаблоны for dirTemplate in dirsTemplates: if self.scanningTemplates(dirTemplate, self._chrootDir) is False: break return (self.createdDirs, self.filesApply) class iniParser(_error, templateFormat): """Класс для работы с ini файлами """ def __init__(self, iniFile=None,text=None): # название ini файла self.iniFile = iniFile # права создаваемого ini-файла self.mode = 0640 # Cоответствует ли формат файла нужному self.checkIni = None self.FD = None self.readOnly = False self.text = text self.locked = False def joinText(self, iniObj, xmlNewDoc): """Объединяет два документа""" newRootNode = xmlNewDoc.documentElement newBodyNode = xpath.Evaluate('child::body',newRootNode)[0] newImportBodyNode = iniObj.doc.importNode(newBodyNode, True) iniObj.docObj.joinBody(iniObj.docObj.body, newImportBodyNode) #iniObj.docObj.insertBRtoBody(iniObj.docObj.body) def setMode(self, mode): """установка прав создаваемого ini-файла""" self.mode = mode def lockfile(self,fd,fn,timeout=5): """ Блокировка файла с таймаутом """ if self.locked: return True for i in range(0,timeout): try: fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) return True except IOError,e: if e.errno != errno.EAGAIN: raise e time.sleep(1) else: raise TemplatesError(_("Lock timeout of %s")%fn) @property def iniFile_lock(self): return self.iniFile+".lock~" def wait_global_lock(self, timeout=10): lockfn = self.iniFile_lock for i in range(0,timeout): if self.locked or not os.path.exists(lockfn): break time.sleep(1) @contextmanager def lock(self): lockfn = self.iniFile_lock lockf = open(lockfn, "w+") self.lockfile(lockf.fileno(), lockfn) self.locked = True try: yield finally: self.locked = False os.unlink(lockfn) def _open(self, mode): self.wait_global_lock() return open(self.iniFile, mode) def openIniFile(self): if not self.text is None: return self.text if not os.access(self.iniFile, os.R_OK): return "" self.FD = self._open("r") self.lockfile(self.FD.fileno(),self.iniFile) textIni = self.FD.read() return textIni def openRWIniFile(self): if not os.access(self.iniFile, os.R_OK): return "" try: self.FD = self._open("r+") except (IOError, OSError), e: self.FD = self._open("r") self.readOnly = True self.lockfile(self.FD.fileno(), self.iniFile) textIni = self.FD.read() return textIni def writeIniFile(self, txtConfig): if self.readOnly: self.setError(_("Failed to write to file") + ": " + self.iniFile) return False if not os.path.exists(self.iniFile): try: # Создание файла self.FD = self._open("w+") self.lockfile(self.FD.fileno(), self.iniFile) os.chmod(self.iniFile, self.mode) except: self.setError(_("Failed to create the file") + ": " + self.iniFile) return False if not self.FD: self.setError(_("Failed to write to file") + ": " + self.iniFile) return False self.FD.truncate(0) self.FD.seek(0) self.FD.write(txtConfig) self.FD.close() self.FD = None return True def setVar(self, strHeader, dictVar): """Заменяет или добавляет область и переменные Добавляет область в ini-файл или объединяет с существующей strHeader - имя области dictVar - словарь переменных """ textIni = self.openRWIniFile() nameFomat = self.checkIniFile(textIni) if not nameFomat: return False if type(strHeader) in (tuple, list): # формат plasma classObj = self.getClassObj("plasma") else: if nameFomat == "plasma": self.setError(_("Trying to write a variable of 'samba' " "format to file %s ('plasma' format)")\ %self.iniFile) return False # формат samba classObj = self.getClassObj("samba") # создаем объект # и записываем в него содержимое ini-файла objIni = classObj(textIni) # создаем текст из строки заголовка и # словаря переменных области txtConfig = objIni.createTxtConfig(strHeader, dictVar) # создаем объект и записываем в него текст objIniAdd = classObj(txtConfig) # объединяем объекты для получения результирующего текста objIni.join(objIniAdd) # получаем текст txtConfig = objIni.getConfig().encode("UTF-8") # записываем его в ini файл if not self.writeIniFile(txtConfig): return False return True def isEmptyFile(self, textIni): """Если файл пустой или содержит только комментарии - False иначе - True """ if textIni.strip(): if filter(lambda x: x.strip(), map(lambda x:x[0].split(";")[0], map(lambda x: x.split("#"), textIni.splitlines()))): return False else: return True else: return True def checkIniFile(self, textIni): """Проверка на правильность формата файла""" if self.checkIni is None: # Ошибка if textIni == False: self.checkIni = False return False self.checkIni = "samba" # В файле есть данные if not self.isEmptyFile(textIni): try: objIni = self.getClassObj("plasma")(textIni) except: self.setError(_("Incorrect file format") + ": " + \ self.iniFile) self.checkIni = False return self.checkIni allAreas = objIni.docObj.getAllAreas() for xmlArea in allAreas: parentNode = xmlArea.parentNode if parentNode and parentNode.tagName == "area": self.checkIni = "plasma" break if self.checkIni == "samba": objIni = self.getClassObj("samba")(textIni) xmlBody = objIni.docObj.getNodeBody() if not xmlBody.firstChild: self.checkIni = False return self.checkIni def delVar(self, strHeader, nameVar): """Удаляем переменную из ini файла""" delNameVar = "!%s" %(nameVar) dictVar = {delNameVar:"del"} res = self.setVar(strHeader, dictVar) return res def delArea(self, strHeader): """Удаляем область из ini файла""" if type(strHeader) in (tuple, list): # Формат plasma delStrHeader = list(strHeader[:]) delStrHeader[-1] = "!%s"%delStrHeader[-1] else: # Формат samba delStrHeader = "!%s" %(strHeader) dictVar = {"del":"del"} res = self.setVar(delStrHeader, dictVar) return res def getVar(self, strHeader, nameVar, checkExistVar=False): """Получаем значение переменной из ini-файла""" textIni = self.openIniFile() if self.FD: self.FD.close() self.FD = None nameFomat = self.checkIniFile(textIni) if not nameFomat: return False formatPlasma = False if type(strHeader) in (tuple, list): # формат plasma classObj = self.getClassObj("plasma") formatPlasma = True else: if nameFomat == "plasma": self.setError(_("Trying to fetch a variable of 'samba' " "format from file %s ('plasma' format)")\ %self.iniFile) return False # формат samba classObj = self.getClassObj("samba") # создаем объект и записываем в него содержимое ini-файла objIni = classObj(textIni) # получаем ноду body xmlBody = objIni.docObj.getNodeBody() flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader, formatPlasma) if flagFound and xmlBody: if formatPlasma: strHeader = strHeader[-1] # находим в области переменную res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody) else: res = False if checkExistVar: if res is False: return False, "" else: return True, res else: if res is False: return "" else: return res def getLastNode(self, objIni, xmlBody, strHeader, formatPlasma): """Ищет область в XML в которой область с переменными""" flagFound = True if not strHeader: flagFound = False return flagFound,xmlBody lenStrHeader = len(strHeader) if formatPlasma and lenStrHeader>0: xmlAreas = [xmlBody] for i in xrange(lenStrHeader-1): flagFound = False for xmlArea in xmlAreas: xmlAreas = objIni.docObj.getArea(strHeader[i], xmlArea) if xmlAreas: flagFound = True break if xmlAreas: xmlBody = xmlAreas[0] return flagFound,xmlBody def getAreaVars(self, strHeader): """Получаем все переменнные области из ini-файла""" textIni = self.openIniFile() if self.FD: self.FD.close() self.FD = None nameFomat = self.checkIniFile(textIni) if not nameFomat: return False formatPlasma = False if type(strHeader) in (tuple, list): # формат plasma classObj = self.getClassObj("plasma") formatPlasma = True else: if nameFomat == "plasma": self.setError(_("Trying to fetch a variable of 'samba' " "format from file %s ('plasma' format)")\ %self.iniFile) return False # формат samba classObj = self.getClassObj("samba") # создаем объект типа samba и записываем в него содержимое ini-файла objIni = classObj(textIni) # получаем ноду body xmlBody = objIni.docObj.getNodeBody() flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader, formatPlasma) if flagFound and xmlBody: if formatPlasma: strHeader = strHeader[-1] # если находим область то выдаем словарем все переменные иначе False res = objIni.docObj.getAreaFields(strHeader, xmlBody, allVars=True) else: res = False if res is False: return {} else: return res def getAllSectionNames(self): """Получаем все имена секций определенных в ini файле Если формат ini файла plasma то имя секции - имена нескольких секций через запятую """ textIni = self.openIniFile() if self.FD: self.FD.close() self.FD = None nameFomat = self.checkIniFile(textIni) if not nameFomat: return False if nameFomat == "samba": # создаем объект типа samba и записываем в него содержимое ini-файла objIni = self.getClassObj("samba")(textIni) elif nameFomat == "plasma": # создаем объект типа plasma и записываем в него содержимое # ini-файла objIni = self.getClassObj("plasma")(textIni) else: return [] xmlNodes = objIni.docObj.getAllAreas() # Имена секций ini файла namesSection = [] if nameFomat == "plasma": for xmlNode in xmlNodes: nSect = objIni.docObj.getNameArea(xmlNode) if nSect: namesSect = [nSect] parentNode = xmlNode.parentNode while parentNode != objIni.docObj.body: nameSect = objIni.docObj.getNameArea(parentNode) if nameSect: namesSect.append(nameSect) parentNode = parentNode.parentNode else: return [] namesSection.append(",".join(reversed(namesSect))) elif nameFomat == "samba": # получаем ноду body for xmlNode in xmlNodes: nSect = objIni.docObj.getNameArea(xmlNode) if nSect: namesSection.append(nSect) return namesSection class ProgressTemplate(Template): """ Progress template for wsdl interface """ def __init__(self, setValueCallback, *args, **kwargs): Template.__init__(self, *args, **kwargs) self.setValueCallback = setValueCallback self.value = None self.firstValue = True def numberAllTemplates(self, number): self.maximum = number return True def numberProcessTemplates(self,number): maximum = self.maximum or 1 value = number * 100 / maximum; if value != self.value: self.setValueCallback(min(100,max(0,value))) self.value = value return True def templateModify(self): if self.firstValue and hasattr(self, "onFirstValue"): self.onFirstValue() self.firstValue = False class SystemIni(iniParser): inifile = '/etc/calculate/ini.env' def __init__(self): super(SystemIni, self).__init__(self.inifile)