summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Cfg.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugins/Cfg.py')
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg.py293
1 files changed, 0 insertions, 293 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg.py b/src/lib/Bcfg2/Server/Plugins/Cfg.py
deleted file mode 100644
index 81904d082..000000000
--- a/src/lib/Bcfg2/Server/Plugins/Cfg.py
+++ /dev/null
@@ -1,293 +0,0 @@
-"""This module implements a config file repository."""
-
-import binascii
-import logging
-import lxml
-import operator
-import os
-import os.path
-import re
-import stat
-import sys
-import tempfile
-from subprocess import Popen, PIPE
-from Bcfg2.Bcfg2Py3k import u_str
-
-import Bcfg2.Server.Plugin
-
-try:
- import genshi.core
- import genshi.input
- from genshi.template import TemplateLoader, NewTextTemplate
- have_genshi = True
-except:
- have_genshi = False
-
-try:
- import Cheetah.Template
- import Cheetah.Parser
- have_cheetah = True
-except:
- have_cheetah = False
-
-# setup logging
-logger = logging.getLogger('Bcfg2.Plugins.Cfg')
-
-
-# snipped from TGenshi
-def removecomment(stream):
- """A genshi filter that removes comments from the stream."""
- for kind, data, pos in stream:
- if kind is genshi.core.COMMENT:
- continue
- yield kind, data, pos
-
-
-def process_delta(data, delta):
- if not delta.specific.delta:
- return data
- if delta.specific.delta == 'cat':
- datalines = data.strip().split('\n')
- for line in delta.data.split('\n'):
- if not line:
- continue
- if line[0] == '+':
- datalines.append(line[1:])
- elif line[0] == '-':
- if line[1:] in datalines:
- datalines.remove(line[1:])
- return "\n".join(datalines) + "\n"
- elif delta.specific.delta == 'diff':
- basehandle, basename = tempfile.mkstemp()
- basefile = open(basename, 'w')
- basefile.write(data)
- basefile.close()
- os.close(basehandle)
-
- cmd = ["patch", "-u", "-f", basefile.name]
- patch = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- stderr = patch.communicate(input=delta.data)[1]
- ret = patch.wait()
- output = open(basefile.name, 'r').read()
- os.unlink(basefile.name)
- if ret >> 8 != 0:
- logger.error("Error applying diff %s: %s" % (delta.name, stderr))
- raise Bcfg2.Server.Plugin.PluginExecutionError('delta', delta)
- return output
-
-
-class CfgMatcher:
-
- def __init__(self, fname):
- name = re.escape(fname)
- self.basefile_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+?)|.G(?P<prio>\d+)_(?P<group>\S+?))((?P<genshi>\\.genshi)|(?P<cheetah>\\.cheetah))?$' % name)
- self.delta_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+)|\\.G(?P<prio>\d+)_(?P<group>\S+))\\.(?P<delta>(cat|diff))$' % name)
- self.cat_count = fname.count(".cat")
- self.diff_count = fname.count(".diff")
-
- def match(self, fname):
- if fname.count(".cat") > self.cat_count \
- or fname.count('.diff') > self.diff_count:
- return self.delta_reg.match(fname)
- return self.basefile_reg.match(fname)
-
-
-class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
-
- def __init__(self, basename, path, entry_type, encoding):
- Bcfg2.Server.Plugin.EntrySet.__init__(self, basename, path,
- entry_type, encoding)
- self.specific = CfgMatcher(path.split('/')[-1])
- path = path
-
- def debug_log(self, message, flag=None):
- if (flag is None and self.debug_flag) or flag:
- logger.error(message)
-
- def sort_by_specific(self, one, other):
- return cmp(one.specific, other.specific)
-
- def get_pertinent_entries(self, entry, metadata):
- """return a list of all entries pertinent
- to a client => [base, delta1, delta2]
- """
- matching = [ent for ent in list(self.entries.values()) if \
- ent.specific.matches(metadata)]
- matching.sort(key=operator.attrgetter('specific'))
- # base entries which apply to a client
- # (e.g. foo, foo.G##_groupname, foo.H_hostname)
- base_files = [matching.index(m) for m in matching
- if not m.specific.delta]
- if not base_files:
- msg = "No base file found for %s" % entry.get('name')
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- base = min(base_files)
- used = matching[:base + 1]
- used.reverse()
- return used
-
- def bind_entry(self, entry, metadata):
- self.bind_info_to_entry(entry, metadata)
- used = self.get_pertinent_entries(entry, metadata)
- basefile = used.pop(0)
- if entry.get('perms').lower() == 'inherit':
- # use on-disk permissions
- fname = os.path.join(self.path, entry.get('name'))
- entry.set('perms',
- str(oct(stat.S_IMODE(os.stat(fname).st_mode))))
- if entry.tag == 'Path':
- entry.set('type', 'file')
- if basefile.name.endswith(".genshi"):
- if not have_genshi:
- msg = "Cfg: Genshi is not available: %s" % entry.get("name")
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- try:
- template_cls = NewTextTemplate
- loader = TemplateLoader()
- template = loader.load(basefile.name, cls=template_cls,
- encoding=self.encoding)
- fname = entry.get('realname', entry.get('name'))
- stream = template.generate(name=fname,
- metadata=metadata,
- path=basefile.name).filter(removecomment)
- try:
- data = stream.render('text', encoding=self.encoding,
- strip_whitespace=False)
- except TypeError:
- data = stream.render('text', encoding=self.encoding)
- if data == '':
- entry.set('empty', 'true')
- except Exception:
- msg = "Cfg: genshi exception (%s): %s" % (entry.get("name"),
- sys.exc_info()[1])
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- elif basefile.name.endswith(".cheetah"):
- if not have_cheetah:
- msg = "Cfg: Cheetah is not available: %s" % entry.get("name")
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- try:
- fname = entry.get('realname', entry.get('name'))
- s = {'useStackFrames': False}
- template = Cheetah.Template.Template(open(basefile.name).read(),
- compilerSettings=s)
- template.metadata = metadata
- template.path = fname
- template.source_path = basefile.name
- data = template.respond()
- if data == '':
- entry.set('empty', 'true')
- except Exception:
- msg = "Cfg: cheetah exception (%s): %s" % (entry.get("name"),
- sys.exc_info()[1])
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- else:
- data = basefile.data
- for delta in used:
- data = process_delta(data, delta)
- if entry.get('encoding') == 'base64':
- entry.text = binascii.b2a_base64(data)
- else:
- try:
- entry.text = u_str(data, self.encoding)
- except UnicodeDecodeError:
- msg = "Failed to decode %s: %s" % (entry.get('name'),
- sys.exc_info()[1])
- logger.error(msg)
- logger.error("Please verify you are using the proper encoding.")
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- except ValueError:
- msg = "Error in specification for %s: %s" % (entry.get('name'),
- sys.exc_info()[1])
- logger.error(msg)
- logger.error("You need to specify base64 encoding for %s." %
- entry.get('name'))
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- if entry.text in ['', None]:
- entry.set('empty', 'true')
-
- def list_accept_choices(self, entry, metadata):
- '''return a list of candidate pull locations'''
- used = self.get_pertinent_entries(entry, metadata)
- ret = []
- if used:
- ret.append(used[0].specific)
- if not ret[0].hostname:
- ret.append(Bcfg2.Server.Plugin.Specificity(hostname=metadata.hostname))
- return ret
-
- def build_filename(self, specific):
- bfname = self.path + '/' + self.path.split('/')[-1]
- if specific.all:
- return bfname
- elif specific.group:
- return "%s.G%02d_%s" % (bfname, specific.prio, specific.group)
- elif specific.hostname:
- return "%s.H_%s" % (bfname, specific.hostname)
-
- def write_update(self, specific, new_entry, log):
- if 'text' in new_entry:
- name = self.build_filename(specific)
- if os.path.exists("%s.genshi" % name):
- msg = "Cfg: Unable to pull data for genshi types"
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- elif os.path.exists("%s.cheetah" % name):
- msg = "Cfg: Unable to pull data for cheetah types"
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- try:
- etext = new_entry['text'].encode(self.encoding)
- except:
- msg = "Cfg: Cannot encode content of %s as %s" % (name,
- self.encoding)
- logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- open(name, 'w').write(etext)
- self.debug_log("Wrote file %s" % name, flag=log)
- badattr = [attr for attr in ['owner', 'group', 'perms']
- if attr in new_entry]
- if badattr:
- # check for info files and inform user of their removal
- if os.path.exists(self.path + "/:info"):
- logger.info("Removing :info file and replacing with "
- "info.xml")
- os.remove(self.path + "/:info")
- if os.path.exists(self.path + "/info"):
- logger.info("Removing info file and replacing with "
- "info.xml")
- os.remove(self.path + "/info")
- metadata_updates = {}
- metadata_updates.update(self.metadata)
- for attr in badattr:
- metadata_updates[attr] = new_entry.get(attr)
- infoxml = lxml.etree.Element('FileInfo')
- infotag = lxml.etree.SubElement(infoxml, 'Info')
- [infotag.attrib.__setitem__(attr, metadata_updates[attr]) \
- for attr in metadata_updates]
- ofile = open(self.path + "/info.xml", "w")
- ofile.write(lxml.etree.tostring(infoxml, pretty_print=True))
- ofile.close()
- self.debug_log("Wrote file %s" % (self.path + "/info.xml"),
- flag=log)
-
-
-class Cfg(Bcfg2.Server.Plugin.GroupSpool,
- Bcfg2.Server.Plugin.PullTarget):
- """This generator in the configuration file repository for Bcfg2."""
- name = 'Cfg'
- __author__ = 'bcfg-dev@mcs.anl.gov'
- es_cls = CfgEntrySet
- es_child_cls = Bcfg2.Server.Plugin.SpecificData
-
- def AcceptChoices(self, entry, metadata):
- return self.entries[entry.get('name')].list_accept_choices(entry, metadata)
-
- def AcceptPullData(self, specific, new_entry, log):
- return self.entries[new_entry.get('name')].write_update(specific,
- new_entry,
- log)