Vieux plugin Python aux archives

This commit is contained in:
Pierre-Elliott Bécue 2015-05-23 16:11:34 +02:00
parent 3fb338e937
commit 1df9d480af
8 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,222 @@
# -*- mode: python; coding: utf-8 -*-
#
# Python.py
# ---------
#
# Copyright (C) 2007 Jeremie Dimino <jeremie@dimino.org>
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
''' Plugin pour bcfg2 pour générer des fichiers de conf en prenant la sortie d'un
script python'''
__all__ = ["Python"]
import logging, lxml.etree, posixpath, re, os, sys, binascii
from cStringIO import StringIO
import Bcfg2.Server.Plugin
import traceback
sys.path.append('/usr/scripts/bcfg2')
import pygen
logger = logging.getLogger('Bcfg2.Plugins.Python')
# Helper pour le deboggage
if os.getenv("BCFG2_DEBUG"):
debug_colored = os.getenv("BCFG2_DEBUG_COLOR")
color_code = {'grey': 30,
'red': 31,
'green': 32,
'yellow': 33,
'blue': 34,
'purple': 35,
'cyan': 36}
def debug(msg, color=None):
if debug_colored and color:
logger.info("\033[1;%dm%s\033[0m" % (color_code[color], msg))
else:
logger.info(msg)
else:
def debug(msg, color=None):
pass
# Dico nom de fichier -> code
includes = {}
def log_traceback(fname, section, exn):
logger.error('Python %s error: %s: %s: %s' % (section, fname, str(exn.__class__).split('.', 2)[1], str(exn)))
s = StringIO()
sys.stderr = s
traceback.print_exc()
sys.stderr = sys.__stderr__
for line in s.getvalue().splitlines():
logger.error('Python %s error: -> %s' % (section, line))
def dump(env, incfile):
exec(includes[incfile], env)
def include(env, incfile):
if not incfile in env.included:
env.included.add(incfile)
exec(includes[incfile], env)
def load_file(filename, logger):
'''Charge un script et affiche un message d'erreur en cas d'exception'''
try:
return pygen.load(filename, os.path.dirname(filename) + "/." + os.path.basename(filename) + ".COMPILED", logger)
except Exception, e:
log_traceback(filename, 'compilation', e)
return None
class PythonProperties(Bcfg2.Server.Plugin.SingleXMLFileBacked):
'''Class for Python 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 Python(Bcfg2.Server.Plugin.Plugin,Bcfg2.Server.Plugin.Generator):
'''The Python generator implements a templating mechanism for configuration files'''
name = 'Python'
__version__ = '1.0'
__author__ = 'dimino@crans.org'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
# Les entrées pour bcfg2
self.Entries['Python'] = {}
# Correspondance entrée ConfigFile -> code
self.codes = {}
# Dico ID de requête GAM -> Dossier surveillé
self.handles = {}
# Le dossier qui contient les fichiers à inclure
self.include = os.path.abspath(self.data + '/../etc/python')
self.AddDirectoryMonitor(self.data)
self.AddDirectoryMonitor(self.include)
try:
self.properties = PythonProperties('%s/../etc/properties.xml' \
% (self.data), self.core.fam)
except:
self.properties = FakeProperties()
self.logger.info("Failed to read properties file; Python properties disabled")
def BuildEntry(self, entry, metadata):
'''Construit le fichier'''
code = self.codes[entry.get('name')]
fname = entry.get('realname', entry.get('name'))
debug("building config file: %s" % fname, 'blue')
env = pygen.Environment()
env["metadata"] = metadata
env["properties"] = self.properties
env["include"] = lambda incfile: include(env, incfile)
env["dump"] = lambda incfile: dump(env, incfile)
env["info"] = { 'owner': 'root',
'group': 'root',
'perms': 0644 }
env.included = set([])
try:
include(env, "common")
text = pygen.generate(code, env, logger)
except Exception, e:
log_traceback(fname, 'exec', e)
raise Bcfg2.Server.Plugin.PluginExecutionError
info = env["info"]
if info.get('encoding', '') == 'base64':
text = binascii.b2a_base64(text)
# lxml n'accepte que de l'ascii ou de l'unicode
try:
entry.text = text.decode("UTF-8")
except:
# solution de fallback
entry.text = text.decode("ISO8859-15")
debug(entry.text)
entry.attrib['owner'] = info.get('owner', 'root')
entry.attrib['group'] = info.get('group', 'root')
entry.attrib['perms'] = oct(info.get('perms', 0644))
if 'encoding' in info:
entry.attrib['encoding'] = info['encoding']
def HandleEvent(self, event):
'''Traitement des événements de FAM'''
# On passe les fichiers ennuyeux
if event.filename[0] == '/' \
or event.filename.endswith(".COMPILED") \
or event.filename.endswith("~") \
or event.filename.endswith(".swp") \
or event.filename.startswith(".#"):
return
action = event.code2str()
debug("event received: action=%s filename=%s requestID=%s" %
(action, event.filename, event.requestID), 'purple')
path = self.handles[event.requestID] + "/" + event.filename
debug("absolute filename: %s" % path, 'yellow')
if path.startswith(self.include):
if posixpath.isfile(path):
# Les fichiers d includes...
identifier = path[len(self.include)+1:-3]
if action in ['exists', 'created', 'changed']:
debug("adding include file: %s" % identifier, 'green')
includes[identifier] = load_file(path, logger)
elif action == 'deleted':
debug("deleting include file: %s" % identifier, 'red')
del includes[identifier]
elif posixpath.isdir(path) and action in ['exists', 'created']:
self.AddDirectoryMonitor(path)
elif posixpath.isfile(path):
# Le nom du dossier est le nom du fichier du fichier de
# configuration à générer
identifier = path[len(self.data):]
if action in ['exists', 'created']:
debug("adding config file: %s" % identifier, 'green')
self.codes[identifier] = load_file(path, logger)
print "Python plugin : creating %s entry due to action %s" % (identifier, action)
self.Entries['Python'][identifier] = self.BuildEntry
elif action == 'changed':
self.codes[identifier] = load_file(path, logger)
elif action == 'deleted':
debug("deleting config file: %s" % identifier, 'red')
del self.codes[identifier]
del self.Entries['Python'][identifier]
elif posixpath.isdir(path):
if action in ['exists', 'created']:
self.AddDirectoryMonitor(path)
else:
logger.info('Ignoring file %s' % path)
def AddDirectoryMonitor(self, path):
'''Surveille un dossier avec FAM'''
if path not in self.handles.values():
if not posixpath.isdir(path):
print "Python: Failed to open directory %s" % path
return
reqid = self.core.fam.AddMonitor(path, self)
self.handles[reqid] = path