From 38cabbcb8c089002fbd5e5759fee5fffb0cf44e9 Mon Sep 17 00:00:00 2001 From: Narayan Desai Date: Wed, 9 Jun 2004 18:36:20 +0000 Subject: fix Metadata usage 2004/06/08 10:04:41-05:00 anl.gov!desai add name argument 2004/06/08 09:48:59-05:00 anl.gov!desai success? 2004/06/04 15:35:30-05:00 anl.gov!desai get complete change notifications working for the repo (Logical change 1.21) git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@86 ce84e21b-d406-0410-9b95-82705330c041 --- generators/cfg.py | 203 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 166 insertions(+), 37 deletions(-) (limited to 'generators') diff --git a/generators/cfg.py b/generators/cfg.py index 2417c9202..3e6590260 100644 --- a/generators/cfg.py +++ b/generators/cfg.py @@ -1,58 +1,187 @@ #!/usr/bin/env python -from os import stat, listdir +from os import stat from re import compile -from stat import S_ISDIR, ST_MODE, ST_MTIME +from stat import S_ISDIR, ST_MODE +from string import join from Types import ConfigFile from Generator import Generator from GeneratorUtils import DirectoryBacked, FileBacked +from Metadata import Metadata -class ConfigFragment(object): - def __init__(self,filename,datafile): - self.re = compile("\S+%s(.(?P[BTH])(?P\d)+_(?P\S+)(.(?Pcat|udiff))?)?"%(filename)) - m = self.re.match(datafile) - self.type = m.group('type') - if self.type: - self.prio = m.group('prio') - self.data = m.group('data') - self.op = m.group('op') - else: - self.type='G' - self.data = file(datafile).read() - self.mtime = stat(datafile)[ST_MTIME] - -class ConfigFileRepository(DirectoryBacked): - fragment = compile("(^:info$|^(?P.*)(\.((B(?P\d+)_(?P\S+))|(T(?P\d+)_(?P\S+))(I(?P\d+)_(?P\S+))|(H_(?P\S+)))(\.(?Pcat|udiff))?)?$)") +class FileEntry(FileBacked): + '''The File Entry class pertains to the config files contained in a particular directory. + This includes :info, all base files and deltas''' + + def __init__(self, name, metadata): + FileBacked.__init__(self, name) + self.metadata = metadata - def __index__(self): - pass + def Applies(self, other): + return self.metadata.Applies(other) -class Repository(object): - def __init__(self,path): - # we need to figure out when to rerun this code + def __cmp__(self, other): + '''figure out if self is more or less specific than other''' + return self.metadata.__cmp__(other.metadata) + +class ConfigFileEntry(object): + mx = compile("(^(?P.*)(\.((B(?P\d+)_(?P\S+))|(T(?P\d+)_(?P\S+))(I(?P\d+)_(?P\S+))|(H_(?P\S+)))(\.(?Pcat|udiff))?)?$)") + info = compile('^owner:(\s)*(?P\w+)|group:(\s)*(?P\w+)|perms:(\s)*(?P\w+)|encoding:(\s)*(?P\w+)$') + + def __init__(self, path): self.path = path - dirs = [path] + self.basefiles = [] + self.deltas = [] + self.encoding = 'ascii' + self.owner = 'root' + self.group = 'root' + self.perms = '644' + + def GetInfo(self, filename): + for line in open(filename).readlines(): + m = self.info.match(line) + if not m: + continue + else: + d = m.groupdict() + if d['owner']: + self.owner=d['owner'] + elif d['group']: + self.group=d['group'] + elif d['encoding']: + self.encoding=d['encoding'] + elif d['perms']: + self.perms=d['perms'] + if len(self.perms) == 3: + self.perms="0%s"%(self.perms) + + def AddEntry(self, name): + if name[-5:] == ':info': + return self.GetInfo(name) + + g = self.mx.match(name) + if g == None: + print "match failed for file name %s"%(name) + return + + data = {} + for attr in ['bundle', 'tag', 'hostname']: + if g.group(attr) != None: data[attr] = g.group(attr) + if data == {}: + all = True + else: + all = False + arg = (all, 'linux') + tuple(map(lambda z:filter(lambda x:x, [data.get(z, None)]), ['bundle','tag'])) + \ + (data.get("hostname",None), ) + m = apply(Metadata, arg) + if g.group("op") != None: + self.deltas.append(FileEntry(name, m)) + # need to sort here + else: + self.basefiles.append(FileEntry(name, m)) + # need to sort here + + def HandleEvent(self, event): + action = event.code2str() + if event.filename == ':info': + return self.GetInfo(event.filename) + for l in [self.basefiles, self.deltas]: + for entry in l: + if entry.name.split('/')[-1] == event.filename: + if action == 'changed': + entry.HandleEvent(event) + elif action == 'deleted': + l.remove(entry) + else: + print "unhandled action %s"%(action) + + def GetConfigFile(self, name, metadata): + filedata = "" + # first find basefile + try: + basefile = filter(lambda x:x.Applies(metadata), self.basefiles)[0] + except IndexError: + raise CfgFileException, ('basefile', self.name) + filedata += basefile.data + + # find applicable deltas + deltas = filter(lambda x:x.Applies(metadata), self.deltas) + # filter for more specific + for delta in deltas: + pass + # apply diffs, etc + return ConfigFile(self.path, self.owner, self.group, self.perms, filedata, self.encoding) + +class ConfigFileRepository(DirectoryBacked): + '''This class implements repos and all change handling''' + + def __init__(self, name, fam): + self.name = name + self.fam = fam self.entries = {} - # we can locate file locations by searching for :info files - while dirs: - el = listdir(dirs[0]) - for entry in el: - s = stat("%s/%s"%(dirs[0],entry)) - if S_ISDIR(s[ST_MODE]): - dirs.append("%s/%s"%(dirs[0],entry)) - if ':info' in el: - print dirs[0] - self.entries[dirs[0][len(path):]] = DirectoryBacked(dirs[0]) - dirs = dirs[1:] + self.provides = {} + self.famID = {} + self.directories = [] + self.AddDirectoryMonitor(self.name) + # eventually flush fam events here so that all generators come out of constructors + # ready to go + + def AddDirectoryMonitor(self, name): + if name not in self.directories: + try: + stat(name) + except: + print "Failed to open %s"%(name) + return + id = self.fam.AddMonitor(name, self) + self.famID[id] = name + self.directories.append(name) + + def AddEntry(self, name): + if S_ISDIR(stat(name)[ST_MODE]): + self.AddDirectoryMonitor(name) + else: + # file entries shouldn't contain path-to-repo + shortname = '/'+ join(name[len(self.name)+1:].split('/')[:-1], '/') + if not self.entries.has_key(shortname): + self.entries[shortname] = ConfigFileEntry(shortname) + self.provides[shortname] = self.entries[shortname].GetConfigFile + self.entries[shortname].AddEntry(name) + #self.entries[shortname].HandleEvent() + + def HandleEvent(self, event): + action = event.code2str() + print "Got event %s %s %s"%(event.requestID, event.code2str(), event.filename) + if event.filename[0] != '/': + filename = "%s/%s"%(self.famID[event.requestID], event.filename) + else: + filename = event.filename + if action == 'exists': + if filename != self.name: + self.AddEntry(filename) + elif action == 'created': + self.AddEntry(filename) + elif action == 'changed': + # pass the event down the chain to the ConfigFileEntry + configfile = filename[len(self.name):-(len(event.filename)+1)] + self.entries[configfile].HandleEvent(event) + elif action == 'deleted': + configfile = filename[len(self.name):-(len(event.filename)+1)] + self.entries[configfile].HandleEvent(event) + elif action in ['endExist']: + pass + else: + print "Got unknown event %s %s %s"%(event.requestID, event.code2str(), event.filename) class cfg(Generator): __name__ = 'cfg' __version__ = '$Id$' __author__ = 'bcfg-dev@mcs.anl.gov' - __build__ = {} + __provides__ = {} def __setup__(self): - self.repo = Repository(self.data) + self.repo = ConfigFileRepository(self.data, self.core.fam) + self.__provides__['ConfigFile'] = self.repo.provides -- cgit v1.2.3-1-g7c22