diff options
Diffstat (limited to 'src/sbin')
-rwxr-xr-x | src/sbin/bcfg2 | 36 | ||||
-rwxr-xr-x | src/sbin/bcfg2-admin | 8 | ||||
-rwxr-xr-x | src/sbin/bcfg2-build-reports | 142 | ||||
-rwxr-xr-x | src/sbin/bcfg2-info | 115 | ||||
-rwxr-xr-x | src/sbin/bcfg2-ping-sweep | 2 | ||||
-rwxr-xr-x | src/sbin/bcfg2-repo-validate | 188 | ||||
-rwxr-xr-x | src/sbin/bcfg2-reports | 38 |
7 files changed, 261 insertions, 268 deletions
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2 index fe962211c..9bc50fe65 100755 --- a/src/sbin/bcfg2 +++ b/src/sbin/bcfg2 @@ -3,26 +3,24 @@ """Bcfg2 Client""" __revision__ = '$Revision$' -import fcntl import logging import os import signal -import stat import sys import tempfile import time import xmlrpclib - +import fcntl +import Bcfg2.Options import Bcfg2.Client.XML import Bcfg2.Client.Frame import Bcfg2.Client.Tools -import Bcfg2.Options -import Bcfg2.Logger + import Bcfg2.Proxy +import Bcfg2.Logger logger = logging.getLogger('bcfg2') - def cb_sigint_handler(signum, frame): """Exit upon CTRL-C.""" os._exit(1) @@ -107,11 +105,9 @@ class Client: self.logger.info(Bcfg2.Client.Tools.drivers) raise SystemExit(0) if self.setup['remove'] and 'services' in self.setup['remove']: - self.logger.error("Service removal is nonsensical, " - "disable services to get former behavior") + self.logger.error("Service removal is nonsensical, disable services to get former behavior") if self.setup['remove'] not in [False, 'all', 'services', 'packages']: - self.logger.error("Got unknown argument %s for -r" % - (self.setup['remove'])) + self.logger.error("Got unknown argument %s for -r" % (self.setup['remove'])) if (self.setup["file"] != False) and (self.setup["cache"] != False): print("cannot use -f and -c together") raise SystemExit(1) @@ -134,17 +130,13 @@ class Client: script.write(probe.text) script.close() os.close(scripthandle) - os.chmod(script.name, stat.S_IRUSR | stat.S_IWUSR | - stat.S_IXUSR | stat.S_IRGRP | - stat.S_IXGRP | stat.S_IROTH | - stat.S_IXOTH) # 0755 + os.chmod(script.name, 0755) ret.text = os.popen(script.name).read().strip() self.logger.info("Probe %s has result:\n%s" % (name, ret.text)) finally: os.unlink(script.name) except: - self.logger.error("Failed to execute probe: %s" % (name), - exc_info=1) + self.logger.error("Failed to execute probe: %s" % (name), exc_info=1) raise SystemExit(1) return ret @@ -177,10 +169,10 @@ class Client: proxy = Bcfg2.Proxy.ComponentProxy(self.setup['server'], self.setup['user'], self.setup['password'], - key=self.setup['key'], - cert=self.setup['certificate'], - ca=self.setup['ca'], - allowedServerCNs=self.setup['serverCN']) + key = self.setup['key'], + cert = self.setup['certificate'], + ca = self.setup['ca'], + allowedServerCNs = self.setup['serverCN']) if self.setup['profile']: try: @@ -218,9 +210,7 @@ class Client: if len(probes.findall(".//probe")) > 0: try: # upload probe responses - proxy.RecvProbeData(Bcfg2.Client.XML.tostring(probedata, - encoding='UTF-8', - xml_declaration=True)) + proxy.RecvProbeData(Bcfg2.Client.XML.tostring(probedata, encoding='UTF-8', xml_declaration=True)) except: self.logger.error("Failed to upload probe data", exc_info=1) raise SystemExit(1) diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin index f8b82d201..2c9a43859 100755 --- a/src/sbin/bcfg2-admin +++ b/src/sbin/bcfg2-admin @@ -12,7 +12,6 @@ log = logging.getLogger('bcfg2-admin') import Bcfg2.Server.Admin - def mode_import(modename): """Load Bcfg2.Server.Admin.<mode>.""" modname = modename.capitalize() @@ -20,12 +19,10 @@ def mode_import(modename): (modname)).Server.Admin, modname) return getattr(mod, modname) - def get_modes(): """Get all available modes, except for the base mode.""" return [x.lower() for x in Bcfg2.Server.Admin.__all__ if x != 'mode'] - def create_description(): """Create the description string from the list of modes.""" modes = get_modes() @@ -39,7 +36,6 @@ def create_description(): continue return description.getvalue() - def main(): Bcfg2.Logger.setup_logging('bcfg2-admin', to_console=True, level=40) usage = "Usage: %prog [options] MODE [args]" @@ -60,7 +56,7 @@ def main(): else: # Print short help for all modes parser.print_help() - print(create_description()) + print create_description() raise SystemExit(0) if args[0] in get_modes(): @@ -77,7 +73,7 @@ def main(): else: log.error("Unknown mode %s" % args[0]) parser.print_help() - print(create_description()) + print create_description() raise SystemExit(1) if __name__ == '__main__': diff --git a/src/sbin/bcfg2-build-reports b/src/sbin/bcfg2-build-reports index 6b3e24e84..231f52105 100755 --- a/src/sbin/bcfg2-build-reports +++ b/src/sbin/bcfg2-build-reports @@ -14,9 +14,7 @@ import socket import sys from time import asctime, strptime from ConfigParser import ConfigParser, NoSectionError, NoOptionError -from lxml.etree import XML, XSLT, parse, Element, ElementTree, \ - SubElement, tostring, XMLSyntaxError - +from lxml.etree import XML, XSLT, parse, Element, ElementTree, SubElement, tostring, XMLSyntaxError def generatereport(rspec, nrpt): """ @@ -26,12 +24,12 @@ def generatereport(rspec, nrpt): reportspec = copy.deepcopy(rspec) nodereprt = copy.deepcopy(nrpt) - reportgood = reportspec.get("good", default='Y') - reportmodified = reportspec.get("modified", default='Y') + reportgood = reportspec.get("good", default = 'Y') + reportmodified = reportspec.get("modified", default = 'Y') current_date = asctime()[:10] """Build regex of all the nodes we are reporting about.""" - pattern = re.compile('|'.join([item.get("name") for item in reportspec.findall('Machine')])) + pattern = re.compile( '|'.join([item.get("name") for item in reportspec.findall('Machine')])) for node in nodereprt.findall('Node'): if not (node.findall("Statistics") and pattern.match(node.get('name'))): @@ -42,27 +40,25 @@ def generatereport(rspec, nrpt): # Reduce to most recent Statistics entry. statisticslist = node.findall('Statistics') # This line actually sorts from most recent to oldest. - statisticslist.sort(lambda y, x: cmp(strptime(x.get("time")), - strptime(y.get("time")))) + statisticslist.sort(lambda y, x: cmp(strptime(x.get("time")), strptime(y.get("time")))) stats = statisticslist[0] - + [node.remove(item) for item in node.findall('Statistics')] - + # Add a good tag if node is good and we wnat to report such. if reportgood == 'Y' and stats.get('state') == 'clean': - SubElement(stats, "Good") + SubElement(stats,"Good") [stats.remove(item) for item in stats.findall("Bad") + stats.findall("Modified") if \ item.getchildren() == []] [stats.remove(item) for item in stats.findall("Modified") if reportmodified == 'N'] - + # Test for staleness -if stale add Stale tag. if stats.get("time").find(current_date) == -1: - SubElement(stats, "Stale") + SubElement(stats,"Stale") node.append(stats) return nodereprt - def mail(mailbody, confi): """mail mails a previously generated report.""" @@ -76,8 +72,7 @@ def mail(mailbody, confi): pipe.write(mailbody) exitcode = pipe.close() if exitcode: - print("Exit code: %s" % exitcode) - + print "Exit code: %s" % exitcode def rss(reportxml, delivery, report): """rss appends a new report to the specified rss file @@ -103,7 +98,7 @@ def rss(reportxml, delivery, report): chantitle = SubElement(channel, "title") chantitle.text = report.attrib['name'] chanlink = SubElement(channel, "link") - + # This can later link to WWW report if one gets published # simultaneously? chanlink.text = "http://www.mcs.anl.gov/cobalt/bcfg2" @@ -120,19 +115,17 @@ def rss(reportxml, delivery, report): fil.write(tree) fil.close() - def www(reportxml, delivery): """www outputs report to.""" # This can later link to WWW report if one gets published - # simultaneously? + # simultaneously? for destination in delivery.findall('Destination'): fil = open(destination.attrib['address'], 'w') fil.write(reportxml) fil.close() - def fileout(reportxml, delivery): """Outputs to plain text file.""" for destination in delivery.findall('Destination'): @@ -141,29 +134,25 @@ def fileout(reportxml, delivery): fil.write(reportxml) fil.close() - def pretty_print(element, level=0): """Produce a pretty-printed text representation of element.""" if element.text: - fmt = "%s<%%s %%s>%%s</%%s>" % (level * " ") - data = (element.tag, (" ".join(["%s='%s'" % keyval for keyval in - list(element.attrib.items())])), + fmt = "%s<%%s %%s>%%s</%%s>" % (level*" ") + data = (element.tag, (" ".join(["%s='%s'" % keyval for keyval in element.attrib.iteritems()])), element.text, element.tag) if element._children: - fmt = "%s<%%s %%s>\n" % (level * " ",) + (len(element._children) * "%s") + "%s</%%s>\n" % (level * " ") - data = (element.tag, ) + (" ".join(["%s='%s'" % keyval for keyval in - list(element.attrib.items())]),) - data += tuple([pretty_print(entry, level + 2) for entry in element._children]) + (element.tag, ) + fmt = "%s<%%s %%s>\n" % (level*" ",) + (len(element._children) * "%s") + "%s</%%s>\n" % (level*" ") + data = (element.tag, ) + (" ".join(["%s='%s'" % keyval for keyval in element.attrib.iteritems()]),) + data += tuple([pretty_print(entry, level+2) for entry in element._children]) + (element.tag, ) else: fmt = "%s<%%s %%s/>\n" % (level * " ") - data = (element.tag, " ".join(["%s='%s'" % keyval for keyval in - list(element.attrib.items())])) + data = (element.tag, " ".join(["%s='%s'" % keyval for keyval in element.attrib.iteritems()])) return fmt % data if __name__ == '__main__': - ping = True - all = False + ping=True + all=False if '-C' in sys.argv: cfpath = sys.argv[sys.argv.index('-C') + 1] else: @@ -182,24 +171,17 @@ if __name__ == '__main__': #websrcspath = "/usr/share/bcfg2/web-rprt-srcs/" try: - opts, args = getopt.getopt(sys.argv[1:], - "C:hAc:Ns:", - ["help", "all", "config=", "no-ping", - "stats="]) + opts, args = getopt.getopt(sys.argv[1:], "C:hAc:Ns:", ["help", "all", "config=","no-ping", "stats="]) except getopt.GetoptError, mesg: # Print help information and exit: - print("%s\nUsage:\n" - "bcfg2-build-reports [-h] [-A (include ALL clients)] " - "[-c <configuration-file>] [-s <statistics-file>] " - "[-N (do not ping clients)]" % (mesg)) - raise SystemExit(2) + print "%s\nUsage:\nbcfg2-build-reports [-h][-A (include ALL clients)] [-c <configuration-file>] [-s <statistics-file>][-N (do not ping clients)]" % (mesg) + raise SystemExit, 2 for o, a in opts: if o in ("-h", "--help"): - print("Usage:\nbcfg2-build-reports [-h] [-c <configuration-file>] " - "[-s <statistics-file>]") + print "Usage:\nbcfg2-build-reports [-h] [-c <configuration-file>] [-s <statistics-file>]" raise SystemExit if o in ("-A", "--all"): - all = True + all=True if o in ("-c", "--config"): configpath = a if o in ("-N", "--no-ping"): @@ -207,96 +189,94 @@ if __name__ == '__main__': if o in ("-s", "--stats"): statpath = a + # See if hostinfo.xml exists, and is less than 23.5 hours old #try: #hostinstat = os.stat(hostinfopath) #if (time() - hostinstat[9])/(60*60) > 23.5: if ping: - os.system('bcfg2-ping-sweep -C %s' % cfpath) # bcfg2-ping-sweep needs to be in path + os.system('bcfg2-ping-sweep -C %s' % cfpath) # bcfg2-ping-sweep needs to be in path #except OSError: # os.system('GenerateHostInfo')#Generate HostInfo needs to be in path + """Reads data & config files.""" try: statsdata = XML(open(statpath).read()) except (IOError, XMLSyntaxError): - print("bcfg2-build-reports: Failed to parse %s" % (statpath)) - raise SystemExit(1) + print("bcfg2-build-reports: Failed to parse %s"%(statpath)) + raise SystemExit, 1 try: configdata = XML(open(configpath).read()) except (IOError, XMLSyntaxError): - print("bcfg2-build-reports: Failed to parse %s" % (configpath)) - raise SystemExit(1) + print("bcfg2-build-reports: Failed to parse %s"%(configpath)) + raise SystemExit, 1 try: clientsdata = XML(open(clientsdatapath).read()) except (IOError, XMLSyntaxError): - print("bcfg2-build-reports: Failed to parse %s" % (clientsdatapath)) - raise SystemExit(1) + print("bcfg2-build-reports: Failed to parse %s"%(clientsdatapath)) + raise SystemExit, 1 # Merge data from three sources. - nodereport = Element("Report", attrib={"time": asctime()}) + nodereport = Element("Report", attrib={"time" : asctime()}) # Should all of the other info in Metadata be appended? # What about all of the package stuff for other types of reports? for client in clientsdata.findall("Client"): - nodel = Element("Node", attrib={"name": client.get("name")}) + nodel = Element("Node", attrib={"name" : client.get("name")}) nodel.append(client) for nod in statsdata.findall("Node"): if client.get('name').find(nod.get('name')) == 0: for statel in nod.findall("Statistics"): nodel.append(statel) nodereport.append(nodel) - + if all: for nod in statsdata.findall("Node"): for client in clientsdata.findall("Client"): if client.get('name').find(nod.get('name')) == 0: break else: - nodel = Element("Node", attrib={"name": nod.get("name")}) - client = Element("Client", attrib={"name": nod.get("name"), - "profile": "default"}) + nodel = Element("Node", attrib={"name" : nod.get("name")}) + client = Element("Client", attrib={"name" : nod.get("name"), "profile" : "default"}) nodel.append(client) for statel in nod.findall("Statistics"): nodel.append(statel) nodereport.append(nodel) - + + for reprt in configdata.findall('Report'): nodereport.set("name", reprt.get("name", default="BCFG Report")) if reprt.get('refresh-time') != None: - nodereport.set("refresh-time", reprt.get("refresh-time", - default="600")) + nodereport.set("refresh-time", reprt.get("refresh-time", default="600")) procnodereport = generatereport(reprt, nodereport) for deliv in reprt.findall('Delivery'): # Is a deepcopy of procnodereport necessary? - + delivtype = deliv.get('type', default='nodes-digest') deliverymechanism = deliv.get('mechanism', default='www') # Apply XSLT, different ones based on report type, and options - if deliverymechanism == 'null-operator': # Special Cases - fileout(tostring(ElementTree(procnodereport).getroot(), - encoding='UTF-8', - xml_declaration=True), - deliv) + if deliverymechanism == 'null-operator': # Special Cases + fileout(tostring(ElementTree(procnodereport).getroot(), encoding='UTF-8', xml_declaration=True), deliv) break transform = delivtype + '-' + deliverymechanism + '.xsl' - try: # Make sure valid stylesheet is selected. + try: # Make sure valid stylesheet is selected. os.stat(transformpath + transform) except: print("bcfg2-build-reports: Invalid report type or delivery mechanism.\n Can't find: "\ + transformpath + transform) - raise SystemExit(1) + raise SystemExit, 1 - try: # Try to parse stylesheet. + try: # Try to parse stylesheet. stylesheet = XSLT(parse(transformpath + transform)) except: print("bcfg2-build-reports: invalid XSLT transform file.") - raise SystemExit(1) - + raise SystemExit, 1 + if deliverymechanism == 'mail': if delivtype == 'nodes-individual': reportdata = copy.deepcopy(procnodereport) @@ -305,37 +285,35 @@ if __name__ == '__main__': reportdata.append(noden) result = stylesheet.apply(ElementTree(reportdata)) outputstring = stylesheet.tostring(result) - + if not outputstring == None: toastring = '' for desti in deliv.findall("Destination"): toastring = "%s%s " % \ (toastring, desti.get('address')) # Prepend To: and From: - outputstring = "To: %s\nFrom: root@%s\n%s" % \ + outputstring = "To: %s\nFrom: root@%s\n%s"% \ (toastring, socket.getfqdn(), outputstring) - mail(outputstring, c) # call function to send - + mail(outputstring, c) #call function to send + else: reportdata = copy.deepcopy(procnodereport) result = stylesheet.apply(ElementTree(reportdata)) outputstring = stylesheet.tostring(result) - + if not outputstring == None: toastring = '' for desti in deliv.findall("Destination"): toastring = "%s%s " % \ (toastring, desti.get('address')) # Prepend To: and From: - outputstring = "To: %s\nFrom: root@%s\n%s" % \ + outputstring = "To: %s\nFrom: root@%s\n%s"% \ (toastring, socket.getfqdn(), outputstring) - mail(outputstring, c) # call function to send + mail(outputstring, c) #call function to send else: - outputstring = tostring(stylesheet.apply(ElementTree(procnodereport)).getroot(), - encoding='UTF-8', - xml_declaration=True) + outputstring = tostring(stylesheet.apply(ElementTree(procnodereport)).getroot(), encoding='UTF-8', xml_declaration=True) if deliverymechanism == 'rss': rss(outputstring, deliv, reprt) - else: # Must be deliverymechanism == 'www': + else: # Must be deliverymechanism == 'www': www(outputstring, deliv) diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info index f78b3a7f4..a6d236bc8 100755 --- a/src/sbin/bcfg2-info +++ b/src/sbin/bcfg2-info @@ -5,6 +5,7 @@ __revision__ = '$Revision$' from code import InteractiveConsole import cmd +import errno import getopt import logging import lxml.etree @@ -27,42 +28,6 @@ import Bcfg2.Server.Plugin logger = logging.getLogger('bcfg2-info') -USAGE = """Commands - -build <hostname> <filename> - Build config for hostname, writing to filename -builddir <hostname> <dirname> - Build config for hostname, writing separate files to dirname -buildall <directory> - Build configs for all clients in directory -buildfile <filename> <hostname> - Build config file for hostname (not written to disk) -bundles - Print out group/bundle information -clients - Print out client/profile information -config - Print out the configuration of the Bcfg2 server -debug - Shell out to native python interpreter -event_debug - Display filesystem events as they are processed -generators - List current versions of generators -groups - List groups -help - Print this list of available commands -mappings <type*> <name*> - Print generator mappings for optional type and name -profile <command> <args> - Profile a single bcfg2-info command -quit - Exit the bcfg2-info command line -showentries <hostname> <type> - Show abstract configuration entries for a given host -showclient <client1> <client2> - Show metadata for given hosts -update - Process pending file events -version - Print version of this tool""" - -BUILDDIR_USAGE = """Usage: builddir [-f] <hostname> <output dir> - -Generates a config for client <hostname> and writes the -individual configuration files out separately in a tree -under <output dir>. The <output dir> directory must be -rooted under /tmp unless the -f argument is provided, in -which case it can be located anywhere. - -NOTE: Currently only handles file entries and writes -all content with the default owner and permissions. These -could be much more permissive than would be created by the -Bcfg2 client itself.""" - - class mockLog(object): def error(self, *args, **kwargs): pass @@ -73,22 +38,18 @@ class mockLog(object): def debug(self, *args, **kwargs): pass - class dummyError(Exception): """This is just a dummy.""" pass - class FileNotBuilt(Exception): """Thrown when File entry contains no content.""" def __init__(self, value): Exception.__init__(self) self.value = value - def __str__(self): return repr(self.value) - def printTabular(rows): """Print data in tabular format.""" cmax = tuple([max([len(str(row[index])) for row in rows]) + 1 \ @@ -100,13 +61,11 @@ def printTabular(rows): for row in rows[1:]: print(fstring % row) - def displayTrace(trace, num=80, sort=('time', 'calls')): stats = pstats.Stats(trace) stats.sort_stats('cumulative', 'calls', 'time') stats.print_stats(200) - class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): """Main class for bcfg2-info.""" def __init__(self, repo, plgs, passwd, encoding, event_debug): @@ -147,7 +106,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): try: opts, _ = getopt.getopt(args.split(), 'nf:') except: - print("Usage: debug [-n] [-f <command list>]") + print "Usage: debug [-n] [-f <command list>]" return self.cont = False scriptmode = False @@ -177,7 +136,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): Exit program. Usage: [quit|exit] """ - for plugin in list(self.plugins.values()): + for plugin in self.plugins.values(): plugin.shutdown() os._exit(0) @@ -186,7 +145,27 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): def do_help(self, _): """Print out usage info.""" - print(USAGE) + print 'Commands:' + print 'build <hostname> <filename> - Build config for hostname, writing to filename' + print 'builddir <hostname> <dirname> - Build config for hostname, writing separate files to dirname' + print 'buildall <directory> - Build configs for all clients in directory' + print 'buildfile <filename> <hostname> - Build config file for hostname (not written to disk)' + print 'bundles - Print out group/bundle information' + print 'clients - Print out client/profile information' + print 'config - Print out the configuration of the Bcfg2 server' + print 'debug - Shell out to native python interpreter' + print 'event_debug - Display filesystem events as they are processed' + print 'generators - List current versions of generators' + print 'groups - List groups' + print 'help - Print this list of available commands' + print 'mappings <type*> <name*> - Print generator mappings for optional type and name' + print 'profile <command> <args> - Profile a single bcfg2-info command' + print 'quit - Exit the bcfg2-info command line' + print 'showentries <hostname> <type> - Show abstract configuration entries for a given host' + print 'showclient <client1> <client2> - Show metadata for given hosts' + print 'update - Process pending file events' + print 'version - Print version of this tool' + def do_update(self, _): """Process pending filesystem events.""" @@ -219,7 +198,18 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): def help_builddir(self): """Display help for builddir command.""" - print(BUILDDIR_USAGE) + print('Usage: builddir [-f] <hostname> <output dir>') + print('') + print('Generates a config for client <hostname> and writes the') + print('individual configuration files out separately in a tree') + print('under <output dir>. The <output dir> directory must be') + print('rooted under /tmp unless the -f argument is provided, in') + print('which case it can be located anywhere.') + print('') + print('NOTE: Currently only handles file entries and writes') + print('all content with the default owner and permissions. These') + print('could be much more permissive than would be created by the') + print('Bcfg2 client itself.') def do_builddir(self, args): """Build client configuration as separate files within a dir.""" @@ -248,9 +238,9 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): p = Bcfg2.Client.Tools.POSIX.POSIX(log, setup, client_config) states = dict() p.Inventory(states) - p.Install(list(states.keys()), states) + p.Install(states.keys(), states) else: - print("Error: Incorrect number of parameters.") + print('Error: Incorrect number of parameters.') self.help_builddir() def do_buildall(self, args): @@ -272,7 +262,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): try: metadata = self.build_metadata(client) self.Bind(entry, metadata) - print(lxml.etree.tostring(entry, encoding="UTF-8", + print(lxml.etree.tostring(entry, encoding="UTF-8", xml_declaration=True)) except: print("Failed to build entry %s for host %s" % (fname, client)) @@ -317,6 +307,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): ] printTabular(output) + def do_generators(self, _): """Print out generator info.""" for generator in self.generators: @@ -380,22 +371,22 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): except: print("Client %s not defined" % client) continue - print("Hostname:\t", client_meta.hostname) - print("Profile:\t", client_meta.profile) - print("Groups:\t\t", list(client_meta.groups)[0]) + print "Hostname:\t", client_meta.hostname + print "Profile:\t", client_meta.profile + print "Groups:\t\t", list(client_meta.groups)[0] for grp in list(client_meta.groups)[1:]: - print("\t\t%s" % grp) + print '\t\t%s' % grp if client_meta.bundles: - print("Bundles:\t", list(client_meta.bundles)[0]) + print "Bundles:\t", list(client_meta.bundles)[0] for bnd in list(client_meta.bundles)[1:]: - print("\t\t%s" % bnd) + print '\t\t%s' % bnd if client_meta.connectors: - print("Connector data") - print("=" * 80) + print "Connector data" + print "=" * 80 for conn in client_meta.connectors: if getattr(client_meta, conn): - print("%s:\t" % (conn), getattr(client_meta, conn)) - print("=" * 80) + print "%s:\t" % (conn), getattr(client_meta, conn) + print "=" * 80 def do_mappings(self, args): """Print out mapping info.""" @@ -411,11 +402,11 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): interested = [(etype, [args.split()[1]]) for etype in etypes] else: - interested = [(etype, generator.Entries[etype]) - for etype in etypes + interested = [(etype, generator.Entries[etype]) + for etype in etypes if etype in generator.Entries] for etype, names in interested: - for name in [name for name in names if name in + for name in [name for name in names if name in generator.Entries.get(etype, {})]: data.append((generator.name, etype, name)) printTabular(data) diff --git a/src/sbin/bcfg2-ping-sweep b/src/sbin/bcfg2-ping-sweep index 70f718690..718ad69d0 100755 --- a/src/sbin/bcfg2-ping-sweep +++ b/src/sbin/bcfg2-ping-sweep @@ -33,7 +33,7 @@ if __name__ == '__main__': osname = uname()[0] while hostlist or pids: - if hostlist and len(list(pids.keys())) < 15: + if hostlist and len(pids.keys()) < 15: host = hostlist.pop() pid = fork() if pid == 0: diff --git a/src/sbin/bcfg2-repo-validate b/src/sbin/bcfg2-repo-validate index 595613d8a..4d8dd6bed 100755 --- a/src/sbin/bcfg2-repo-validate +++ b/src/sbin/bcfg2-repo-validate @@ -11,12 +11,56 @@ import glob import lxml.etree import os import sys +import fnmatch +import logging import Bcfg2.Options +from subprocess import Popen, PIPE, STDOUT + +def validate(filename, schemafile, schema=None, xinclude=True): + """validate a fail against the given lxml.etree.Schema. return + True on success, False on failure""" + if schema is None: + # if no schema object was provided, instantiate one + try: + schema = lxml.etree.XMLSchema(lxml.etree.parse(schemafile)) + except: + logging.warn("Failed to process schema %s", schemafile) + return False + + try: + datafile = lxml.etree.parse(filename) + except SyntaxError: + logging.warn("%s ***FAILS*** to parse \t\t<----", filename) + lint = Popen(["xmllint", filename], stdout=PIPE, stderr=STDOUT) + logging.warn(lint.communicate()[0]) + lint.wait() + return False + except IOError: + logging.warn("Failed to open file %s \t\t<---", filename) + return False + + if schema.validate(datafile): + logging.info("%s checks out", filename) + else: + cmd = ["xmllint"] + if xinclude: + cmd.append("--xinclude") + cmd.extend(["--noout", "--schema", schemafile, filename]) + lint = Popen(cmd, stdout=PIPE, stderr=STDOUT) + output = lint.communicate()[0] + if lint.wait(): + logging.warn("%s ***FAILS*** to verify \t\t<----", filename) + logging.warn(output) + return False + else: + logging.info("%s checks out", filename) + return True if __name__ == '__main__': opts = {'repo': Bcfg2.Options.SERVER_REPOSITORY, 'verbose': Bcfg2.Options.VERBOSE, 'configfile': Bcfg2.Options.CFILE, + 'require-schema': Bcfg2.Options.REQUIRE_SCHEMA} 'schema': Bcfg2.Options.SCHEMA_PATH, 'stdin': Bcfg2.Options.FILES_ON_STDIN} setup = Bcfg2.Options.OptionParser(opts) @@ -27,6 +71,12 @@ if __name__ == '__main__': os.chdir(schemadir) repo = setup['repo'] + # set up logging + level = logging.WARNING + if verbose: + level = logging.INFO + logging.basicConfig(level=level, format="%(message)s") + if setup['stdin']: file_list = [s.strip() for s in sys.stdin.readlines()] info_list = [f for f in file_list if os.path.basename(f) == 'info.xml'] @@ -44,6 +94,9 @@ if __name__ == '__main__': dec_list = fnmatch.filter(file_list, "*/Decisions/*") pkgcfg_list = fnmatch.filter(file_list, "*/Packages/config.xml") gp_list = fnmatch.filter(file_list, "*/GroupPatterns/config.xml") + props_list = [f + for f in fnmatch.filter(file_list, "*/Properties/*.xml") + if "%s.xsd" % os.path.splitext(f)[0] in file_list] else: # not reading files from stdin @@ -70,6 +123,7 @@ if __name__ == '__main__': dec_list = glob.glob("%s/Decisions/*" % repo) pkgcfg_list = glob.glob("%s/Packages/config.xml" % repo) gp_list = glob.glob('%s/GroupPatterns/config.xml' % repo) + props_list = glob.glob("%s/Properties/*.xml" % repo) # include files in metadata_list ref_bundles = set() @@ -81,8 +135,7 @@ if __name__ == '__main__': filename = included.pop() except KeyError: continue - if not setup['stdin'] or filepath in file_list: - metadata_list.append("%s/Metadata/%s" % (repo, filename)) + metadata_list.append("%s/Metadata/%s" % (repo, filename)) groupdata = lxml.etree.parse("%s/Metadata/%s" % (repo, filename)) group_ents = [ent.get('href') for ent in \ groupdata. @@ -97,15 +150,14 @@ if __name__ == '__main__': ref_bundles.add("%s/Bundler/%s" % (repo, bundle.get('name'))) # check for multiple default group definitions - default_groups = [] - for grp in lxml.etree.parse("%s/Metadata/groups.xml" \ - % repo).findall('.//Group'): - if grp.get('default') == 'true': - default_groups.append(grp) - if len(default_groups) > 1: - print("*** Warning: Multiple default groups defined") - for grp in default_groups: - print(" %s" % grp.get('name')) + if "%s/Metadata/groups.xml" % repo in metadata_list: + default_groups = [g for g in lxml.etree.parse("%s/Metadata/groups.xml" % + repo).findall('.//Group') + if g.get('default') == 'true'] + if len(default_groups) > 1: + logging.warn("*** Warning: Multiple default groups defined") + for grp in default_groups: + logging.warn(" %s", grp.get('name')) # verify attributes for configuration entries # (as defined in doc/server/configurationentries) @@ -123,7 +175,7 @@ if __name__ == '__main__': try: xdata = lxml.etree.parse(rfile) except lxml.etree.XMLSyntaxError, e: - print("Failed to parse %s: %s" % (rfile, e)) + logging.warn("Failed to parse %s: %s", rfile, e) for posixpath in xdata.findall("//Path"): pathname = posixpath.get('name') pathtype = posixpath.get('type') @@ -141,9 +193,11 @@ if __name__ == '__main__': if pathset.issuperset(required_attrs): continue else: - print("The following required attributes are missing for" - " Path %s in %s: %s" % (pathname, rfile, - [attr for attr in required_attrs.difference(pathset)])) + logging.warn("The following required attributes are missing for" + " Path %s in %s: %s", + pathname, rfile, + [attr + for attr in required_attrs.difference(pathset)]) # warn on duplicate Pkgmgr entries with the same priority pset = set() @@ -151,7 +205,7 @@ if __name__ == '__main__': try: xdata = lxml.etree.parse(plist) except lxml.etree.XMLSyntaxError, e: - print("Failed to parse %s: %s" % (plist, e)) + logging.warn("Failed to parse %s: %s", plist, e) # get priority, type, group priority = xdata.getroot().get('priority') ptype = xdata.getroot().get('type') @@ -169,8 +223,8 @@ if __name__ == '__main__': # check if package is already listed with same priority, # type, grp if ptuple in pset: - print("Duplicate Package %s, priority:%s, type:%s"\ - % (pkg.get('name'), priority, ptype)) + logging.warn("Duplicate Package %s, priority:%s, type:%s", + pkg.get('name'), priority, ptype) else: pset.add(ptuple) @@ -190,65 +244,55 @@ if __name__ == '__main__': failures = 0 for schemaname, filelist in list(filesets.items()): - try: - schema = lxml.etree.XMLSchema(lxml.etree.parse(open(schemaname % - schemadir))) - except: - print("Failed to process schema %s" % (schemaname % schemadir)) - failures = 1 - continue - for filename in filelist: + if filelist: + # avoid loading schemas for empty file lists try: - datafile = lxml.etree.parse(open(filename)) - except SyntaxError: - print("%s ***FAILS*** to parse \t\t<----" % (filename)) - os.system("xmllint %s" % filename) - failures = 1 - continue - except IOError: - print("Failed to open file %s \t\t<---" % (filename)) + schema = lxml.etree.XMLSchema(lxml.etree.parse(schemaname % + schemadir)) + except: + logging.warn("Failed to process schema %s", + schemaname % schemadir) failures = 1 continue - if schema.validate(datafile): - if verbose: - print("%s checks out" % (filename)) - else: - rc = os.system("xmllint --noout --xinclude --schema \ - %s %s > /dev/null 2>/dev/null" % \ - (schemaname % schemadir, filename)) - if rc: + for filename in filelist: + if not validate(filename, schemaname % schemadir, + schema=schema, xinclude=not setup['stdin']): failures = 1 - print("%s ***FAILS*** to verify \t\t<----" % (filename)) - os.system("xmllint --noout --xinclude --schema %s %s" % \ - (schemaname % schemadir, filename)) - elif verbose: - print("%s checks out" % (filename)) + # check Properties files against their schemas + for filename in props_list: + logging.info("checking %s" % filename) + schemafile = "%s.xsd" % os.path.splitext(filename)[0] + if os.path.exists(schemafile): + if not validate(filename, schemafile, xinclude=not setup['stdin']): + failures = 1 + elif setup['require-schema']: + logging.warn("No schema found for %s", filename) + failures = 1 + # print out missing bundle information - if verbose: - print("") - if not setup['stdin']: - # if we've taken a list of files on stdin, there's an - # excellent chance that referenced bundles do not exist, - # so skip this check - for bundle in ref_bundles: - # check for both regular and genshi bundles - xmlbundle = "%s.xml" % bundle - genshibundle = "%s.genshi" % bundle - allbundles = bundle_list + genshibundle_list - if (xmlbundle not in allbundles and - genshibundle not in allbundles): - print("*** Warning: Bundle %s referenced, but does not " - "exist." % bundle) - - # verify bundle name attribute matches filename - for bundle in (bundle_list + genshibundle_list): - fname = bundle.split('Bundler/')[1].split('.')[0] - xdata = lxml.etree.parse(bundle) - bname = xdata.getroot().get('name') - if fname != bname: - print("The following names are inconsistent:") - print(" Filename is %s" % fname) - print(" Bundle name found in %s is %s" % (fname, bname)) + logging.info("") + if not setup['stdin']: + # if we've taken a list of files on stdin, there's an + # excellent chance that referenced bundles do not exist, so + # skip this check + for bundle in ref_bundles: + # check for both regular and genshi bundles + xmlbundle = "%s.xml" % bundle + genshibundle = "%s.genshi" % bundle + allbundles = bundle_list + genshibundle_list + if xmlbundle not in allbundles and genshibundle not in allbundles: + logging.info("*** Warning: Bundle %s referenced, but does not " + "exist.", bundle) + + # verify bundle name attribute matches filename + for bundle in (bundle_list + genshibundle_list): + fname = bundle.split('Bundler/')[1].split('.')[0] + xdata = lxml.etree.parse(bundle) + bname = xdata.getroot().get('name') + if fname != bname: + logging.warn("The following names are inconsistent:") + logging.warn(" Filename is %s", fname) + logging.warn(" Bundle name found in %s is %s", fname, bname) raise SystemExit(failures) diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports index c6cc766c6..559e9fb43 100755 --- a/src/sbin/bcfg2-reports +++ b/src/sbin/bcfg2-reports @@ -26,18 +26,15 @@ from getopt import getopt import datetime import fileinput - def timecompare(client1, client2): """Compares two clients by their timestamps.""" return cmp(client1.current_interaction.timestamp, \ client2.current_interaction.timestamp) - def namecompare(client1, client2): """Compares two clients by their names.""" return cmp(client1.name, client2.name) - def statecompare(client1, client2): """Compares two clients by their states.""" clean1 = client1.current_interaction.isclean() @@ -50,7 +47,6 @@ def statecompare(client1, client2): else: return 0 - def crit_compare(criterion, client1, client2): """Compares two clients by the criteria provided in criterion.""" for crit in criterion: @@ -61,13 +57,12 @@ def crit_compare(criterion, client1, client2): comp = statecompare(client1, client2) elif crit == 'time': comp = timecompare(client1, client2) - + if comp != 0: return comp - + return 0 - def print_fields(fields, cli, max_name, entrydict): """ Prints the fields specified in fields of cli, max_name @@ -98,15 +93,14 @@ def print_fields(fields, cli, max_name, entrydict): if len(entrydict) > 0: display += " " display += str(entrydict[cli]) - print(display) - + print display def print_entry(item, max_name): fmt = ("%%-%ds " % (max_name)) fdata = item.entry.kind + ":" + item.entry.name display = fmt % (fdata) - print(display) - + print display + fields = "" sort = "" badentry = "" @@ -143,14 +137,14 @@ if expire != "": if expire == c_inst.name: if c_inst.expiration == None: c_inst.expiration = datetime.datetime.now() - print("Host expired.") + print "Host expired." else: c_inst.expiration = None - print("Host un-expired.") + print "Host un-expired." c_inst.save() elif '-h' in args: - print("""Usage: bcfg2-reports [option] ... + print """Usage: bcfg2-reports [option] ... Options and arguments (and corresponding environment variables): -a : shows all hosts, including expired hosts @@ -176,13 +170,13 @@ Options and arguments (and corresponding environment variables): (name,time,state) --sort=ARG1,ARG2,... : sorts output on ARG1,ARG2,... (name,time,state) --stale : shows hosts which haven't run in the last 24 hours -""") +""" elif singlehost != "": for c_inst in c_list: if singlehost == c_inst.name: baditems = c_inst.current_interaction.bad() if len(baditems) > 0 and ('-b' in args or '-s' in args): - print("Bad Entries:") + print "Bad Entries:" max_name = -1 for item in baditems: if len(item.entry.name) > max_name: @@ -191,14 +185,14 @@ elif singlehost != "": print_entry(item, max_name) extraitems = c_inst.current_interaction.extra() if len(extraitems) > 0 and ('-e' in args or '-s' in args): - print("Extra Entries:") + print "Extra Entries:" max_name = -1 for item in extraitems: if len(item.entry.name) > max_name: max_name = len(item.entry.name) for item in extraitems: print_entry(item, max_name) - + else: if fields == "": @@ -214,19 +208,19 @@ else: if extraentry != "": extraentry = extraentry.split(',') - + # stale hosts if '--stale' in args: for c_inst in c_list: if c_inst.current_interaction.isstale(): result.append(c_inst) # clean hosts - elif '-c' in args: + elif '-c' in args: for c_inst in c_list: if c_inst.current_interaction.isclean(): result.append(c_inst) # dirty hosts - elif '-d' in args: + elif '-d' in args: for c_inst in c_list: if not c_inst.current_interaction.isclean(): result.append(c_inst) @@ -287,7 +281,7 @@ else: if sort != "": result.sort(lambda x, y: crit_compare(sort, x, y)) - + if fields != "": for c_inst in result: if '-a' in args or c_inst.expiration == None: |