summaryrefslogtreecommitdiffstats
path: root/src/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbin')
-rwxr-xr-xsrc/sbin/bcfg23
-rwxr-xr-xsrc/sbin/bcfg2-admin18
-rwxr-xr-xsrc/sbin/bcfg2-build-reports2
-rwxr-xr-xsrc/sbin/bcfg2-info276
-rwxr-xr-xsrc/sbin/bcfg2-lint1
-rwxr-xr-xsrc/sbin/bcfg2-ping-sweep2
-rwxr-xr-xsrc/sbin/bcfg2-reports1
-rwxr-xr-xsrc/sbin/bcfg2-server1
-rwxr-xr-x[-rw-r--r--]src/sbin/bcfg2-test107
-rwxr-xr-xsrc/sbin/bcfg2-yum-helper118
10 files changed, 378 insertions, 151 deletions
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index 58f2964f9..fb34e627b 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""Bcfg2 Client"""
-__revision__ = '$Revision$'
import fcntl
import logging
@@ -109,7 +108,7 @@ 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; removed services will only be disabled")
if self.setup['remove'] not in [False,
'all',
'Services',
diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin
index 09117a3f4..d3b06733f 100755
--- a/src/sbin/bcfg2-admin
+++ b/src/sbin/bcfg2-admin
@@ -6,13 +6,12 @@ import logging
import Bcfg2.Server.Core
import Bcfg2.Logger
import Bcfg2.Options
+import Bcfg2.Server.Admin
# Compatibility import
from Bcfg2.Bcfg2Py3k import StringIO
log = logging.getLogger('bcfg2-admin')
-import Bcfg2.Server.Admin
-
def mode_import(modename):
"""Load Bcfg2.Server.Admin.<mode>."""
modname = modename.capitalize()
@@ -42,8 +41,16 @@ def main():
'configfile': Bcfg2.Options.CFILE,
'help': Bcfg2.Options.HELP,
'verbose': Bcfg2.Options.VERBOSE,
+ 'repo': Bcfg2.Options.SERVER_REPOSITORY,
+ 'plugins': Bcfg2.Options.SERVER_PLUGINS,
+ 'event debug': Bcfg2.Options.DEBUG,
+ 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'encoding': Bcfg2.Options.ENCODING,
}
setup = Bcfg2.Options.OptionParser(optinfo)
+ # override default help message to include description of all modes
+ setup.hm = "%s\n%s" % (setup.buildHelpMessage(), create_description())
setup.parse(sys.argv[1:])
log_args = dict(to_syslog=False, to_console=logging.WARNING)
@@ -58,13 +65,12 @@ def main():
setup['args'] = [setup['args'][1], setup['args'][0]]
else:
# Print short help for all modes
- print("Usage:\n %s" % setup.buildHelpMessage())
- print(create_description())
+ print(setup.hm)
raise SystemExit(0)
if setup['args'][0] in get_modes():
modname = setup['args'][0].capitalize()
- if len(setup['args']) > 1 and setup['args'][1] == 'help':
+ if len(setup['args']) > 1 and setup['args'][1] == 'help':
print(mode_import(modname).__longhelp__)
raise SystemExit(0)
try:
@@ -73,7 +79,7 @@ def main():
e = sys.exc_info()[1]
log.error("Failed to load admin mode %s: %s" % (modname, e))
raise SystemExit(1)
- mode = mode_cls(setup['configfile'])
+ mode = mode_cls(setup)
mode(setup['args'][1:])
if hasattr(mode, 'bcore'):
mode.bcore.shutdown()
diff --git a/src/sbin/bcfg2-build-reports b/src/sbin/bcfg2-build-reports
index 7122fb300..7fa08110a 100755
--- a/src/sbin/bcfg2-build-reports
+++ b/src/sbin/bcfg2-build-reports
@@ -4,8 +4,6 @@
bcfg2-build-reports generates & distributes reports of statistic
information for Bcfg2."""
-__revision__ = '$Revision$'
-
import copy
import getopt
import re
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 4412c712a..4fe4ce9d5 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,20 +1,22 @@
#!/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
+from code import InteractiveConsole
try:
- import profile
+ try:
+ import cProfile as profile
+ except:
+ import profile
import pstats
have_profile = True
except:
@@ -31,7 +33,8 @@ 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
@@ -42,12 +45,13 @@ event_debug - Display filesystem events as they are processed
groups - List groups
help - Print this list of available commands
mappings <type*> <name*> - Print generator mappings for optional type and name
+packageresolve <hostname> <package> [<package>...] - Resolve the specified set of packages
+packagesources <hostname> - Show package sources
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>
@@ -73,10 +77,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):
@@ -85,6 +91,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 \
@@ -103,11 +133,11 @@ 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):
+ def __init__(self, repo, plgs, passwd, encoding, event_debug, filemonitor='default'):
cmd.Cmd.__init__(self)
try:
Bcfg2.Server.Core.Core.__init__(self, repo, plgs, passwd,
- encoding)
+ encoding, filemonitor=filemonitor)
if event_debug:
self.fam.debug = True
except Bcfg2.Server.Core.CoreInitError:
@@ -162,8 +192,13 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
print("Dropping to python interpreter; press ^D to resume")
try:
import IPython
- shell = IPython.Shell.IPShell(argv=[], user_ns=locals())
- shell.mainloop()
+ if hasattr(IPython, "Shell"):
+ shell = IPython.Shell.IPShell(argv=[], user_ns=locals())
+ shell.mainloop()
+ elif hasattr(IPython, "embed"):
+ IPython.embed(user_ns=locals())
+ else:
+ raise ImportError
except ImportError:
sh.interact()
@@ -187,10 +222,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()
@@ -204,12 +235,9 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
if not ofile.startswith('/tmp') and not path_force:
print("Refusing to write files outside of /tmp without -f option")
return
- output = open(ofile, 'w')
- data = lxml.etree.tostring(self.BuildConfiguration(client),
+ lxml.etree.ElementTree(self.BuildConfiguration(client)).write(ofile,
encoding='UTF-8', xml_declaration=True,
pretty_print=True)
- output.write(data)
- output.close()
else:
print('Usage: build [-f] <hostname> <output file>')
@@ -250,42 +278,99 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
self.help_builddir()
def do_buildall(self, args):
- if len(args.split()) != 1:
- print("Usage: buildall <directory>")
+ if len(args.split()) < 1:
+ print("Usage: buildall <directory> [<hostnames*>]")
return
+
+ destdir = args[0]
try:
- os.mkdir(args)
- except:
- pass
- for client in self.metadata.clients:
+ 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) > 1:
+ clients = getClientList(args[1:])
+ else:
+ clients = list(self.metadata.clients.keys())
+ for client in clients:
self.do_build("%s %s/%s.xml" % (client, args, client))
+ 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" % (fname, client))
+ raise
def do_buildbundle(self, args):
"""Render a bundle for client."""
@@ -454,7 +539,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
try:
meta = self.build_metadata(args)
except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
- print("Unable to find metadata for host %s" % client)
+ print("Unable to find metadata for host %s" % args)
return
structures = self.GetStructures(meta)
for clist in [struct.findall('Path') for struct in structures]:
@@ -473,6 +558,60 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
continue
print(cand[0].name)
+ def do_packageresolve(self, args):
+ arglist = args.split(" ")
+ if len(arglist) < 2:
+ print("Usage: packageresolve <hostname> <package> [<package>...]")
+ return
+
+ hostname = arglist[0]
+ initial = arglist[1:]
+ metadata = self.build_metadata(hostname)
+ self.plugins['Packages'].toggle_debug()
+ collection = self.plugins['Packages']._get_collection(metadata)
+ packages, unknown = collection.complete(initial)
+ newpkgs = list(packages.difference(initial))
+ print("%d initial packages" % len(initial))
+ print(" %s" % "\n ".join(initial))
+ print("%d new packages added" % len(newpkgs))
+ if newpkgs:
+ print(" %s" % "\n ".join(newpkgs))
+ print("%d unknown packages" % len(unknown))
+ if unknown:
+ print(" %s" % "\n ".join(unknown))
+
+ def do_packagesources(self, args):
+ try:
+ metadata = self.build_metadata(args)
+ except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
+ 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("")
+
def do_profile(self, arg):
"""."""
if not have_profile:
@@ -496,42 +635,43 @@ if __name__ == '__main__':
optinfo = {
'configfile': Bcfg2.Options.CFILE,
'help': Bcfg2.Options.HELP,
- }
- optinfo.update({
- '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
- })
+ '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
+ }
setup = Bcfg2.Options.OptionParser(optinfo)
+ setup.hm = "Usage:\n %s\n%s" % (setup.buildHelpMessage(),
+ USAGE)
+
setup.parse(sys.argv[1:])
if setup['args'] and setup['args'][0] == 'help':
- print(USAGE)
+ print(setup.hm)
sys.exit(0)
elif setup['profile'] and have_profile:
prof = profile.Profile()
loop = prof.runcall(infoCore, setup['repo'], setup['plugins'],
setup['password'], setup['encoding'],
- setup['event debug'])
+ setup['event debug'], setup['filemonitor'])
displayTrace(prof)
else:
if setup['profile']:
print("Profiling functionality not available.")
loop = infoCore(setup['repo'], setup['plugins'], setup['password'],
- setup['encoding'], setup['event debug'])
+ setup['encoding'], setup['event debug'], setup['filemonitor'])
loop.Run(setup['args'])
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
index 2d371f4aa..ad6b6139c 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""This tool examines your Bcfg2 specifications for errors."""
-__revision__ = '$Revision$'
import sys
import inspect
diff --git a/src/sbin/bcfg2-ping-sweep b/src/sbin/bcfg2-ping-sweep
index 70f718690..be8994be3 100755
--- a/src/sbin/bcfg2-ping-sweep
+++ b/src/sbin/bcfg2-ping-sweep
@@ -3,8 +3,6 @@
"""Generates hostinfo.xml at a regular interval."""
-__revision__ = '$Revision$'
-
from os import dup2, execl, fork, uname, wait
import sys
import time
diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports
index 6acdd27e3..1f101b9a7 100755
--- a/src/sbin/bcfg2-reports
+++ b/src/sbin/bcfg2-reports
@@ -1,6 +1,5 @@
#!/usr/bin/env python
"""Query reporting system for client status."""
-__revision__ = '$Revision$'
import os
import sys
diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server
index 546d5a249..89bc7c331 100755
--- a/src/sbin/bcfg2-server
+++ b/src/sbin/bcfg2-server
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""The XML-RPC Bcfg2 server."""
-__revision__ = '$Revision$'
import logging
import os.path
diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test
index a0257ca52..01a2a4893 100644..100755
--- a/src/sbin/bcfg2-test
+++ b/src/sbin/bcfg2-test
@@ -1,41 +1,60 @@
#!/usr/bin/env python
-"""This tool verifies that all clients known to the server build without failures"""
+"""This tool verifies that all clients known to the server build
+without failures"""
import sys
+import fnmatch
+import logging
+import Bcfg2.Logger
import Bcfg2.Server.Core
from nose.core import TestProgram
+from nose.suite import LazySuite
from unittest import TestCase
class ClientTest(TestCase):
"""
- A test case representing the build of all of the configuration for a single host.
- Checks that none of the build config entities has had a failure when it is building.
- Optionally ignores some config files that we know will cause errors (because they
- are private files we don't have access to, for instance)
+ A test case representing the build of all of the configuration for
+ a single host. Checks that none of the build config entities has
+ had a failure when it is building. Optionally ignores some config
+ files that we know will cause errors (because they are private
+ files we don't have access to, for instance)
"""
__test__ = False # Do not collect
- def __init__(self, bcfg2_core, client):
+ def __init__(self, bcfg2_core, client, ignore=None):
TestCase.__init__(self)
self.bcfg2_core = bcfg2_core
self.client = client
+ if ignore is None:
+ self.ignore = dict()
+ else:
+ self.ignore = ignore
+
+ def ignore_entry(self, tag, name):
+ if tag in self.ignore:
+ if name in self.ignore[tag]:
+ return True
+ else:
+ # try wildcard matching
+ for pattern in self.ignore[tag]:
+ if fnmatch.fnmatch(name, pattern):
+ return True
+ return False
def runTest(self):
config = self.bcfg2_core.BuildConfiguration(self.client)
- failures = config.xpath('//*[@failure]')
- def format_failure(failure):
- return "%s(%s): %s" % (
- failure.tag,
- failure.attrib.get('name'),
- failure.attrib.get('failure')
- )
+ failures = []
+ msg = ["Failures:"]
+ for failure in config.xpath('//*[@failure]'):
+ if not self.ignore_entry(failure.tag, failure.get('name')):
+ failures.append(failure)
+ msg.append("%s:%s: %s" % (failure.tag, failure.get("name"),
+ failure.get("failure")))
+
+ assert len(failures) == 0, "\n".join(msg)
- assert len(failures) == 0, "Failures:\n%s" % "\n".join(
- [format_failure(failure) for failure in failures]
- )
-
def __str__(self):
return "ClientTest(%s)" % self.client
@@ -43,25 +62,55 @@ class ClientTest(TestCase):
def main():
optinfo = {
- 'configfile': Bcfg2.Options.CFILE,
- 'help': Bcfg2.Options.HELP,
- 'encoding': Bcfg2.Options.ENCODING,
- 'repo': Bcfg2.Options.SERVER_REPOSITORY,
- 'plugins': Bcfg2.Options.SERVER_PLUGINS,
- 'password': Bcfg2.Options.SERVER_PASSWORD,
- }
+ 'configfile': Bcfg2.Options.CFILE,
+ 'help': Bcfg2.Options.HELP,
+ 'encoding': Bcfg2.Options.ENCODING,
+ 'repo': Bcfg2.Options.SERVER_REPOSITORY,
+ 'plugins': Bcfg2.Options.SERVER_PLUGINS,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'verbose': Bcfg2.Options.VERBOSE,
+ 'noseopts': Bcfg2.Options.TEST_NOSEOPTS,
+ 'ignore': Bcfg2.Options.TEST_IGNORE,
+ }
setup = Bcfg2.Options.OptionParser(optinfo)
+ setup.hm = \
+ "bcfg2-test [options] [client] [client] [...]\nOptions:\n %s" % \
+ setup.buildHelpMessage()
setup.parse(sys.argv[1:])
+
+ if setup['verbose']:
+ Bcfg2.Logger.setup_logging("bcfg2-test", to_syslog=False)
+
core = Bcfg2.Server.Core.Core(
setup['repo'],
setup['plugins'],
setup['password'],
- setup['encoding']
- )
- core.fam.handle_events_in_interval(4)
- suite = [ClientTest(core, client) for client in core.metadata.clients]
+ setup['encoding'],
+ filemonitor='pseudo'
+ )
+
+ ignore = dict()
+ for entry in setup['ignore']:
+ tag, name = entry.split(":")
+ try:
+ ignore[tag].append(name)
+ except KeyError:
+ ignore[tag] = [name]
+
+ def run_tests():
+ core.fam.handle_events_in_interval(0.1)
+
+ if setup['args']:
+ clients = setup['args']
+ else:
+ clients = core.metadata.clients
+
+ for client in clients:
+ logging.info("Building %s" % client)
+ yield ClientTest(core, client, ignore)
- TestProgram(argv=sys.argv[0:1], suite = suite)
+ TestProgram(argv=sys.argv[0:1] + setup['noseopts'],
+ suite=LazySuite(run_tests))
if __name__ == "__main__":
sys.exit(main())
diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper
index 94836d748..2da7c6336 100755
--- a/src/sbin/bcfg2-yum-helper
+++ b/src/sbin/bcfg2-yum-helper
@@ -5,8 +5,6 @@ the right way to get around that in long-running processes it to have
a short-lived helper. No, seriously -- check out the yum-updatesd
code. It's pure madness. """
-__revision__ = '$Revision$'
-
import os
import sys
import yum
@@ -19,7 +17,26 @@ try:
except ImportError:
import simplejson as json
-logger = logging.getLogger('bcfg2-yum-helper')
+LOGGER = None
+
+def get_logger(verbose=0):
+ """ set up logging according to the verbose level given on the
+ command line """
+ global LOGGER
+ if LOGGER is None:
+ LOGGER = logging.getLogger(sys.argv[0])
+ stderr = logging.StreamHandler()
+ if verbose:
+ level = logging.DEBUG
+ else:
+ level = logging.WARNING
+ LOGGER.setLevel(level)
+ LOGGER.addHandler(stderr)
+ syslog = logging.handlers.SysLogHandler("/dev/log")
+ syslog.setFormatter(logging.Formatter("%(name)s: %(message)s"))
+ LOGGER.addHandler(syslog)
+ return LOGGER
+
class DepSolver(object):
def __init__(self, cfgfile, verbose=1):
@@ -28,8 +45,10 @@ class DepSolver(object):
try:
self.yumbase.preconf.debuglevel = verbose
self.yumbase.preconf.fn = cfgfile
+ self.yumbase._getConfig()
except AttributeError:
self.yumbase._getConfig(cfgfile, debuglevel=verbose)
+ self.logger = get_logger(verbose)
def get_groups(self):
try:
@@ -38,7 +57,7 @@ class DepSolver(object):
return ["noarch"]
def set_groups(self, groups):
- self._groups = set(groups).add("noarch")
+ self._groups = set(groups).union(["noarch"])
groups = property(get_groups, set_groups)
@@ -59,13 +78,13 @@ class DepSolver(object):
matches = self.yumbase.pkgSack.returnNewestByName(name=package)
except yum.Errors.PackageSackError:
if not silent:
- logger.warning("Packages: Package '%s' not found" %
- self.get_package_name(package))
+ self.logger.warning("Package '%s' not found" %
+ self.get_package_name(package))
matches = []
except yum.Errors.RepoError:
err = sys.exc_info()[1]
- logger.error("Packages: Temporary failure loading metadata for "
- "'%s': %s" % (self.get_package_name(package), err))
+ self.logger.error("Temporary failure loading metadata for %s: %s" %
+ (self.get_package_name(package), err))
matches = []
pkgs = self._filter_arch(matches)
@@ -83,8 +102,8 @@ class DepSolver(object):
deps.difference_update([dep for dep in deps
if pkg.checkPrco('provides', dep)])
else:
- logger.error("Packages: No package available: %s" %
- self.get_package_name(package))
+ self.logger.error("No package available: %s" %
+ self.get_package_name(package))
return deps
def get_provides(self, required, all=False, silent=False):
@@ -96,16 +115,15 @@ class DepSolver(object):
self.yumbase.whatProvides(*required).returnNewestByNameArch()
except yum.Errors.NoMoreMirrorsRepoError:
err = sys.exc_info()[1]
- logger.error("Packages: Temporary failure loading metadata for "
- "'%s': %s" %
- (self.get_package_name(required), err))
+ self.logger.error("Temporary failure loading metadata for %s: %s" %
+ (self.get_package_name(required), err))
return []
if prov and not all:
prov = self._filter_provides(required, prov)
elif not prov and not silent:
- logger.error("Packages: No package provides %s" %
- self.get_package_name(required))
+ self.logger.error("No package provides %s" %
+ self.get_package_name(required))
return prov
def get_group(self, group, ptype="default"):
@@ -116,11 +134,11 @@ class DepSolver(object):
if self.yumbase.comps.has_group(group):
group = self.yumbase.comps.return_group(group)
else:
- logger.warning("Packages: '%s' is not a valid group" % group)
+ self.logger.warning("%s is not a valid group" % group)
return []
except yum.Errors.GroupsError:
err = sys.exc_info()[1]
- logger.warning("Packages: %s" % err)
+ self.logger.warning(err)
return []
if ptype == "default":
@@ -134,7 +152,7 @@ class DepSolver(object):
elif ptype == "optional" or ptype == "all":
return group.packages
else:
- logger.warning("Unknown group package type '%s'" % ptype)
+ self.logger.warning("Unknown group package type '%s'" % ptype)
return []
def _filter_provides(self, package, providers):
@@ -156,14 +174,24 @@ class DepSolver(object):
# provider of perl(lib).
rv = []
for pkg in providers:
- if self.get_package_object(pkg.name) == pkg:
+ found = self.get_package_object(pkg.name)
+ if found == pkg or found.pkgtup == pkg.pkgtup:
rv.append(pkg)
+ else:
+ self.logger.debug("Skipping %s, not newest (%s)" %
+ (pkg, found))
else:
rv = providers
return [p.name for p in rv]
def _filter_arch(self, packages):
- matching = [pkg for pkg in packages if pkg.arch in self.groups]
+ matching = []
+ for pkg in packages:
+ if pkg.arch in self.groups:
+ matching.append(pkg)
+ else:
+ self.logger.debug("%s has non-matching architecture (%s)" %
+ (pkg, pkg.arch))
if matching:
return matching
else:
@@ -180,10 +208,7 @@ class DepSolver(object):
else:
return str(package)
- def complete(self, packagelist, groups=None):
- if groups is None:
- groups = []
-
+ def complete(self, packagelist):
packages = set()
pkgs = set(packagelist)
requires = set()
@@ -202,12 +227,17 @@ class DepSolver(object):
if not self.is_package(package):
# try this package out as a requirement
+ self.logger.debug("Adding requirement %s" % package)
requires.add((package, None, (None, None, None)))
continue
packages.add(package)
reqs = set(self.get_deps(package)).difference(satisfied)
if reqs:
+ self.logger.debug("Adding requirements for %s: %s" %
+ (package,
+ ",".join([self.get_package_name(r)
+ for r in reqs])))
requires.update(reqs)
reqs_satisfied = set()
@@ -222,8 +252,8 @@ class DepSolver(object):
reqs_satisfied.add(req)
continue
- logger.debug("Packages: Handling requirement '%s'" %
- self.get_package_name(req))
+ self.logger.debug("Handling requirement '%s'" %
+ self.get_package_name(req))
providers = list(set(self.get_provides(req)))
if len(providers) > 1:
# hopefully one of the providing packages is already
@@ -237,16 +267,24 @@ class DepSolver(object):
if len(best) == 1:
providers = best
elif not final_pass:
- # found no "best" package, so defer
+ self.logger.debug("%s has multiple providers: %s" %
+ (self.get_package_name(req),
+ providers))
+ self.logger.debug("No provider is obviously the "
+ "best; deferring")
providers = None
- # else: found no "best" package, but it's the
- # final pass, so include them all
+ else:
+ # found no "best" package, but it's the
+ # final pass, so include them all
+ self.logger.debug("Found multiple providers for %s,"
+ "including all" %
+ self.get_package_name(req))
if providers:
- logger.debug("Packages: Requirement '%s' satisfied by %s" %
- (self.get_package_name(req),
- ",".join([self.get_package_name(p)
- for p in providers])))
+ self.logger.debug("Requirement '%s' satisfied by %s" %
+ (self.get_package_name(req),
+ ",".join([self.get_package_name(p)
+ for p in providers])))
newpkgs = set(providers).difference(packages)
if newpkgs:
for package in newpkgs:
@@ -257,6 +295,8 @@ class DepSolver(object):
reqs_satisfied.add(req)
elif providers is not None:
# nothing provided this requirement at all
+ self.logger.debug("Nothing provides %s" %
+ self.get_package_name(req))
unknown.add(req)
reqs_satisfied.add(req)
# else, defer
@@ -283,7 +323,7 @@ class DepSolver(object):
# many files were deleted. so useful. thanks, yum.
msg = getattr(self.yumbase, "clean%s" % mdtype)()[1][0]
if not msg.startswith("0 "):
- logger.info("Packages: %s" % msg)
+ self.logger.info(msg)
def main():
@@ -291,15 +331,15 @@ def main():
parser.add_option("-c", "--config", help="Config file")
parser.add_option("-v", "--verbose", help="Verbosity level", action="count")
(options, args) = parser.parse_args()
+ logger = get_logger(options.verbose)
try:
cmd = args[0]
except IndexError:
- logger.error("bcfg2-yum-helper: No command given")
+ logger.error("No command given")
return 1
if not os.path.exists(options.config):
- logger.error("bcfg2-yum-helper: Config file %s not found" %
- options.config)
+ logger.error("Config file %s not found" % options.config)
return 1
depsolver = DepSolver(options.config, options.verbose)
@@ -308,8 +348,8 @@ def main():
print json.dumps(True)
elif cmd == "complete":
data = json.loads(sys.stdin.read())
- (packages, unknown) = depsolver.complete(data['packages'],
- groups=data['groups'])
+ depsolver.groups = data['groups']
+ (packages, unknown) = depsolver.complete(data['packages'])
print json.dumps(dict(packages=list(packages),
unknown=list(unknown)))
elif cmd == "is_virtual_package":