|
|
|
@ -30,6 +30,7 @@ import random
|
|
|
|
|
import string
|
|
|
|
|
import time
|
|
|
|
|
import glob
|
|
|
|
|
import hashlib
|
|
|
|
|
import fcntl
|
|
|
|
|
from itertools import *
|
|
|
|
|
# < <= == != >= >
|
|
|
|
@ -38,7 +39,7 @@ from operator import lt, le, eq, ne, ge, gt
|
|
|
|
|
from utils.common import _error, _warning
|
|
|
|
|
from utils.text import _toUNICODE, convertStrListDict
|
|
|
|
|
from utils.portage import isPkgInstalled,reVerSplitToPV
|
|
|
|
|
from utils.content import PkgContents
|
|
|
|
|
from utils.content import PkgContents,checkContents,getCfgFiles
|
|
|
|
|
from utils.files import (getModeFile, listDirectory,removeDir, typeFile,
|
|
|
|
|
scanDirectory,
|
|
|
|
|
pathJoin,readFile,readLinesFile,process,STDOUT)
|
|
|
|
@ -2232,7 +2233,12 @@ class _file(_error):
|
|
|
|
|
self.F_CONF = self.__openConfFile(self.nameFileConfig)
|
|
|
|
|
if self.F_TEMPL and self.F_CONF:
|
|
|
|
|
self.textTemplate = self.F_TEMPL.read()
|
|
|
|
|
self.textConfig = self.F_CONF.read()
|
|
|
|
|
if self.configMode == T_NEWCFG:
|
|
|
|
|
origConfigName = re.sub(r'/._cfg\d{4}_([^/]+)$','/\\1',
|
|
|
|
|
self.nameFileConfig)
|
|
|
|
|
self.textConfig = readFile(origConfigName)
|
|
|
|
|
else:
|
|
|
|
|
self.textConfig = self.F_CONF.read()
|
|
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
|
self.closeFiles()
|
|
|
|
@ -3775,6 +3781,12 @@ class ChangedFiles:
|
|
|
|
|
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):
|
|
|
|
|
"""Класс для работы с шаблонами
|
|
|
|
|
|
|
|
|
@ -3794,7 +3806,10 @@ class Template(_file,_terms,_warning,xmlShare,templateFormat,_shareTemplate):
|
|
|
|
|
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):
|
|
|
|
|
printERROR=lambda x:x,askConfirm=lambda x:x,
|
|
|
|
|
userProfile=False,dispatchConf=None):
|
|
|
|
|
self.userProfile = userProfile
|
|
|
|
|
self.dispatchConf = dispatchConf
|
|
|
|
|
self.changedFiles = ChangedFiles()
|
|
|
|
|
self.printSUCCESS = printSUCCESS
|
|
|
|
|
self.printERROR = printERROR
|
|
|
|
@ -4196,6 +4211,7 @@ gettext -d cl_template "$*"
|
|
|
|
|
origstat = os.stat(origfilename)[stat.ST_CTIME]
|
|
|
|
|
newstat = os.stat(filename)[stat.ST_CTIME]
|
|
|
|
|
if newstat > origstat:
|
|
|
|
|
self.configMode = T_CFG
|
|
|
|
|
return filename
|
|
|
|
|
return origfilename
|
|
|
|
|
return origfilename
|
|
|
|
@ -4263,8 +4279,8 @@ gettext -d cl_template "$*"
|
|
|
|
|
if skipDirs or skipTemplates:
|
|
|
|
|
# print warning
|
|
|
|
|
from cl_print import color_print
|
|
|
|
|
self.printWARNING(_("No conditions for checking the value of variable"
|
|
|
|
|
" 'cl_name'"))
|
|
|
|
|
self.printWARNING(_("No conditions for checking the value of "
|
|
|
|
|
"an action variable"))
|
|
|
|
|
skipDirTemplates = []
|
|
|
|
|
for skipDir in skipDirs:
|
|
|
|
|
skipTempl = os.path.join(skipDir,self.templDirNameFile)
|
|
|
|
@ -4281,9 +4297,9 @@ gettext -d cl_template "$*"
|
|
|
|
|
setWARNING("")
|
|
|
|
|
setWARNING(_("Headers of directory templates and headers "
|
|
|
|
|
"of files on the first level should include "
|
|
|
|
|
"the 'cl_name' variable or an action variable."))
|
|
|
|
|
"an action variable."))
|
|
|
|
|
setWARNING(_("Example:"))
|
|
|
|
|
setWARNING("# Calculate cl_name==calculate-install")
|
|
|
|
|
setWARNING("# Calculate ac_install_merge==on")
|
|
|
|
|
return skipDirs + skipTemplates
|
|
|
|
|
|
|
|
|
|
def applyTemplates(self,progress=True,rerun=True):
|
|
|
|
@ -4449,8 +4465,76 @@ gettext -d cl_template "$*"
|
|
|
|
|
nameTemplate)
|
|
|
|
|
return False
|
|
|
|
|
self.queueExecute = []
|
|
|
|
|
if not self.userProfile:
|
|
|
|
|
self.updateProtectedFiles()
|
|
|
|
|
return (self.createdDirs, self.filesApply)
|
|
|
|
|
|
|
|
|
|
def updateProtectedFiles(self):
|
|
|
|
|
"""
|
|
|
|
|
Update ._cfg0000 files
|
|
|
|
|
"""
|
|
|
|
|
chrootPath = self.objVar.Get('cl_chroot_path')
|
|
|
|
|
cfgs = getCfgFiles(self.objVar.Get('cl_config_protect'),
|
|
|
|
|
prefix=chrootPath)
|
|
|
|
|
autoUpdateDict = {}
|
|
|
|
|
for pkg in list(set(self.changedFiles.getPkgs())):
|
|
|
|
|
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_autoupdate_set') == 'off':
|
|
|
|
|
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)
|
|
|
|
|
print "Added ",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)
|
|
|
|
|
pkgContents.writeContents()
|
|
|
|
|
self.filesApply = map(lambda x:autoUpdateDict.get(x,x),self.filesApply)
|
|
|
|
|
if filter(lambda x:"._cfg" in x, self.filesApply):
|
|
|
|
|
self.printWARNING(_("Some config files need updating. Perform dispatch-conf."))
|
|
|
|
|
if self.dispatchConf:
|
|
|
|
|
self.dispatchConf(self.filesApply)
|
|
|
|
|
|
|
|
|
|
def scanningTemplates(self, scanDir, prefix=None, flagDir=False,
|
|
|
|
|
optDir={}, skipTemplates=[]):
|
|
|
|
@ -4882,6 +4966,41 @@ gettext -d cl_template "$*"
|
|
|
|
|
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:
|
|
|
|
|
return pathFile
|
|
|
|
|
pkg = isPkgInstalled(pkg,sortByVersion=True,prefix=chrootPath)
|
|
|
|
|
if not pkg:
|
|
|
|
|
return pathFile
|
|
|
|
|
if checkContents("{CATEGORY}/{PF}".format(**pkg[-1]),
|
|
|
|
|
pathFile,
|
|
|
|
|
prefix=chrootPath):
|
|
|
|
|
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):
|
|
|
|
|
"""Применяет заголовок к шаблону (права, владелец, и.т. д)"""
|
|
|
|
@ -5011,7 +5130,10 @@ gettext -d cl_template "$*"
|
|
|
|
|
pathOldFile = pathJoin(path,nameFile)
|
|
|
|
|
else:
|
|
|
|
|
pathOldFile = pathJoin(path,os.path.split(nameFileConfig)[1])
|
|
|
|
|
pathOldFile = self.fixNameFileConfig(pathOldFile)
|
|
|
|
|
pathOrigFile = pathOldFile
|
|
|
|
|
if not self.userProfile:
|
|
|
|
|
pathOldFile = self.fixNameFileConfig(pathOldFile)
|
|
|
|
|
pathOldFile = self.checkOnNewConfigName(pathOldFile)
|
|
|
|
|
applyFiles = [pathOldFile]
|
|
|
|
|
# Фильтрация шаблонов по названию файла
|
|
|
|
|
realPath = os.path.join("/",pathOldFile.partition(self._baseDir)[2])
|
|
|
|
@ -5037,7 +5159,7 @@ gettext -d cl_template "$*"
|
|
|
|
|
": " +\
|
|
|
|
|
nameFileTemplate)
|
|
|
|
|
return ([], False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Очищаем оригинальный файл
|
|
|
|
|
if typeAppendTemplate == "clear":
|
|
|
|
|
try:
|
|
|
|
@ -5050,27 +5172,31 @@ gettext -d cl_template "$*"
|
|
|
|
|
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):
|
|
|
|
|
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 (applyFiles, False)
|
|
|
|
|
return ([], False)
|
|
|
|
|
# Пропускаем обработку шаблона
|
|
|
|
|
elif typeAppendTemplate == "skip":
|
|
|
|
|
return ([], False)
|
|
|
|
@ -5370,6 +5496,7 @@ gettext -d cl_template "$*"
|
|
|
|
|
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
|
|
|
|
|