summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/Options.py2
-rw-r--r--src/lib/Server/Admin/Init.py4
-rw-r--r--src/lib/Server/Plugin.py34
-rw-r--r--src/lib/Server/Plugins/Properties.py10
-rwxr-xr-xsrc/sbin/bcfg236
-rwxr-xr-xsrc/sbin/bcfg2-admin8
-rwxr-xr-xsrc/sbin/bcfg2-build-reports142
-rwxr-xr-xsrc/sbin/bcfg2-info115
-rwxr-xr-xsrc/sbin/bcfg2-ping-sweep2
-rwxr-xr-xsrc/sbin/bcfg2-repo-validate188
-rwxr-xr-xsrc/sbin/bcfg2-reports38
11 files changed, 290 insertions, 289 deletions
diff --git a/src/lib/Options.py b/src/lib/Options.py
index f64b491d5..1973e7091 100644
--- a/src/lib/Options.py
+++ b/src/lib/Options.py
@@ -207,6 +207,8 @@ SCHEMA_PATH = Option('Path to XML Schema files', cmd='--schema',
odesc='<schema path>',
default="%s/share/bcfg2/schemas" % DEFAULT_INSTALL_PREFIX,
long_arg=True)
+REQUIRE_SCHEMA = Option("Require property files to have matching schema files",
+ cmd="--require-schema", default=False, long_arg=True)
# Metadata options
MDATA_OWNER = Option('Default Path owner',
diff --git a/src/lib/Server/Admin/Init.py b/src/lib/Server/Admin/Init.py
index 1c12aee24..9771fd10b 100644
--- a/src/lib/Server/Admin/Init.py
+++ b/src/lib/Server/Admin/Init.py
@@ -138,7 +138,7 @@ def create_key(hostname, keypath, certpath, country, state, location):
keypath,
certpath))
subprocess.call((ccstr), shell=True)
- os.chmod(keypath, stat.S_IRUSR | stat.S_IWUSR) # 0600
+ os.chmod(keypath, stat.S_IRUSR|stat.S_IWUSR) # 0600
def create_conf(confpath, confdata):
@@ -156,7 +156,7 @@ def create_conf(confpath, confdata):
return
try:
open(confpath, "w").write(confdata)
- os.chmod(keypath, stat.S_IRUSR | stat.S_IWUSR) # 0600
+ os.chmod(keypath, stat.S_IRUSR|stat.S_IWUSR) # 0600
except Exception, e:
print("Error %s occured while trying to write configuration "
"file to '%s'.\n" %
diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py
index ac9479d44..69b38c4ff 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Server/Plugin.py
@@ -457,17 +457,29 @@ class StructFile(XMLFileBacked):
work = {lambda x: True: xdata.getchildren()}
while work:
(predicate, worklist) = work.popitem()
- self.fragments[predicate] = [item for item in worklist if item.tag != 'Group'
- and not isinstance(item, lxml.etree._Comment)]
- for group in [item for item in worklist if item.tag == 'Group']:
- # if only python had forceable early-binding
- if group.get('negate', 'false') in ['true', 'True']:
- cmd = "lambda x:'%s' not in x.groups and predicate(x)"
- else:
- cmd = "lambda x:'%s' in x.groups and predicate(x)"
-
- newpred = eval(cmd % (group.get('name')), {'predicate': predicate})
- work[newpred] = group.getchildren()
+ self.fragments[predicate] = \
+ [item for item in worklist
+ if (item.tag != 'Group' and
+ item.tag != 'Client' and
+ not isinstance(item,
+ lxml.etree._Comment))]
+ for item in worklist:
+ cmd = None
+ if item.tag == 'Group':
+ if item.get('negate', 'false').lower() == 'true':
+ cmd = "lambda x:'%s' not in x.groups and predicate(x)"
+ else:
+ cmd = "lambda x:'%s' in x.groups and predicate(x)"
+ elif item.tag == 'Client':
+ if item.get('negate', 'false').lower() == 'true':
+ cmd = "lambda x:x.hostname != '%s' and predicate(x)"
+ else:
+ cmd = "lambda x:x.hostname == '%s' and predicate(x)"
+ # else, ignore item
+ if cmd is not None:
+ newpred = eval(cmd % item.get('name'),
+ {'predicate':predicate})
+ work[newpred] = item.getchildren()
def Match(self, metadata):
"""Return matching fragments of independent."""
diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py
index 2888ef1d1..c5d2acc44 100644
--- a/src/lib/Server/Plugins/Properties.py
+++ b/src/lib/Server/Plugins/Properties.py
@@ -4,15 +4,9 @@ import lxml.etree
import Bcfg2.Server.Plugin
-class PropertyFile(Bcfg2.Server.Plugin.XMLFileBacked):
+class PropertyFile(Bcfg2.Server.Plugin.StructFile):
"""Class for properties files."""
-
- def Index(self):
- """Build data into an xml object."""
- try:
- self.data = lxml.etree.XML(self.data)
- except lxml.etree.XMLSyntaxError:
- Bcfg2.Server.Plugin.logger.error("Failed to parse %s" % self.name)
+ pass
class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked):
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: