summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNarayan Desai <desai@mcs.anl.gov>2007-07-18 19:00:57 +0000
committerNarayan Desai <desai@mcs.anl.gov>2007-07-18 19:00:57 +0000
commitfd787917bdcd0aaf5f5b0b99dfa5e2a992d734a4 (patch)
tree0f71d021b49cf0698baabee9ba91a9b957d29318
parente21264fd11c7f2e32572467403fda08b0c94fe6d (diff)
downloadbcfg2-fd787917bdcd0aaf5f5b0b99dfa5e2a992d734a4.tar.gz
bcfg2-fd787917bdcd0aaf5f5b0b99dfa5e2a992d734a4.tar.bz2
bcfg2-fd787917bdcd0aaf5f5b0b99dfa5e2a992d734a4.zip
Initial TGenshi checking (from jcollie)
git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@3476 ce84e21b-d406-0410-9b95-82705330c041
-rw-r--r--examples/TGenshi/tmp/bar/template.txt20
-rw-r--r--examples/TGenshi/tmp/foo/template.xml46
-rw-r--r--src/lib/Server/Plugins/TGenshi.py145
-rw-r--r--src/lib/Server/Plugins/__init__.py3
4 files changed, 213 insertions, 1 deletions
diff --git a/examples/TGenshi/tmp/bar/template.txt b/examples/TGenshi/tmp/bar/template.txt
new file mode 100644
index 000000000..3e43340fe
--- /dev/null
+++ b/examples/TGenshi/tmp/bar/template.txt
@@ -0,0 +1,20 @@
+[communication]
+protocol = xmlrpc/ssl
+#if metadata.uuid != None
+user = $metadata.uuid
+#end
+#choose
+#when metadata.password is not None
+password = $metadata.password
+#end
+#when metadata.password is None
+password = GlobalPassword
+#end
+#end
+fingerprint = ac152f42f03253a30d3379dea88eddf2be033d47
+
+[client]
+drivers = Action,Chkconfig,POSIX,YUMng
+
+[components]
+bcfg2 = https://config.example.com:6789
diff --git a/examples/TGenshi/tmp/foo/template.xml b/examples/TGenshi/tmp/foo/template.xml
new file mode 100644
index 000000000..522c6e7fe
--- /dev/null
+++ b/examples/TGenshi/tmp/foo/template.xml
@@ -0,0 +1,46 @@
+<html xmlns:py="http://genshi.edgewall.org/">
+ <head>
+ <title>${name}</title>
+ </head>
+ <body>
+ <table>
+ <tr><th>Name:</th><td>${name}</td></tr>
+ <tr><th>Hostname:</th><td>${metadata.hostname}</td></tr>
+ <tr><th>Toolset:</th><td>${metadata.hostname}</td></tr>
+ <tr><th>UUID:</th><td>${metadata.uuid}</td></tr>
+ <tr><th>Password:</th><td>${metadata.password}</td></tr>
+ <tr>
+ <th>Bundles:</th>
+ <td>
+ <table>
+ <tr py:for="bundle in metadata.bundles"><td>${bundle}</td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>Groups:</th>
+ <td>
+ <table>
+ <tr py:for="group in metadata.groups"><td>${group}</td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>Categories:</th>
+ <td>
+ <table>
+ <tr py:for="category in metadata.categories"><td>${category}</td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>Probes:</th>
+ <td>
+ <table>
+ <tr py:for="probe in metadata.probes"><td>${probe}</td><td>${metadata.probes[probe]}</td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/src/lib/Server/Plugins/TGenshi.py b/src/lib/Server/Plugins/TGenshi.py
new file mode 100644
index 000000000..da6dcf956
--- /dev/null
+++ b/src/lib/Server/Plugins/TGenshi.py
@@ -0,0 +1,145 @@
+'''This module implements a templating generator based on Genshi'''
+__revision__ = '$Revision$'
+
+from genshi.template import TemplateLoader, TextTemplate, MarkupTemplate, TemplateError
+import logging, lxml.etree, posixpath, re, os
+import Bcfg2.Server.Plugin
+
+logger = logging.getLogger('Bcfg2.Plugins.TGenshi')
+info = re.compile('^owner:(\s)*(?P<owner>\w+)$|group:(\s)*(?P<group>\w+)$|' +
+ 'perms:(\s)*(?P<perms>\w+)$')
+
+class TemplateFile:
+ '''Template file creates Genshi template structures for the loaded file'''
+ def __init__(self, name, loader, properties):
+ self.name = name
+ self.states = {'template': False, 'info': False}
+ self.metadata = {'owner': 'root', 'group': 'root', 'perms': '0644'}
+ self.properties = properties
+ self.loader = loader
+
+ def HandleEvent(self, event):
+ '''Handle all fs events for this template'''
+ if event.filename in ['template.xml', 'template.txt']:
+ try:
+ if event.filename.endswith('.txt'):
+ self.template = self.loader.load(os.path.join(self.name[1:], event.filename), cls=TextTemplate)
+ else:
+ self.template = self.loader.load(os.path.join(self.name[1:], event.filename), cls=MarkupTemplate)
+ except TemplateError, terror:
+ logger.error('Genshi template error: %s' % terror)
+
+ elif event.filename == 'info':
+ for line in open(self.name + '/info').readlines():
+ match = info.match(line)
+ if not match:
+ logger.warning("Failed to match line: %s"%line)
+ continue
+ else:
+ mgd = match.groupdict()
+ if mgd['owner']:
+ self.metadata['owner'] = mgd['owner']
+ elif mgd['group']:
+ self.metadata['group'] = mgd['group']
+ elif mgd['perms']:
+ self.metadata['perms'] = mgd['perms']
+ if len(self.metadata['perms']) == 3:
+ self.metadata['perms'] = "0%s" % (self.metadata['perms'])
+ else:
+ logger.info('Ignoring event for %s' % event.filename)
+
+ def BuildFile(self, entry, metadata):
+ '''Build literal file information'''
+ try:
+ stream = self.template.generate(name=entry.get('name'), metadata=metadata, properties=self.properties)
+ if isinstance(self.template, TextTemplate):
+ entry.text = stream.render('text')
+ else:
+ entry.text = stream.render('xml')
+ except TemplateError, terror:
+ logger.error('Genshi template error: %s' % terror)
+ raise Bcfg2.Server.Plugin.PluginExecutionError
+ [entry.attrib.__setitem__(key, value) for (key, value) in self.metadata.iteritems()]
+
+class GenshiProperties(Bcfg2.Server.Plugin.SingleXMLFileBacked):
+ '''Class for Genshi properties'''
+ def Index(self):
+ '''Build data into an elementtree object for templating usage'''
+ try:
+ self.properties = lxml.etree.XML(self.data)
+ del self.data
+ except lxml.etree.XMLSyntaxError:
+ logger.error("Failed to parse properties")
+
+class FakeProperties:
+ '''Dummy class used when properties dont exist'''
+ def __init__(self):
+ self.properties = lxml.etree.Element("Properties")
+
+class TGenshi(Bcfg2.Server.Plugin.Plugin):
+ '''The TGenshi generator implements a templating mechanism for configuration files'''
+ __name__ = 'TGenshi'
+ __version__ = '$Id$'
+ __author__ = 'jeff@ocjtech.us'
+
+ def __init__(self, core, datastore):
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ if self.data[-1] == '/':
+ self.data = self.data[:-1]
+ self.Entries['ConfigFile'] = {}
+ self.entries = {}
+ self.handles = {}
+ self.loader = TemplateLoader(self.data, auto_reload=True)
+ self.AddDirectoryMonitor('')
+ try:
+ self.properties = GenshiProperties('%s/../etc/properties.xml' \
+ % (self.data), self.core.fam)
+ except:
+ self.properties = FakeProperties()
+ self.logger.info("Failed to read properties file; TGenshi properties disabled")
+
+ def BuildEntry(self, entry, metadata):
+ '''Dispatch fetch calls to the correct object'''
+ self.entries[entry.get('name')].BuildFile(entry, metadata)
+
+ def HandleEvent(self, event):
+ '''Unified FAM event handler for DirShadow'''
+ action = event.code2str()
+ if event.filename[0] == '/':
+ return
+ epath = "".join([self.data, self.handles[event.requestID], event.filename])
+ if event.filename in ['info', 'template.xml', 'template.txt']:
+ identifier = self.handles[event.requestID][:-1]
+ else:
+ identifier = self.handles[event.requestID] + event.filename
+ if action in ['exists', 'created']:
+ if posixpath.isdir(epath):
+ self.AddDirectoryMonitor(epath[len(self.data):])
+ elif event.filename in ['info', 'template.xml', 'template.txt']:
+ if not self.entries.has_key(identifier):
+ self.entries[identifier] = TemplateFile(identifier, self.loader, self.properties)
+ self.Entries['ConfigFile'][identifier] = self.BuildEntry
+ self.entries[identifier].HandleEvent(event)
+ else:
+ logger.info('Not creating template for %s' % identifier)
+ elif action == 'changed':
+ if self.entries.has_key(identifier):
+ self.entries[identifier].HandleEvent(event)
+ elif action == 'deleted':
+ if event.filename in ['template.xml', 'template.txt'] and self.entries.has_key(identifier):
+ del self.entries[identifier]
+ del self.Entries['ConfigFile'][identifier]
+
+ def AddDirectoryMonitor(self, relative):
+ '''Add new directory to FAM structures'''
+ if not relative:
+ relative = '/'
+ if relative[-1] != '/':
+ relative += '/'
+ name = self.data + relative
+ if relative not in self.handles.values():
+ if not posixpath.isdir(name):
+ print "Genshi: Failed to open directory %s" % (name)
+ return
+ reqid = self.core.fam.AddMonitor(name, self)
+ self.handles[reqid] = relative
diff --git a/src/lib/Server/Plugins/__init__.py b/src/lib/Server/Plugins/__init__.py
index f863fdce0..484162451 100644
--- a/src/lib/Server/Plugins/__init__.py
+++ b/src/lib/Server/Plugins/__init__.py
@@ -2,4 +2,5 @@
__revision__ = '$Revision$'
all = ['Account', 'Base', 'Bundler', 'Cfg', 'Hostbase', 'Metadata',
- 'Pkgmgr', 'Rules', 'SSHbase', 'Svcmgr', 'Vhost']
+ 'Pkgmgr', 'Rules', 'SSHbase', 'Svcmgr', 'TCheetah',
+ 'TGenshi', 'Vhost']