summaryrefslogtreecommitdiffstats
path: root/src/sbin/bcfg2-info
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbin/bcfg2-info')
-rwxr-xr-xsrc/sbin/bcfg2-info255
1 files changed, 149 insertions, 106 deletions
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 656532155..617584d3d 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,17 +1,17 @@
#!/usr/bin/env python
-
"""This tool loads the Bcfg2 core into an interactive debugger."""
-__revision__ = '$Revision$'
-from code import InteractiveConsole
+import os
+import sys
import cmd
import errno
import getopt
+import fnmatch
import logging
-import lxml.etree
-import os
-import sys
import tempfile
+import lxml.etree
+import traceback
+from code import InteractiveConsole
try:
try:
@@ -27,14 +27,20 @@ import Bcfg2.Logger
import Bcfg2.Options
import Bcfg2.Server.Core
import Bcfg2.Server.Plugins.Metadata
-import Bcfg2.Server.Plugins.SGenshi
import Bcfg2.Server.Plugin
+try:
+ import Bcfg2.Server.Plugins.SGenshi
+ has_genshi = True
+except ImportError:
+ has_genshi = False
+
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
+buildall <directory> [<hostnames*>] - Build configs for all clients in directory
+buildallfile <directory> <filename> [<hostnames*>] - Build config file for all clients in directory
buildfile <filename> <hostname> - Build config file for hostname (not written to disk)
buildbundle <bundle> <hostname> - Render a templated bundle for hostname (not written to disk)
bundles - Print out group/bundle information
@@ -51,8 +57,7 @@ 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"""
+update - Process pending file events"""
BUILDDIR_USAGE = """Usage: builddir [-f] <hostname> <output dir>
@@ -78,10 +83,12 @@ 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):
@@ -90,6 +97,30 @@ class FileNotBuilt(Exception):
def __str__(self):
return repr(self.value)
+
+def getClientList(hostglobs):
+ """ given a host glob, get a list of clients that match it """
+ # special cases to speed things up:
+ if '*' in hostglobs:
+ return list(self.metadata.clients.keys())
+ has_wildcards = False
+ for glob in hostglobs:
+ # check if any wildcard characters are in the string
+ if set('*?[]') & set(glob):
+ has_wildcards = True
+ break
+ if not has_wildcards:
+ return hostglobs
+
+ rv = set()
+ clist = set(self.metadata.clients.keys())
+ for glob in hostglobs:
+ for client in clist:
+ if fnmatch.fnmatch(client, glob):
+ rv.update(client)
+ clist.difference_update(rv)
+ return list(rv)
+
def printTabular(rows):
"""Print data in tabular format."""
cmax = tuple([max([len(str(row[index])) for row in rows]) + 1 \
@@ -109,12 +140,12 @@ def displayTrace(trace, num=80, sort=('time', 'calls')):
class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
"""Main class for bcfg2-info."""
def __init__(self, repo, plgs, passwd, encoding, event_debug,
- cfile='/etc/bcfg2.conf', filemonitor='default'):
+ filemonitor='default', setup=None):
cmd.Cmd.__init__(self)
try:
Bcfg2.Server.Core.Core.__init__(self, repo, plgs, passwd,
- encoding, cfile=cfile,
- filemonitor=filemonitor)
+ encoding, filemonitor=filemonitor,
+ setup=setup)
if event_debug:
self.fam.debug = True
except Bcfg2.Server.Core.CoreInitError:
@@ -177,7 +208,11 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
else:
raise ImportError
except ImportError:
- sh.interact()
+ try:
+ import bpython.cli
+ bpython.cli.main(locals_=locals())
+ except ImportError:
+ sh.interact()
def do_quit(self, _):
"""
@@ -199,10 +234,6 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
"""Process pending filesystem events."""
self.fam.handle_events_in_interval(0.1)
- def do_version(self, _):
- """Print out code version."""
- print(__revision__)
-
def do_build(self, args):
"""Build client configuration."""
alist = args.split()
@@ -260,50 +291,101 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
def do_buildall(self, args):
alist = args.split()
- flags = []
- for arg in alist:
- if arg == '-f':
- alist.remove('-f')
- flags.append(arg)
- if len(alist) != 1:
- print("Usage: buildall [-f] <directory>")
+ if len(alist) < 1:
+ print("Usage: buildall <directory> [<hostnames*>]")
return
- if not os.path.exists(alist[0]):
- try:
- os.mkdir(alist[0])
- except OSError:
- err = sys.exc_info()[1]
- logger.error("Could not create %s: %s" % (alist[0], err))
- for client in self.metadata.clients:
- self.do_build("%s %s %s/%s.xml" % (" ".join(flags),
- client, args, client))
+
+ destdir = alist[0]
+ try:
+ os.mkdir(destdir)
+ except OSError:
+ err = sys.exc_info()[1]
+ if err.errno != 17:
+ print("Could not create %s: %s" % (destdir, err))
+ if len(alist) > 1:
+ clients = getClientList(alist[1:])
+ else:
+ clients = list(self.metadata.clients.keys())
+ for client in clients:
+ self.do_build("%s %s" % (client, os.path.join(destdir,
+ client + ".xml")))
+
+ def do_buildallfile(self, args):
+ """Build a config file for all clients."""
+ usage = 'Usage: buildallfile [--altsrc=<altsrc>] <directory> <filename> [<hostnames*>]'
+ try:
+ opts, args = getopt.gnu_getopt(args.split(), '', ['altsrc='])
+ except:
+ print(usage)
+ return
+ altsrc = None
+ for opt in opts:
+ if opt[0] == '--altsrc':
+ altsrc = opt[1]
+ if len(args) < 2:
+ print(usage)
+ return
+
+ destdir = args[0]
+ filename = args[1]
+ try:
+ os.mkdir(destdir)
+ except OSError:
+ err = sys.exc_info()[1]
+ if err.errno != 17:
+ print("Could not create %s: %s" % (destdir, err))
+ if len(args) > 2:
+ clients = getClientList(args[1:])
+ else:
+ clients = list(self.metadata.clients.keys())
+ if altsrc:
+ args = "--altsrc %s -f %%s %%s %%s" % altsrc
+ else:
+ args = "-f %s %s %s"
+ for client in clients:
+ self.do_buildfile(args % (os.path.join(destdir, client),
+ filename, client))
def do_buildfile(self, args):
"""Build a config file for client."""
- usage = 'Usage: buildfile [--altsrc=<altsrc>] filename hostname'
+ usage = 'Usage: buildfile [-f <outfile>] [--altsrc=<altsrc>] filename hostname'
try:
- opts, alist = getopt.gnu_getopt(args.split(), '', ['altsrc='])
+ opts, alist = getopt.gnu_getopt(args.split(), 'f:', ['altsrc='])
except:
print(usage)
return
altsrc = None
+ outfile = None
for opt in opts:
if opt[0] == '--altsrc':
altsrc = opt[1]
- if len(alist) == 2:
- fname, client = alist
- entry = lxml.etree.Element('Path', type='file', name=fname)
- if altsrc:
- entry.set("altsrc", altsrc)
- try:
- metadata = self.build_metadata(client)
- self.Bind(entry, metadata)
- print(lxml.etree.tostring(entry, encoding="UTF-8",
- xml_declaration=True))
- except:
- print("Failed to build entry %s for host %s" % (fname, client))
- else:
+ elif opt[0] == '-f':
+ outfile = opt[1]
+ if len(alist) != 2:
print(usage)
+ return
+
+ fname, client = alist
+ entry = lxml.etree.Element('Path', type='file', name=fname)
+ if altsrc:
+ entry.set("altsrc", altsrc)
+ try:
+ metadata = self.build_metadata(client)
+ self.Bind(entry, metadata)
+ data = lxml.etree.tostring(entry, encoding="UTF-8",
+ xml_declaration=True)
+ if outfile:
+ open(outfile, 'w').write(data)
+ else:
+ print(data)
+ except IOError:
+ err = sys.exc_info()[1]
+ print("Could not write to %s: %s" % (outfile, err))
+ print(data)
+ except Exception:
+ print("Failed to build entry %s for host %s: %s" %
+ (fname, client, traceback.format_exc().splitlines()[-1]))
+ raise
def do_buildbundle(self, args):
"""Render a bundle for client."""
@@ -313,8 +395,9 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
metadata = self.build_metadata(client)
if bname in self.plugins['Bundler'].entries:
bundle = self.plugins['Bundler'].entries[bname]
- if isinstance(bundle,
- Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile):
+ if (has_genshi and
+ isinstance(bundle,
+ Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile)):
stream = bundle.template.generate(metadata=metadata)
print(stream.render("xml"))
else:
@@ -523,31 +606,8 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
print("Unable to build metadata for host %s" % args)
return
collection = self.plugins['Packages']._get_collection(metadata)
- for source in collection.sources:
- # get_urls() loads url_map as a side-effect
- source.get_urls()
- for url_map in source.url_map:
- for arch in url_map['arches']:
- # make sure client is in all the proper arch groups
- if arch not in metadata.groups:
- continue
- reponame = source.get_repo_name(url_map)
- print("Name: %s" % reponame)
- print(" Type: %s" % source.ptype)
- if url_map['url'] != '':
- print(" URL: %s" % url_map['url'])
- elif url_map['rawurl'] != '':
- print(" RAWURL: %s" % url_map['rawurl'])
- if source.gpgkeys:
- print(" GPG Key(s): %s" % ", ".join(source.gpgkeys))
- else:
- print(" GPG Key(s): None")
- if len(source.blacklist):
- print(" Blacklist: %s" % ", ".join(source.blacklist))
- if len(source.whitelist):
- print(" Whitelist: %s" % ", ".join(source.whitelist))
- print("")
-
+ print collection.sourcelist()
+
def do_profile(self, arg):
"""."""
if not have_profile:
@@ -568,32 +628,16 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
if __name__ == '__main__':
Bcfg2.Logger.setup_logging('bcfg2-info', to_syslog=False)
- optinfo = {
- 'configfile': Bcfg2.Options.CFILE,
- 'help': Bcfg2.Options.HELP,
- 'event debug': Bcfg2.Options.DEBUG,
- 'profile': Bcfg2.Options.CORE_PROFILE,
- 'encoding': Bcfg2.Options.ENCODING,
- # Server options
- 'repo': Bcfg2.Options.SERVER_REPOSITORY,
- 'plugins': Bcfg2.Options.SERVER_PLUGINS,
- 'password': Bcfg2.Options.SERVER_PASSWORD,
- 'mconnect': Bcfg2.Options.SERVER_MCONNECT,
- 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR,
- 'location': Bcfg2.Options.SERVER_LOCATION,
- 'static': Bcfg2.Options.SERVER_STATIC,
- 'key': Bcfg2.Options.SERVER_KEY,
- 'cert': Bcfg2.Options.SERVER_CERT,
- 'ca': Bcfg2.Options.SERVER_CA,
- 'password': Bcfg2.Options.SERVER_PASSWORD,
- 'protocol': Bcfg2.Options.SERVER_PROTOCOL,
- # More options
- 'logging': Bcfg2.Options.LOGGING_FILE_PATH,
- 'interactive': Bcfg2.Options.INTERACTIVE,
- }
+ optinfo = dict(profile=Bcfg2.Options.CORE_PROFILE,
+ mconnect=Bcfg2.Options.SERVER_MCONNECT,
+ interactive=Bcfg2.Options.INTERACTIVE)
+ optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
+ optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
setup = Bcfg2.Options.OptionParser(optinfo)
- setup.hm = "Usage:\n %s\n%s" % (setup.buildHelpMessage(),
- USAGE)
+ setup.hm = "\n".join([" bcfg2-info [options] [command <command args>]",
+ "Options:",
+ setup.buildHelpMessage(),
+ USAGE])
setup.parse(sys.argv[1:])
if setup['args'] and setup['args'][0] == 'help':
@@ -603,15 +647,14 @@ if __name__ == '__main__':
prof = profile.Profile()
loop = prof.runcall(infoCore, setup['repo'], setup['plugins'],
setup['password'], setup['encoding'],
- setup['event debug'], cfile=setup['configfile'],
- filemonitor=setup['filemonitor'])
+ setup['debug'], setup['filemonitor'],
+ setup)
displayTrace(prof)
else:
if setup['profile']:
print("Profiling functionality not available.")
loop = infoCore(setup['repo'], setup['plugins'], setup['password'],
- setup['encoding'], setup['event debug'],
- cfile=setup['configfile'],
- filemonitor=setup['filemonitor'])
+ setup['encoding'], setup['debug'],
+ setup['filemonitor'], setup)
loop.Run(setup['args'])