#!/usr/bin/env python # $Id: $ '''Bcfg2 Server''' __revision__ = '$Revision' from getopt import getopt, GetoptError from socket import gethostbyaddr, herror from syslog import openlog, syslog, LOG_INFO, LOG_ERR, LOG_LOCAL0 from sys import argv, exit as sysexit, exc_info from time import time from traceback import extract_tb from elementtree.ElementTree import Element from Bcfg2.Server.Core import Core, CoreInitError from Bcfg2.Server.Metadata import MetadataConsistencyError from Bcfg2.Server.Statistics import Statistics from sss.daemonize import daemonize from sss.server import Server def dgetopt(arglist, opt, vopt): '''parse options into a dictionary''' ret = {} for optname in opt.values() + vopt.values(): ret[optname] = False gstr = "".join(opt.keys()) + "".join([xy+':' for xy in vopt.keys()]) try: (o, a) = getopt(arglist, gstr) except GetoptError, gerr: print gerr print "bcfg2 Usage:" for arg in opt.iteritems(): print " -%s %s" % arg for arg in vopt.iteritems(): print " -%s <%s>" % arg sysexit(1) for (gopt, garg) in o: option = gopt[1:] if opt.has_key(option): ret[opt[option]] = True else: ret[vopt[option]] = garg return ret class BcfgServer(Server): '''Bcfg2 Server Class''' __implementation__ = 'Bcfg2' __component__ = 'bcfg2' __dispatch__ = {'get-config':'BuildConfig', 'get-probes':'get_probes', 'probe-data':'put_probe_data', 'upload-statistics':'HandleStats'} __validate__ = 0 def __setup__(self): self.setup = self.kwargs['setup'] try: self.core = Core(setup, self.kwargs.get('configfile', '/etc/bcfg2.conf')) except CoreInitError, msg: print msg raise SystemExit, 1 self.__progress__() def __progress__(self): while self.core.fam.fm.pending(): self.core.fam.HandleEvent() try: self.core.RunCronTasks() except: self.LogFailure("Cron") try: self.core.stats.WriteBack() except: self.LogFailure("Statistics") return 0 def __shutdown__(self): # Update Statistics on shutdown self.core.stats.WriteBack() def BuildConfig(self, xml, (peer, port)): '''Build Client Config''' # find client info if setup['client']: client = setup['client'] else: try: client = gethostbyaddr(peer)[0].split('.')[0] except herror: return Element("error", type='host resolution error') if xml.attrib.has_key("profile") and xml.attrib.has_key("image"): try: # if metadata is provided, call FetchMetadata with settings # it is screwey. i know. meta = self.core.metadata.FetchMetadata(client, image=xml.attrib['image'], profile=xml.attrib['profile']) except MetadataConsistencyError: syslog(LOG_ERR, "Metadata consistency error for client %s" % client) return Element("error", type='metadata error') return self.core.BuildConfiguration(client) def get_probes(self, xml, (peer, port)): '''Get Probes for Client''' r = Element('probes') try: client = gethostbyaddr(peer)[0].split('.')[0] except herror: return Element("error", type='host resolution error') try: m = self.core.metadata.FetchMetadata(client) except MetadataConsistencyError: return Element("error", type='metadata resolution error') for g in self.core.generators: for p in g.get_probes(m): r.append(p) return r def put_probe_data(self, xml, (peer, port)): '''Return Probe output to generators''' try: client = gethostbyaddr(peer)[0].split('.')[0] except herror: return Element("error", type='host resolution error') for data in xml.findall(".//probe-data"): try: [g] = [x for x in self.core.generators if x.__name__ == data.attrib['source']] g.accept_probe_data(client, data) except: self.LogFailure("put_probe_data") return Element("OK") def HandleStats(self, xml, (peer, port)): '''Act on statistics upload''' e = xml.find(".//Statistics") # Versioned stats to prevent tied client/server upgrade if e.get('version') >= '2.0': try: client = gethostbyaddr(peer)[0].split('.')[0] except herror: return Element("error", type='host resolution error') # Update statistics self.core.stats.updateStats(xml, client) syslog(LOG_INFO, "Client %s reported state %s"%(peer, e.attrib['state'])) return Element("ok") def LogFailure(self, failure): '''Log Failures in unexpected cases''' (t, v, tb)=exc_info() syslog(LOG_ERR, "Unexpected failure in %s"%(failure)) for line in extract_tb(tb): syslog(LOG_ERR, ' File "%s", line %i, in %s\n %s\n'%line) syslog(LOG_ERR, "%s: %s\n"%(t, v)) del t, v, tb if __name__ == '__main__': openlog("Bcfg2", 0, LOG_LOCAL0) options = {'v':'verbose', 'd':'debug'} doptions = {'D':'daemon', 'C':'client'} setup = dgetopt(argv[1:], options, doptions) if setup['daemon']: daemonize(setup['daemon']) server = BcfgServer(setup=setup) for i in range(10): server.__progress__() server.ServeForever()