summaryrefslogtreecommitdiffstats
path: root/src/sbin
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-08-08 13:35:20 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-08-08 13:35:20 -0400
commit73c61dd6c0c464f06637db750484417bc8bbb6de (patch)
tree01381b324b6e0a44ebe49660752f31a4abca648a /src/sbin
parente1e194a573b3803fa7f45a646bbb36b2f164a3e1 (diff)
parentcd14868d4db8eaa7e9421e1d5fe8653294ac1e38 (diff)
downloadbcfg2-73c61dd6c0c464f06637db750484417bc8bbb6de.tar.gz
bcfg2-73c61dd6c0c464f06637db750484417bc8bbb6de.tar.bz2
bcfg2-73c61dd6c0c464f06637db750484417bc8bbb6de.zip
Merge branch 'options-rewrite'
Conflicts: src/lib/Bcfg2/Client/Frame.py src/lib/Bcfg2/Options.py src/lib/Bcfg2/Server/Admin/Init.py src/lib/Bcfg2/Server/Admin/Xcmd.py src/lib/Bcfg2/Server/BuiltinCore.py src/lib/Bcfg2/Server/Core.py src/lib/Bcfg2/Server/MultiprocessingCore.py src/lib/Bcfg2/Server/Plugin/base.py src/lib/Bcfg2/Server/Plugin/helpers.py src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py src/lib/Bcfg2/Server/Plugins/Packages/Yum.py src/lib/Bcfg2/Server/Plugins/Packages/__init__.py src/lib/Bcfg2/Server/SSLServer.py src/lib/Bcfg2/Utils.py src/lib/Bcfg2/settings.py src/sbin/bcfg2-crypt src/sbin/bcfg2-info src/sbin/bcfg2-lint src/sbin/bcfg2-test src/sbin/bcfg2-yum-helper tools/bcfg2-profile-templates.py
Diffstat (limited to 'src/sbin')
-rwxr-xr-xsrc/sbin/bcfg226
-rwxr-xr-xsrc/sbin/bcfg2-admin90
-rwxr-xr-xsrc/sbin/bcfg2-crypt441
-rwxr-xr-xsrc/sbin/bcfg2-info797
-rwxr-xr-xsrc/sbin/bcfg2-lint209
-rwxr-xr-xsrc/sbin/bcfg2-report-collector14
-rwxr-xr-xsrc/sbin/bcfg2-server86
-rwxr-xr-xsrc/sbin/bcfg2-test318
-rwxr-xr-xsrc/sbin/bcfg2-yum-helper354
9 files changed, 57 insertions, 2278 deletions
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index 62f749b80..eca7c3395 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -2,27 +2,9 @@
"""Bcfg2 Client"""
import sys
-import signal
-from Bcfg2.Client.Client import Client
-from Bcfg2.Options import load_option_parser, CLIENT_COMMON_OPTIONS
-
-
-def cb_sigint_handler(signum, frame):
- """ Exit upon CTRL-C. """
- raise SystemExit(1)
-
-
-def main():
- setup = load_option_parser(CLIENT_COMMON_OPTIONS)
- setup.parse(sys.argv[1:])
-
- if setup['args']:
- print("Bcfg2 takes no arguments, only options")
- print(setup.buildHelpMessage())
- raise SystemExit(1)
-
- signal.signal(signal.SIGINT, cb_sigint_handler)
- return Client().run()
+from Bcfg2.Options import get_parser
+from Bcfg2.Client import Client
if __name__ == '__main__':
- sys.exit(main())
+ get_parser("Bcfg2 client", components=[Client]).parse()
+ sys.exit(Client().run())
diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin
index 0e1e34c60..d57cd8b35 100755
--- a/src/sbin/bcfg2-admin
+++ b/src/sbin/bcfg2-admin
@@ -2,97 +2,11 @@
""" bcfg2-admin is a script that helps to administer a Bcfg2
deployment. """
-import re
import sys
-import logging
-import Bcfg2.Logger
-import Bcfg2.Options
-import Bcfg2.Server.Admin
-from Bcfg2.Compat import StringIO
-
-
-def mode_import(modename):
- """Load Bcfg2.Server.Admin.<mode>."""
- modname = modename.capitalize()
- mod = getattr(__import__("Bcfg2.Server.Admin.%s" %
- (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()
- description = StringIO()
- description.write("Available modes are:\n\n")
- for mode in modes:
- try:
- doc = re.sub(r'\s{2,}', ' ', mode_import(mode).__doc__.strip())
- except (ImportError, SystemExit):
- continue
- description.write((" %-15s %s\n" % (mode, doc)))
- return description.getvalue()
-
-
-def main():
- optinfo = dict()
- optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
- optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
- setup = Bcfg2.Options.load_option_parser(optinfo)
- # override default help message to include description of all modes
- setup.hm = "Usage:\n\n%s\n%s" % (setup.buildHelpMessage(),
- create_description())
- setup.parse(sys.argv[1:])
-
- if setup['debug']:
- level = logging.DEBUG
- elif setup['verbose']:
- level = logging.INFO
- else:
- level = logging.WARNING
- Bcfg2.Logger.setup_logging('bcfg2-admin', to_syslog=setup['syslog'],
- level=level)
-
- log = logging.getLogger('bcfg2-admin')
-
- # Provide help if requested or no args were specified
- if (not setup['args'] or len(setup['args']) < 1 or
- setup['args'][0] == 'help' or setup['help']):
- if len(setup['args']) > 1:
- # Get help for a specific mode by passing it the help argument
- setup['args'] = [setup['args'][1], setup['args'][0]]
- else:
- # Print short help for all modes
- 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':
- mode_cls = mode_import(modname)
- mode_cls.usage(rv=0)
- try:
- mode_cls = mode_import(modname)
- except ImportError:
- err = sys.exc_info()[1]
- log.error("Failed to load admin mode %s: %s" % (modname, err))
- raise SystemExit(1)
- mode = mode_cls()
- try:
- return mode(setup['args'][1:])
- finally:
- mode.shutdown()
- else:
- log.error("Error: Unknown mode '%s'\n" % setup['args'][0])
- print(create_description())
- raise SystemExit(1)
+from Bcfg2.Server.Admin import CLI
if __name__ == '__main__':
try:
- sys.exit(main())
+ sys.exit(CLI().run())
except KeyboardInterrupt:
raise SystemExit(1)
diff --git a/src/sbin/bcfg2-crypt b/src/sbin/bcfg2-crypt
index 9190f1390..26d5eedf1 100755
--- a/src/sbin/bcfg2-crypt
+++ b/src/sbin/bcfg2-crypt
@@ -1,445 +1,8 @@
#!/usr/bin/env python
""" helper for encrypting/decrypting Cfg and Properties files """
-import os
import sys
-import copy
-import select
-import logging
-import lxml.etree
-import Bcfg2.Logger
-import Bcfg2.Options
-from Bcfg2.Server import XMLParser
-from Bcfg2.Compat import input # pylint: disable=W0622
-try:
- import Bcfg2.Server.Encryption
-except ImportError:
- print("Could not import %s. Is M2Crypto installed?" % sys.exc_info()[1])
- raise SystemExit(1)
-
-
-class PassphraseError(Exception):
- """ Exception raised when there's a problem determining the
- passphrase to encrypt or decrypt with """
-
-
-class CryptoTool(object):
- """ Generic decryption/encryption interface base object """
- def __init__(self, filename, setup):
- self.setup = setup
- self.logger = logging.getLogger(self.__class__.__name__)
- self.passphrases = Bcfg2.Server.Encryption.get_passphrases(self.setup)
-
- self.filename = filename
- try:
- self.data = open(self.filename).read()
- except IOError:
- err = sys.exc_info()[1]
- self.logger.error("Error reading %s, skipping: %s" % (filename,
- err))
- return False
-
- self.pname, self.passphrase = self._get_passphrase()
-
- def _get_passphrase(self):
- """ get the passphrase for the current file """
- if (not self.setup.cfp.has_section(
- Bcfg2.Server.Encryption.CFG_SECTION) or
- len(Bcfg2.Server.Encryption.get_passphrases(self.setup)) == 0):
- raise PassphraseError("No passphrases available in %s" %
- self.setup['configfile'])
-
- pname = None
- if self.setup['passphrase']:
- pname = self.setup['passphrase']
-
- if pname:
- if self.setup.cfp.has_option(Bcfg2.Server.Encryption.CFG_SECTION,
- pname):
- passphrase = self.setup.cfp.get(
- Bcfg2.Server.Encryption.CFG_SECTION, pname)
- self.logger.debug("Using passphrase %s specified on command "
- "line" % pname)
- return (pname, passphrase)
- else:
- raise PassphraseError("Could not find passphrase %s in %s" %
- (pname, self.setup['configfile']))
- else:
- pnames = Bcfg2.Server.Encryption.get_passphrases()
- if len(pnames) == 1:
- pname = pnames.keys()[0]
- passphrase = pnames[pname]
- self.logger.info("Using passphrase %s" % pname)
- return (pname, passphrase)
- elif len(pnames) > 1:
- return (None, None)
- raise PassphraseError("No passphrase could be determined")
-
- def get_destination_filename(self, original_filename):
- """ Get the filename where data should be written """
- return original_filename
-
- def write(self, data):
- """ write data to disk """
- new_fname = self.get_destination_filename(self.filename)
- try:
- self._write(new_fname, data)
- self.logger.info("Wrote data to %s" % new_fname)
- return True
- except IOError:
- err = sys.exc_info()[1]
- self.logger.error("Error writing data from %s to %s: %s" %
- (self.filename, new_fname, err))
- return False
-
- def _write(self, filename, data):
- """ Perform the actual write of data. This is separate from
- :func:`CryptoTool.write` so it can be easily
- overridden. """
- open(filename, "wb").write(data)
-
-
-class Decryptor(CryptoTool):
- """ Decryptor interface """
- def decrypt(self):
- """ decrypt the file, returning the encrypted data """
- raise NotImplementedError
-
-
-class Encryptor(CryptoTool):
- """ encryptor interface """
- def encrypt(self):
- """ encrypt the file, returning the encrypted data """
- raise NotImplementedError
-
-
-class CfgEncryptor(Encryptor):
- """ encryptor class for Cfg files """
-
- def __init__(self, filename, setup):
- Encryptor.__init__(self, filename, setup)
- if self.passphrase is None:
- raise PassphraseError("Multiple passphrases found in %s, "
- "specify one on the command line with -p" %
- self.setup['configfile'])
-
- def encrypt(self):
- return Bcfg2.Server.Encryption.ssl_encrypt(
- self.data, self.passphrase,
- Bcfg2.Server.Encryption.get_algorithm(self.setup))
-
- def get_destination_filename(self, original_filename):
- return original_filename + ".crypt"
-
-
-class CfgDecryptor(Decryptor):
- """ Decrypt Cfg files """
-
- def decrypt(self):
- """ decrypt the given file, returning the plaintext data """
- if self.passphrase:
- try:
- return Bcfg2.Server.Encryption.ssl_decrypt(
- self.data, self.passphrase,
- Bcfg2.Server.Encryption.get_algorithm(self.setup))
- except Bcfg2.Server.Encryption.EVPError:
- self.logger.info("Could not decrypt %s with the "
- "specified passphrase" % self.filename)
- return False
- except:
- err = sys.exc_info()[1]
- self.logger.error("Error decrypting %s: %s" %
- (self.filename, err))
- return False
- else: # no passphrase given, brute force
- try:
- return Bcfg2.Server.Encryption.bruteforce_decrypt(
- self.data, passphrases=self.passphrases.values(),
- algorithm=Bcfg2.Server.Encryption.get_algorithm(
- self.setup))
- except Bcfg2.Server.Encryption.EVPError:
- self.logger.info("Could not decrypt %s with any passphrase" %
- self.filename)
- return False
-
- def get_destination_filename(self, original_filename):
- if original_filename.endswith(".crypt"):
- return original_filename[:-6]
- else:
- return Decryptor.get_plaintext_filename(self, original_filename)
-
-
-class PropertiesCryptoMixin(object):
- """ Mixin to provide some common methods for Properties crypto """
- default_xpath = '//*'
-
- def _get_elements(self, xdata):
- """ Get the list of elements to encrypt or decrypt """
- if self.setup['xpath']:
- elements = xdata.xpath(self.setup['xpath'])
- if not elements:
- self.logger.warning("XPath expression %s matched no "
- "elements" % self.setup['xpath'])
- else:
- elements = xdata.xpath(self.default_xpath)
- if not elements:
- elements = list(xdata.getiterator(tag=lxml.etree.Element))
-
- # filter out elements without text data
- for el in elements[:]:
- if not el.text:
- elements.remove(el)
-
- if self.setup['interactive']:
- for element in elements[:]:
- if len(element):
- elt = copy.copy(element)
- for child in elt.iterchildren():
- elt.remove(child)
- else:
- elt = element
- print(lxml.etree.tostring(
- elt,
- xml_declaration=False).decode("UTF-8").strip())
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [],
- 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- ans = input("Encrypt this element? [y/N] ")
- if not ans.lower().startswith("y"):
- elements.remove(element)
- return elements
-
- def _get_element_passphrase(self, element):
- """ Get the passphrase to use to encrypt or decrypt a given
- element """
- pname = element.get("encrypted")
- if pname in self.passphrases:
- passphrase = self.passphrases[pname]
- elif self.passphrase:
- if pname:
- self.logger.warning("Passphrase %s not found in %s, "
- "using passphrase given on command line"
- % (pname, self.setup['configfile']))
- passphrase = self.passphrase
- pname = self.pname
- else:
- raise PassphraseError("Multiple passphrases found in %s, "
- "specify one on the command line with -p" %
- self.setup['configfile'])
- return (pname, passphrase)
-
- def _write(self, filename, data):
- """ Write the data """
- data.getroottree().write(filename,
- xml_declaration=False,
- pretty_print=True)
-
-
-class PropertiesEncryptor(Encryptor, PropertiesCryptoMixin):
- """ encryptor class for Properties files """
-
- def encrypt(self):
- xdata = lxml.etree.XML(self.data, parser=XMLParser)
- for elt in self._get_elements(xdata):
- try:
- pname, passphrase = self._get_element_passphrase(elt)
- except PassphraseError:
- self.logger.error(str(sys.exc_info()[1]))
- return False
- elt.text = Bcfg2.Server.Encryption.ssl_encrypt(
- elt.text, passphrase,
- Bcfg2.Server.Encryption.get_algorithm(self.setup)).strip()
- elt.set("encrypted", pname)
- return xdata
-
- def _write(self, filename, data):
- PropertiesCryptoMixin._write(self, filename, data)
-
-
-class PropertiesDecryptor(Decryptor, PropertiesCryptoMixin):
- """ decryptor class for Properties files """
- default_xpath = '//*[@encrypted]'
-
- def decrypt(self):
- xdata = lxml.etree.XML(self.data, parser=XMLParser)
- for elt in self._get_elements(xdata):
- try:
- pname, passphrase = self._get_element_passphrase(elt)
- except PassphraseError:
- self.logger.error(str(sys.exc_info()[1]))
- return False
- decrypted = Bcfg2.Server.Encryption.ssl_decrypt(
- elt.text, passphrase,
- Bcfg2.Server.Encryption.get_algorithm(self.setup)).strip()
- try:
- elt.text = decrypted.encode('ascii', 'xmlcharrefreplace')
- elt.set("encrypted", pname)
- except UnicodeDecodeError:
- # we managed to decrypt the value, but it contains
- # content that can't even be encoded into xml
- # entities. what probably happened here is that we
- # coincidentally could decrypt a value encrypted with
- # a different key, and wound up with gibberish.
- self.logger.warning("Decrypted %s to gibberish, skipping" %
- elt.tag)
- return xdata
-
- def _write(self, filename, data):
- PropertiesCryptoMixin._write(self, filename, data)
-
-
-def main(): # pylint: disable=R0912,R0915
- optinfo = dict(interactive=Bcfg2.Options.INTERACTIVE)
- optinfo.update(Bcfg2.Options.CRYPT_OPTIONS)
- optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
- setup = Bcfg2.Options.load_option_parser(optinfo)
- setup.hm = " bcfg2-crypt [options] <filename>\nOptions:\n%s" % \
- setup.buildHelpMessage()
- setup.parse()
-
- if not setup['args']:
- print(setup.hm)
- raise SystemExit(1)
-
- log_args = dict(to_syslog=setup['syslog'], to_console=logging.WARNING)
- if setup['verbose']:
- log_args['to_console'] = logging.DEBUG
- Bcfg2.Logger.setup_logging('bcfg2-crypt', **log_args)
- logger = logging.getLogger('bcfg2-crypt')
-
- if setup['decrypt']:
- if setup['encrypt']:
- logger.error("You cannot specify both --encrypt and --decrypt")
- raise SystemExit(1)
- elif setup['remove']:
- logger.error("--remove cannot be used with --decrypt, ignoring")
- setup['remove'] = Bcfg2.Options.CRYPT_REMOVE.default
- elif setup['interactive']:
- logger.error("Cannot decrypt interactively")
- setup['interactive'] = False
-
- if setup['cfg']:
- if setup['properties']:
- logger.error("You cannot specify both --cfg and --properties")
- raise SystemExit(1)
- if setup['xpath']:
- logger.error("Specifying --xpath with --cfg is nonsensical, "
- "ignoring --xpath")
- setup['xpath'] = Bcfg2.Options.CRYPT_XPATH.default
- if setup['interactive']:
- logger.error("You cannot use interactive mode with --cfg, "
- "ignoring -I")
- setup['interactive'] = False
- elif setup['properties']:
- if setup['remove']:
- logger.error("--remove cannot be used with --properties, ignoring")
- setup['remove'] = Bcfg2.Options.CRYPT_REMOVE.default
-
- for fname in setup['args']:
- if not os.path.exists(fname):
- logger.error("%s does not exist, skipping" % fname)
- continue
-
- # figure out if we need to encrypt this as a Properties file
- # or as a Cfg file
- props = False
- if setup['properties']:
- props = True
- elif setup['cfg']:
- props = False
- elif fname.endswith(".xml"):
- try:
- xroot = lxml.etree.parse(fname).getroot()
- if xroot.tag == "Properties":
- props = True
- else:
- props = False
- except IOError:
- err = sys.exc_info()[1]
- logger.error("Error reading %s, skipping: %s" % (fname, err))
- continue
- except lxml.etree.XMLSyntaxError:
- props = False
- else:
- props = False
-
- if props:
- if setup['remove']:
- logger.info("Cannot use --remove with Properties file %s, "
- "ignoring for this file" % fname)
- tools = (PropertiesEncryptor, PropertiesDecryptor)
- else:
- if setup['xpath']:
- logger.info("Cannot use xpath with Cfg file %s, ignoring "
- "xpath for this file" % fname)
- if setup['interactive']:
- logger.info("Cannot use interactive mode with Cfg file %s, "
- "ignoring -I for this file" % fname)
- tools = (CfgEncryptor, CfgDecryptor)
-
- data = None
- mode = None
- if setup['encrypt']:
- try:
- tool = tools[0](fname, setup)
- except PassphraseError:
- logger.error(str(sys.exc_info()[1]))
- return 2
- mode = "encrypt"
- elif setup['decrypt']:
- try:
- tool = tools[1](fname, setup)
- except PassphraseError:
- logger.error(str(sys.exc_info()[1]))
- return 2
- mode = "decrypt"
- else:
- logger.info("Neither --encrypt nor --decrypt specified, "
- "determining mode")
- try:
- tool = tools[1](fname, setup)
- except PassphraseError:
- logger.error(str(sys.exc_info()[1]))
- return 2
-
- try:
- data = tool.decrypt()
- mode = "decrypt"
- except: # pylint: disable=W0702
- pass
- if data is False:
- data = None
- logger.info("Failed to decrypt %s, trying encryption" % fname)
- try:
- tool = tools[0](fname, setup)
- except PassphraseError:
- logger.error(str(sys.exc_info()[1]))
- return 2
- mode = "encrypt"
-
- if data is None:
- data = getattr(tool, mode)()
- if not data:
- logger.error("Failed to %s %s, skipping" % (mode, fname))
- continue
- if setup['crypt_stdout']:
- if len(setup['args']) > 1:
- print("----- %s -----" % fname)
- print(data)
- if len(setup['args']) > 1:
- print("")
- else:
- tool.write(data)
-
- if (setup['remove'] and
- tool.get_destination_filename(fname) != fname):
- try:
- os.unlink(fname)
- except IOError:
- err = sys.exc_info()[1]
- logger.error("Error removing %s: %s" % (fname, err))
- continue
+from Bcfg2.Server.Encryption import CLI
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(CLI().run())
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 9e3a671da..adfa96852 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,801 +1,8 @@
#!/usr/bin/env python
"""This tool loads the Bcfg2 core into an interactive debugger."""
-import os
-import re
import sys
-import cmd
-import getopt
-import fnmatch
-import logging
-import lxml.etree
-import traceback
-from code import InteractiveConsole
-import Bcfg2.Logger
-import Bcfg2.Options
-import Bcfg2.Server.Core
-import Bcfg2.Server.Plugin
-import Bcfg2.Client.Tools.POSIX
-from Bcfg2.Compat import unicode # pylint: disable=W0622
-
-try:
- try:
- import cProfile as profile
- except ImportError:
- import profile
- import pstats
- HAS_PROFILE = True
-except ImportError:
- HAS_PROFILE = False
-
-
-class MockLog(object):
- """ Fake logger that just discards all messages in order to mask
- errors from builddir being unable to chown files it creates """
- def error(self, *args, **kwargs):
- """ discard error messages """
- pass
-
- def warning(self, *args, **kwargs):
- """ discard warning messages """
- pass
-
- def info(self, *args, **kwargs):
- """ discard info messages """
- pass
-
- def debug(self, *args, **kwargs):
- """ discard debug messages """
- 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 print_tabular(rows):
- """Print data in tabular format."""
- cmax = tuple([max([len(str(row[index])) for row in rows]) + 1
- for index in range(len(rows[0]))])
- fstring = (" %%-%ss |" * len(cmax)) % cmax
- fstring = ('|'.join([" %%-%ss "] * len(cmax))) % cmax
- print(fstring % rows[0])
- print((sum(cmax) + (len(cmax) * 2) + (len(cmax) - 1)) * '=')
- for row in rows[1:]:
- print(fstring % row)
-
-
-def display_trace(trace):
- """ display statistics from a profile trace """
- stats = pstats.Stats(trace)
- stats.sort_stats('cumulative', 'calls', 'time')
- stats.print_stats(200)
-
-
-def load_interpreters():
- """ Load a dict of available Python interpreters """
- interpreters = dict(python=lambda v: InteractiveConsole(v).interact())
- best = "python"
- try:
- import bpython.cli
- interpreters["bpython"] = lambda v: bpython.cli.main(args=[],
- locals_=v)
- best = "bpython"
- except ImportError:
- pass
-
- try:
- # whether ipython is actually better than bpython is
- # up for debate, but this is the behavior that existed
- # before --interpreter was added, so we call IPython
- # better
- import IPython
- # pylint: disable=E1101
- if hasattr(IPython, "Shell"):
- interpreters["ipython"] = lambda v: \
- IPython.Shell.IPShell(argv=[], user_ns=v).mainloop()
- best = "ipython"
- elif hasattr(IPython, "embed"):
- interpreters["ipython"] = lambda v: IPython.embed(user_ns=v)
- best = "ipython"
- else:
- print("Unknown IPython API version")
- # pylint: enable=E1101
- except ImportError:
- pass
-
- interpreters['best'] = interpreters[best]
- return interpreters
-
-
-class InfoCore(cmd.Cmd, Bcfg2.Server.Core.BaseCore):
- """Main class for bcfg2-info."""
- doc_header = "bcfg2-info commands (type help <command>):"
- prompt = 'bcfg2-info> '
-
- def __init__(self):
- cmd.Cmd.__init__(self)
- self.setup = Bcfg2.Options.get_option_parser()
- self.setup.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
- self.setup.update(dict(interpreter=Bcfg2.Options.INTERPRETER))
- Bcfg2.Server.Core.BaseCore.__init__(self)
-
- def _get_client_list(self, hostglobs):
- """ given a host glob, get a list of clients that match it """
- # special cases to speed things up:
- if '*' in hostglobs:
- return self.metadata.clients
- 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)
- for glob in hostglobs:
- for client in clist:
- if fnmatch.fnmatch(client, glob):
- rv.update(client)
- clist.difference_update(rv)
- return list(rv)
-
- def do_debug(self, args):
- """debug [-n] [-f <command list>]
- Shell out to native python interpreter"""
- try:
- opts, _ = getopt.getopt(args.split(), 'nf:')
- except getopt.GetoptError:
- print(str(sys.exc_info()[1]))
- print(self.do_debug.__doc__)
- return
- scriptmode = False
- interactive = True
- for opt in opts:
- if opt[0] == '-f':
- scriptmode = True
- spath = opt[1]
- elif opt[0] == '-n':
- interactive = False
- if scriptmode:
- console = InteractiveConsole(locals())
- for command in [c.strip() for c in open(spath).readlines()]:
- if command:
- console.push(command)
- if interactive:
- interpreters = load_interpreters()
- if self.setup['interpreter'] in interpreters:
- print("Dropping to %s interpreter; press ^D to resume" %
- self.setup['interpreter'])
- interpreters[self.setup['interpreter']](locals())
- else:
- self.logger.error("Invalid interpreter %s" %
- self.setup['interpreter'])
- self.logger.error("Valid interpreters are: %s" %
- ", ".join(interpreters.keys()))
-
- def do_quit(self, _):
- """quit|exit
- Exit program"""
- print("") # put user's prompt on a new line
- self.shutdown()
- os._exit(0) # pylint: disable=W0212
-
- do_EOF = do_quit
- do_exit = do_quit
-
- def do_update(self, _):
- """update
- Process pending filesystem events"""
- self.fam.handle_events_in_interval(0.1)
-
- def do_build(self, args):
- """build [-f] <hostname> <filename>
- Build config for hostname, writing to filename"""
- alist = args.split()
- path_force = False
- for arg in alist:
- if arg == '-f':
- alist.remove('-f')
- path_force = True
- if len(alist) == 2:
- client, ofile = alist
- if not ofile.startswith('/tmp') and not path_force:
- print("Refusing to write files outside of /tmp without -f "
- "option")
- return
- try:
- lxml.etree.ElementTree(self.BuildConfiguration(client)).write(
- ofile,
- encoding='UTF-8', xml_declaration=True,
- pretty_print=True)
- except IOError:
- err = sys.exc_info()[1]
- print("Failed to write File %s: %s" % (ofile, err))
- else:
- print(self.do_build.__doc__)
-
- def help_builddir(self):
- """Display help for builddir command."""
- print("""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.""")
-
- def do_builddir(self, args):
- """ builddir [-f] <hostname> <dirname>
- Build config for hostname, writing separate files to dirname"""
- alist = args.split()
- path_force = False
- if '-f' in args:
- alist.remove('-f')
- path_force = True
- if len(alist) == 2:
- client, odir = alist
- if not odir.startswith('/tmp') and not path_force:
- print("Refusing to write files outside of /tmp without -f "
- "option")
- return
- client_config = self.BuildConfiguration(client)
- if client_config.tag == 'error':
- print("Building client configuration failed.")
- return
-
- for struct in client_config:
- for entry in struct:
- if entry.tag == 'Path':
- entry.set('name', odir + '/' + entry.get('name'))
-
- posix = Bcfg2.Client.Tools.POSIX.POSIX(MockLog(),
- self.setup,
- client_config)
- states = posix.Inventory()
- posix.Install(list(states.keys()))
- else:
- print('Error: Incorrect number of parameters.')
- print(self.do_builddir.__doc__)
-
- def do_buildall(self, args):
- """buildall <directory> [<hostnames*>]
- Build configs for all clients in directory"""
- alist = args.split()
- if len(alist) < 1:
- print(self.do_buildall.__doc__)
- return
-
- 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 = self._get_client_list(alist[1:])
- else:
- clients = self.metadata.clients
- for client in clients:
- self.do_build("%s %s" % (client, os.path.join(destdir,
- client + ".xml")))
-
- def do_buildallfile(self, args):
- """ buildallfile <directory> <filename> [<hostnames*>]
- Build config file for all clients in directory"""
- try:
- opts, args = getopt.gnu_getopt(args.split(), '', ['altsrc='])
- except getopt.GetoptError:
- print(str(sys.exc_info()[1]))
- print(self.do_buildallfile.__doc__)
- return
- altsrc = None
- for opt in opts:
- if opt[0] == '--altsrc':
- altsrc = opt[1]
- if len(args) < 2:
- print(self.do_buildallfile.__doc__)
- 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 = self._get_client_list(args[1:])
- else:
- clients = self.metadata.clients
- 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):
- """buildfile [-f <outfile>] [--altsrc=<altsrc>] <filename> <hostname>
- Build config file for hostname (not written to disk)"""
- try:
- opts, alist = getopt.gnu_getopt(args.split(), 'f:', ['altsrc='])
- except getopt.GetoptError:
- print(str(sys.exc_info()[1]))
- print(self.do_buildfile.__doc__)
- return
- altsrc = None
- outfile = None
- for opt in opts:
- if opt[0] == '--altsrc':
- altsrc = opt[1]
- elif opt[0] == '-f':
- outfile = opt[1]
- if len(alist) != 2:
- print(self.do_buildfile.__doc__)
- 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,
- xml_declaration=False).decode('UTF-8')
- except Exception:
- print("Failed to build entry %s for host %s: %s" %
- (fname, client, traceback.format_exc().splitlines()[-1]))
- raise
- try:
- 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)
-
- def do_buildbundle(self, args):
- """buildbundle <bundle> <hostname>
- Render a templated bundle for hostname (not written to disk)"""
- if len(args.split()) != 2:
- print(self.do_buildbundle.__doc__)
- return
-
- bname, client = args.split()
- try:
- metadata = self.build_metadata(client)
- bundle = self.plugins['Bundler'].entries[bname]
- print(lxml.etree.tostring(bundle.get_xml_value(metadata),
- xml_declaration=False,
- pretty_print=True).decode('UTF-8'))
- except KeyError:
- print("No such bundle %s" % bname)
- except: # pylint: disable=W0702
- err = sys.exc_info()[1]
- print("Failed to render bundle %s for host %s: %s" % (bname,
- client,
- err))
-
- def do_automatch(self, args):
- """automatch [-f] <propertyfile> <hostname>
- Perform automatch on a Properties file"""
- alist = args.split()
- force = False
- for arg in alist:
- if arg == '-f':
- alist.remove('-f')
- force = True
- if len(alist) != 2:
- print(self.do_automatch.__doc__)
- return
-
- if 'Properties' not in self.plugins:
- print("Properties plugin not enabled")
- return
-
- pname, client = alist
- automatch = self.setup.cfp.getboolean("properties", "automatch",
- default=False)
- pfile = self.plugins['Properties'].entries[pname]
- if (not force and
- not automatch and
- pfile.xdata.get("automatch", "false").lower() != "true"):
- print("Automatch not enabled on %s" % pname)
- else:
- metadata = self.build_metadata(client)
- print(lxml.etree.tostring(pfile.XMLMatch(metadata),
- xml_declaration=False,
- pretty_print=True).decode('UTF-8'))
-
- def do_bundles(self, _):
- """bundles
- Print out group/bundle info"""
- data = [('Group', 'Bundles')]
- groups = list(self.metadata.groups.keys())
- groups.sort()
- for group in groups:
- data.append((group,
- ','.join(self.metadata.groups[group][0])))
- print_tabular(data)
-
- def do_clients(self, _):
- """clients
- Print out client/profile info"""
- data = [('Client', 'Profile')]
- for client in sorted(self.metadata.list_clients()):
- imd = self.metadata.get_initial_metadata(client)
- data.append((client, imd.profile))
- print_tabular(data)
-
- def do_config(self, _):
- """config
- Print out the current configuration of Bcfg2"""
- output = [
- ('Description', 'Value'),
- ('Path Bcfg2 repository', self.setup['repo']),
- ('Plugins', self.setup['plugins']),
- ('Password', self.setup['password']),
- ('Filemonitor', self.setup['filemonitor']),
- ('Server address', self.setup['location']),
- ('Path to key', self.setup['key']),
- ('Path to SSL certificate', self.setup['cert']),
- ('Path to SSL CA certificate', self.setup['ca']),
- ('Protocol', self.setup['protocol']),
- ('Logging', self.setup['logging'])]
- print_tabular(output)
-
- def do_expirecache(self, args):
- """ expirecache [<hostname> [<hostname> ...]]- Expire the
- metadata cache """
- alist = args.split()
- if len(alist):
- for client in self._get_client_list(alist):
- self.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata,
- key=client)
- else:
- self.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata)
-
- def do_probes(self, args):
- """probes [-p] <hostname>
- Get probe list for the given host, in XML (the default) \
-or human-readable pretty (with -p) format"""
- alist = args.split()
- pretty = False
- if '-p' in alist:
- pretty = True
- alist.remove('-p')
- if len(alist) != 1:
- print(self.do_probes.__doc__)
- return
- hostname = alist[0]
- if pretty:
- probes = []
- else:
- probes = lxml.etree.Element('probes')
- metadata = self.build_metadata(hostname)
- for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Probing):
- for probe in plugin.GetProbes(metadata):
- probes.append(probe)
- if pretty:
- for probe in probes:
- pname = probe.get("name")
- print("=" * (len(pname) + 2))
- print(" %s" % pname)
- print("=" * (len(pname) + 2))
- print("")
- print(probe.text)
- print("")
- else:
- print(lxml.etree.tostring(probes,
- xml_declaration=False,
- pretty_print=True).decode('UTF-8'))
-
- def do_showentries(self, args):
- """showentries <hostname> <type>
- Show abstract configuration entries for a given host"""
- arglen = len(args.split())
- if arglen not in [1, 2]:
- print(self.do_showentries.__doc__)
- return
- client = args.split()[0]
- try:
- meta = self.build_metadata(client)
- except Bcfg2.Server.Plugin.MetadataConsistencyError:
- print("Unable to find metadata for host %s" % client)
- return
- structures = self.GetStructures(meta)
- output = [('entrytype', 'name')]
- if arglen == 1:
- for item in structures:
- for child in item.getchildren():
- output.append((child.tag, child.get('name')))
- if arglen == 2:
- etype = args.split()[1]
- for item in structures:
- for child in item.getchildren():
- if child.tag in [etype, "Bound%s" % etype]:
- output.append((child.tag, child.get('name')))
- print_tabular(output)
-
- def do_groups(self, _):
- """groups
- Print out group info"""
- data = [("Groups", "Profile", "Category")]
- grouplist = list(self.metadata.groups.keys())
- grouplist.sort()
- for group in grouplist:
- if self.metadata.groups[group].is_profile:
- prof = 'yes'
- else:
- prof = 'no'
- cat = self.metadata.groups[group].category
- data.append((group, prof, cat))
- print_tabular(data)
-
- def do_showclient(self, args):
- """showclient <client> [<client> ...]
- Show metadata for the given hosts"""
- if not len(args):
- print(self.do_showclient.__doc__)
- return
- for client in args.split():
- try:
- client_meta = self.build_metadata(client)
- except Bcfg2.Server.Plugin.MetadataConsistencyError:
- print("Client %s not defined" % client)
- continue
- fmt = "%-10s %s"
- print(fmt % ("Hostname:", client_meta.hostname))
- print(fmt % ("Profile:", client_meta.profile))
-
- group_fmt = "%-10s %-30s %s"
- header = False
- for group in list(client_meta.groups):
- category = ""
- for cat, grp in client_meta.categories.items():
- if grp == group:
- category = "Category: %s" % cat
- break
- if not header:
- print(group_fmt % ("Groups:", group, category))
- header = True
- else:
- print(group_fmt % ("", group, category))
-
- if client_meta.bundles:
- print(fmt % ("Bundles:", list(client_meta.bundles)[0]))
- for bnd in list(client_meta.bundles)[1:]:
- print(fmt % ("", bnd))
- if client_meta.connectors:
- print("Connector data")
- print("=" * 80)
- for conn in client_meta.connectors:
- if getattr(client_meta, conn):
- print(fmt % (conn + ":", getattr(client_meta, conn)))
- print("=" * 80)
-
- def do_mappings(self, args):
- """mappings <type*> <name*>
- Print generator mappings for optional type and name"""
- # Dump all mappings unless type specified
- data = [('Plugin', 'Type', 'Name')]
- arglen = len(args.split())
- for generator in self.plugins_by_type(Bcfg2.Server.Plugin.Generator):
- if arglen == 0:
- etypes = list(generator.Entries.keys())
- else:
- etypes = [args.split()[0]]
- if arglen == 2:
- interested = [(etype, [args.split()[1]])
- for etype in etypes]
- else:
- 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
- generator.Entries.get(etype, {})]:
- data.append((generator.name, etype, name))
- print_tabular(data)
-
- def do_event_debug(self, _):
- """event_debug
- Display filesystem events as they are processed"""
- self.fam.debug = True
-
- def do_packageresolve(self, args):
- """packageresolve <hostname> [<package> [<package>...]]
- Resolve packages for the given host, optionally specifying a \
-set of packages"""
- arglist = args.split(" ")
- if len(arglist) < 1:
- print(self.do_packageresolve.__doc__)
- return
-
- try:
- pkgs = self.plugins['Packages']
- except KeyError:
- print("Packages plugin not enabled")
- return
- pkgs.toggle_debug()
-
- hostname = arglist[0]
- metadata = self.build_metadata(hostname)
-
- indep = lxml.etree.Element("Independent")
- if len(arglist) > 1:
- structures = [lxml.etree.Element("Bundle", name="packages")]
- for arg in arglist[1:]:
- lxml.etree.SubElement(structures[0], "Package", name=arg)
- else:
- structures = self.GetStructures(metadata)
-
- pkgs._build_packages(metadata, indep, # pylint: disable=W0212
- structures)
- print("%d new packages added" % len(indep.getchildren()))
- if len(indep.getchildren()):
- print(" %s" % "\n ".join(
- lxml.etree.tostring(p, encoding=unicode)
- for p in indep.getchildren()))
-
- def do_packagesources(self, args):
- """packagesources <hostname>
- Show package sources"""
- if not args:
- print(self.do_packagesources.__doc__)
- return
- if 'Packages' not in self.plugins:
- print("Packages plugin not enabled")
- return
- try:
- metadata = self.build_metadata(args)
- except Bcfg2.Server.Plugin.MetadataConsistencyError:
- print("Unable to build metadata for host %s" % args)
- return
- collection = self.plugins['Packages'].get_collection(metadata)
- print(collection.sourcelist())
-
- def do_query(self, args):
- """query <-g group|-p profile|-b bundle>
- Query clients"""
- if len(args) == 0:
- print("\n".join(self.metadata.clients))
- return
- arglist = args.split(" ")
- if len(arglist) != 2:
- print(self.do_query.__doc__)
- return
-
- qtype, qparam = arglist
- if qtype == '-p':
- res = self.metadata.get_client_names_by_profiles(qparam.split(','))
- elif qtype == '-g':
- res = self.metadata.get_client_names_by_groups(qparam.split(','))
- elif qtype == '-b':
- res = self.metadata.get_client_names_by_bundles(qparam.split(','))
- else:
- print(self.do_query.__doc__)
- return
- print("\n".join(res))
-
- def do_profile(self, arg):
- """profile <command> <args>
- Profile a single bcfg2-info command"""
- if not HAS_PROFILE:
- print("Profiling functionality not available.")
- return
- if len(arg) == 0:
- print(self.do_profile.__doc__)
- return
- prof = profile.Profile()
- prof.runcall(self.onecmd, arg)
- display_trace(prof)
-
- def run(self, args): # pylint: disable=W0221
- try:
- self.load_plugins()
- self.block_for_fam_events(handle_events=True)
- if args:
- self.onecmd(" ".join(args))
- else:
- try:
- self.cmdloop('Welcome to bcfg2-info\n'
- 'Type "help" for more information')
- except KeyboardInterrupt:
- print("\nCtrl-C pressed exiting...")
- self.do_exit([])
- except Bcfg2.Server.Plugin.PluginExecutionError:
- pass
- finally:
- self.shutdown()
-
- def _daemonize(self):
- pass
-
- def _run(self):
- pass
-
- def _block(self):
- pass
-
-
-def build_usage():
- """build usage message"""
- cmd_blacklist = ["do_loop", "do_EOF"]
- usage = dict()
- for attrname in dir(InfoCore):
- attr = getattr(InfoCore, attrname)
-
- # shim for python 2.4, __func__ is im_func
- funcattr = getattr(attr, "__func__", getattr(attr, "im_func", None))
- if (funcattr is not None and
- funcattr.func_name not in cmd_blacklist and
- funcattr.func_name.startswith("do_") and
- funcattr.func_doc):
- usage[attr.__name__] = re.sub(r'\s+', ' ', attr.__doc__)
- return "Commands:\n" + "\n".join(usage[k] for k in sorted(usage.keys()))
-
-
-USAGE = build_usage()
-
-
-def main():
- optinfo = dict(profile=Bcfg2.Options.CORE_PROFILE,
- interactive=Bcfg2.Options.INTERACTIVE,
- interpreter=Bcfg2.Options.INTERPRETER,
- command_timeout=Bcfg2.Options.CLIENT_COMMAND_TIMEOUT)
- optinfo.update(Bcfg2.Options.INFO_COMMON_OPTIONS)
- setup = Bcfg2.Options.OptionParser(optinfo)
- setup.hm = "\n".join(["bcfg2-info [options] [command <command args>]",
- "Options:",
- setup.buildHelpMessage(),
- USAGE])
-
- setup.parse(sys.argv[1:])
-
- if setup['debug']:
- level = logging.DEBUG
- elif setup['verbose']:
- level = logging.INFO
- else:
- level = logging.WARNING
- Bcfg2.Logger.setup_logging('bcfg2-info', to_syslog=False, level=level)
-
- if setup['args'] and setup['args'][0] == 'help':
- print(setup.hm)
- sys.exit(0)
- elif setup['profile'] and HAS_PROFILE:
- prof = profile.Profile()
- loop = prof.runcall(InfoCore)
- display_trace(prof)
- else:
- if setup['profile']:
- print("Profiling functionality not available.")
- loop = InfoCore()
-
- loop.run(setup['args'])
-
+from Bcfg2.Server.Info import CLI
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(CLI().run())
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
index 27d8d4291..e818dc3be 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -1,213 +1,8 @@
#!/usr/bin/env python
-
"""This tool examines your Bcfg2 specifications for errors."""
import sys
-import time
-import inspect
-import logging
-import Bcfg2.Logger
-import Bcfg2.Options
-import Bcfg2.Server.Core
-import Bcfg2.Server.Lint
-
-LOGGER = logging.getLogger('bcfg2-lint')
-
-
-def run_serverless_plugins(plugins, errorhandler=None, files=None):
- """ Run serverless plugins """
- LOGGER.debug("Running serverless plugins")
- for plugin_name, plugin in list(plugins.items()):
- run_plugin(plugin, plugin_name, errorhandler=errorhandler, files=files)
-
-
-def run_server_plugins(plugins, errorhandler=None, files=None):
- """ run plugins that require a running server to run """
- core = load_server()
- try:
- LOGGER.debug("Running server plugins")
- for plugin_name, plugin in list(plugins.items()):
- run_plugin(plugin, plugin_name, args=[core],
- errorhandler=errorhandler, files=files)
- finally:
- core.shutdown()
-
-
-def run_plugin(plugin, plugin_name, errorhandler=None, args=None, files=None):
- """ run a single plugin, server-ful or serverless. """
- LOGGER.debug(" Running %s" % plugin_name)
- if args is None:
- args = []
-
- if errorhandler is None:
- errorhandler = get_errorhandler()
-
- setup = Bcfg2.Options.get_option_parser()
- if setup.cfp.has_section(plugin_name):
- arg = setup
- for key, val in setup.cfp.items(plugin_name):
- arg[key] = val
- args.append(arg)
- else:
- args.append(setup)
-
- # python 2.5 doesn't support mixing *magic and keyword arguments
- start = time.time()
- rv = plugin(*args, **dict(files=files, errorhandler=errorhandler)).Run()
- LOGGER.debug(" Ran %s in %0.2f seconds" % (plugin_name,
- time.time() - start))
- return rv
-
-
-def get_errorhandler():
- """ get a Bcfg2.Server.Lint.ErrorHandler object """
- setup = Bcfg2.Options.get_option_parser()
- if setup.cfp.has_section("errors"):
- errors = dict(setup.cfp.items("errors"))
- else:
- errors = None
- return Bcfg2.Server.Lint.ErrorHandler(errors=errors)
-
-
-def load_server():
- """ load server """
- core = Bcfg2.Server.Core.BaseCore()
- core.load_plugins()
- core.block_for_fam_events(handle_events=True)
- return core
-
-
-def load_plugin(module, obj_name=None):
- """ load a single plugin """
- parts = module.split(".")
- if obj_name is None:
- obj_name = parts[-1]
-
- mod = __import__(module)
- for part in parts[1:]:
- mod = getattr(mod, part)
- return getattr(mod, obj_name)
-
-
-def load_plugins():
- """ get list of plugins to run """
- setup = Bcfg2.Options.get_option_parser()
- if setup['args']:
- plugin_list = setup['args']
- elif "bcfg2-repo-validate" in sys.argv[0]:
- plugin_list = 'RequiredAttrs,Validate'.split(',')
- elif setup['lint_plugins']:
- plugin_list = setup['lint_plugins']
- else:
- plugin_list = Bcfg2.Server.Lint.plugins
-
- allplugins = dict()
- for plugin in plugin_list:
- try:
- allplugins[plugin] = load_plugin("Bcfg2.Server.Lint." + plugin)
- except ImportError:
- try:
- allplugins[plugin] = \
- load_plugin("Bcfg2.Server.Plugins." + plugin,
- obj_name=plugin + "Lint")
- except (ImportError, AttributeError):
- err = sys.exc_info()[1]
- LOGGER.error("Failed to load plugin %s: %s" %
- (plugin + "Lint", err))
- except AttributeError:
- err = sys.exc_info()[1]
- LOGGER.error("Failed to load plugin %s: %s" % (plugin, err))
-
- for plugin in setup['plugins']:
- if plugin in allplugins:
- # already loaded
- continue
-
- try:
- allplugins[plugin] = \
- load_plugin("Bcfg2.Server.Plugins." + plugin,
- obj_name=plugin + "Lint")
- except AttributeError:
- pass
- except ImportError:
- err = sys.exc_info()[1]
- LOGGER.error("Failed to load plugin %s: %s" % (plugin + "Lint",
- err))
-
- serverplugins = dict()
- serverlessplugins = dict()
- for plugin_name, plugin in allplugins.items():
- if [c for c in inspect.getmro(plugin)
- if c == Bcfg2.Server.Lint.ServerPlugin]:
- serverplugins[plugin_name] = plugin
- else:
- serverlessplugins[plugin_name] = plugin
- return (serverlessplugins, serverplugins)
-
-
-def main():
- optinfo = dict(lint_config=Bcfg2.Options.LINT_CONFIG,
- showerrors=Bcfg2.Options.LINT_SHOW_ERRORS,
- stdin=Bcfg2.Options.LINT_FILES_ON_STDIN,
- schema=Bcfg2.Options.SCHEMA_PATH,
- lint_plugins=Bcfg2.Options.LINT_PLUGINS)
- optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
- optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
- setup = Bcfg2.Options.load_option_parser(optinfo)
- setup.parse(sys.argv[1:])
-
- log_args = dict(to_syslog=setup['syslog'], to_console=logging.WARNING)
- if setup['verbose']:
- log_args['to_console'] = logging.DEBUG
- Bcfg2.Logger.setup_logging('bcfg2-info', **log_args)
-
- setup.cfp.read(setup['lint_config'])
- setup.reparse()
-
- if setup['stdin']:
- files = [s.strip() for s in sys.stdin.readlines()]
- else:
- files = None
-
- (serverlessplugins, serverplugins) = load_plugins()
- errorhandler = get_errorhandler()
-
- if setup['showerrors']:
- for plugin in serverplugins.values() + serverlessplugins.values():
- errorhandler.RegisterErrors(getattr(plugin, 'Errors')())
-
- print("%-35s %-35s" % ("Error name", "Handler"))
- for err, handler in errorhandler.errortypes.items():
- print("%-35s %-35s" % (err, handler.__name__))
- raise SystemExit(0)
-
- run_serverless_plugins(serverlessplugins, errorhandler=errorhandler,
- files=files)
-
- if serverplugins:
- if errorhandler.errors:
- # it would be swell if we could try to start the server
- # even if there were errors with the serverless plugins,
- # but since XML parsing errors occur in the FAM thread
- # (not in the core server thread), there's no way we can
- # start the server and try to catch exceptions --
- # bcfg2-lint isn't in the same stack as the exceptions.
- # so we're forced to assume that a serverless plugin error
- # will prevent the server from starting
- print("Serverless plugins encountered errors, skipping server "
- "plugins")
- else:
- run_server_plugins(serverplugins, errorhandler=errorhandler,
- files=files)
-
- if errorhandler.errors or errorhandler.warnings or setup['verbose']:
- print("%d errors" % errorhandler.errors)
- print("%d warnings" % errorhandler.warnings)
-
- if errorhandler.errors:
- raise SystemExit(2)
- elif errorhandler.warnings:
- raise SystemExit(3)
+from Bcfg2.Server.Lint import CLI
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(CLI().run())
diff --git a/src/sbin/bcfg2-report-collector b/src/sbin/bcfg2-report-collector
index ae6d3b167..00e015100 100755
--- a/src/sbin/bcfg2-report-collector
+++ b/src/sbin/bcfg2-report-collector
@@ -11,20 +11,14 @@ from Bcfg2.Reporting.Collector import ReportingCollector, ReportingError
def main():
+ parser = Bcfg2.Options.get_parser(description="Collect Bcfg2 report data",
+ components=[ReportingCollector])
+ parser.parse()
logger = logging.getLogger('bcfg2-report-collector')
- optinfo = dict(daemon=Bcfg2.Options.DAEMON,
- repo=Bcfg2.Options.SERVER_REPOSITORY,
- filemonitor=Bcfg2.Options.SERVER_FILEMONITOR,
- web_configfile=Bcfg2.Options.WEB_CFILE)
- optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
- optinfo.update(Bcfg2.Options.REPORTING_COMMON_OPTIONS)
- setup = Bcfg2.Options.load_option_parser(optinfo)
- setup.parse()
# run collector
try:
- collector = ReportingCollector(setup)
- collector.run()
+ ReportingCollector().run()
except ReportingError:
msg = sys.exc_info()[1]
logger.error(msg)
diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server
index beb19cef6..d6ce7d44f 100755
--- a/src/sbin/bcfg2-server
+++ b/src/sbin/bcfg2-server
@@ -2,63 +2,47 @@
"""The XML-RPC Bcfg2 server."""
-import os
import sys
import logging
-import Bcfg2.Logger
import Bcfg2.Options
from Bcfg2.Server.Core import CoreInitError
-LOGGER = logging.getLogger('bcfg2-server')
-
-def main():
- optinfo = dict()
- optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
- optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
- optinfo.update(Bcfg2.Options.DAEMON_COMMON_OPTIONS)
- setup = Bcfg2.Options.load_option_parser(optinfo)
- setup.parse(sys.argv[1:])
- # check whether the specified bcfg2.conf exists
- if not os.path.exists(setup['configfile']):
- print("Could not read %s" % setup['configfile'])
- sys.exit(1)
-
- # TODO: normalize case of various core modules so we can add a new
- # core without modifying this script
- backends = dict(cherrypy='CherryPyCore',
- builtin='BuiltinCore',
- best='BuiltinCore',
- multiprocessing='MultiprocessingCore')
-
- if setup['backend'] not in backends:
- print("Unknown server backend %s, using 'best'" % setup['backend'])
- setup['backend'] = 'best'
-
- coremodule = backends[setup['backend']]
- try:
- corecls = getattr(__import__("Bcfg2.Server.%s" % coremodule).Server,
- coremodule).Core
- except ImportError:
- err = sys.exc_info()[1]
- print("Unable to import %s server core: %s" % (setup['backend'], err))
- raise
- except AttributeError:
- err = sys.exc_info()[1]
- print("Unable to load %s server core: %s" % (setup['backend'], err))
- raise
-
- try:
- core = corecls(setup)
- core.run()
- except CoreInitError:
- msg = sys.exc_info()[1]
- LOGGER.error(msg)
- sys.exit(1)
- except KeyboardInterrupt:
- sys.exit(1)
- sys.exit(0)
+class BackendAction(Bcfg2.Options.ComponentAction):
+ """ Action to load Bcfg2 backends """
+ islist = False
+ bases = ['Bcfg2.Server']
+
+
+class CLI(object):
+ """ bcfg2-server CLI class """
+ options = [
+ Bcfg2.Options.Option(
+ cf=('server', 'backend'), help='Server Backend',
+ default='Builtin', type=lambda b: b.title() + "Core",
+ action=BackendAction)]
+
+ def __init__(self):
+ parser = Bcfg2.Options.get_parser("Bcfg2 server", components=[self])
+ parser.parse()
+ self.logger = logging.getLogger(parser.prog)
+
+ def run(self):
+ """ Run the bcfg2 server """
+ try:
+ core = Bcfg2.Options.setup.backend()
+ core.run()
+ except CoreInitError:
+ self.logger.error(sys.exc_info()[1])
+ return 1
+ except TypeError:
+ self.logger.error("Failed to load %s server backend: %s" %
+ (Bcfg2.Options.setup.backend.__name__,
+ sys.exc_info()[1]))
+ raise
+ except KeyboardInterrupt:
+ return 1
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(CLI().run())
diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test
index 564ddec49..73d9f13a7 100755
--- a/src/sbin/bcfg2-test
+++ b/src/sbin/bcfg2-test
@@ -1,319 +1,9 @@
#!/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 os
import sys
-import signal
-import fnmatch
-import logging
-import Bcfg2.Logger
-import Bcfg2.Server.Core
-from math import ceil
-from nose.core import TestProgram
-from nose.suite import LazySuite
-from unittest import TestCase
-
-try:
- from multiprocessing import Process, Queue, active_children
- HAS_MULTIPROC = True
-except ImportError:
- HAS_MULTIPROC = False
- active_children = lambda: [] # pylint: disable=C0103
-
-
-class CapturingLogger(object):
- """ Fake logger that captures logging output so that errors are
- only displayed for clients that fail tests """
- def __init__(self, *args, **kwargs): # pylint: disable=W0613
- self.output = []
-
- def error(self, msg):
- """ discard error messages """
- self.output.append(msg)
-
- def warning(self, msg):
- """ discard error messages """
- self.output.append(msg)
-
- def info(self, msg):
- """ discard error messages """
- self.output.append(msg)
-
- def debug(self, msg):
- """ discard error messages """
- self.output.append(msg)
-
- def reset_output(self):
- """ Reset the captured output """
- self.output = []
-
-
-class ClientTestFromQueue(TestCase):
- """ A test case that tests a value that has been enqueued by a
- child test process. ``client`` is the name of the client that has
- been tested; ``result`` is the result from the :class:`ClientTest`
- test. ``None`` indicates a successful test; a string value
- indicates a failed test; and an exception indicates an error while
- running the test. """
- __test__ = False # Do not collect
-
- def __init__(self, client, result):
- TestCase.__init__(self)
- self.client = client
- self.result = result
-
- def shortDescription(self):
- return "Building configuration for %s" % self.client
-
- def runTest(self):
- """ parse the result from this test """
- if isinstance(self.result, Exception):
- raise self.result
- assert self.result is None, self.result
-
-
-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) """
- __test__ = False # Do not collect
- divider = "-" * 70
-
- def __init__(self, core, client, ignore=None):
- TestCase.__init__(self)
- self.core = core
- self.core.logger = CapturingLogger()
- self.client = client
- if ignore is None:
- self.ignore = dict()
- else:
- self.ignore = ignore
-
- def ignore_entry(self, tag, name):
- """ return True if an error on a given entry should be ignored
- """
- 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 shortDescription(self):
- return "Building configuration for %s" % self.client
-
- def runTest(self):
- """ run this individual test """
- config = self.core.BuildConfiguration(self.client)
- output = self.core.logger.output[:]
- if output:
- output.append(self.divider)
- self.core.logger.reset_output()
-
- # check for empty client configuration
- assert len(config.findall("Bundle")) > 0, \
- "\n".join(output + ["%s has no content" % self.client])
-
- # check for missing bundles
- metadata = self.core.build_metadata(self.client)
- sbundles = [el.get('name') for el in config.findall("Bundle")]
- missing = [b for b in metadata.bundles if b not in sbundles]
- assert len(missing) == 0, \
- "\n".join(output + ["Configuration is missing bundle(s): %s" %
- ':'.join(missing)])
-
- # check for unknown packages
- unknown_pkgs = [el.get("name")
- for el in config.xpath('//Package[@type="unknown"]')
- if not self.ignore_entry(el.tag, el.get("name"))]
- assert len(unknown_pkgs) == 0, \
- "Configuration contains unknown packages: %s" % \
- ", ".join(unknown_pkgs)
-
- # check for render failures
- failures = []
- msg = output + ["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)
-
- def __str__(self):
- return "ClientTest(%s)" % self.client
-
- id = __str__
-
-
-def get_core(setup):
- """ Get a server core, with events handled """
- core = Bcfg2.Server.Core.BaseCore(setup)
- core.load_plugins()
- core.block_for_fam_events(handle_events=True)
- return core
-
-
-def get_ignore(setup):
- """ Given an options dict, get a dict of entry tags and names to
- ignore errors from """
- ignore = dict()
- for entry in setup['test_ignore']:
- tag, name = entry.split(":")
- try:
- ignore[tag].append(name)
- except KeyError:
- ignore[tag] = [name]
- return ignore
-
-
-def run_child(setup, clients, queue):
- """ Run tests for the given clients in a child process, returning
- results via the given Queue """
- core = get_core(setup)
- ignore = get_ignore(setup)
- for client in clients:
- try:
- ClientTest(core, client, ignore).runTest()
- queue.put((client, None))
- except AssertionError:
- queue.put((client, str(sys.exc_info()[1])))
- except:
- queue.put((client, sys.exc_info()[1]))
-
- core.shutdown()
-
-
-def get_sigint_handler(core):
- """ Get a function that handles SIGINT/Ctrl-C by shutting down the
- core and exiting properly."""
-
- def hdlr(sig, frame): # pylint: disable=W0613
- """ Handle SIGINT/Ctrl-C by shutting down the core and exiting
- properly. """
- core.shutdown()
- os._exit(1) # pylint: disable=W0212
-
- return hdlr
-
-
-def parse_args():
- """ Parse command line arguments. """
- optinfo = dict(Bcfg2.Options.TEST_COMMON_OPTIONS)
-
- optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
- optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
- setup = Bcfg2.Options.load_option_parser(optinfo)
- setup.hm = \
- "bcfg2-test [options] [client] [client] [...]\nOptions:\n %s" % \
- setup.buildHelpMessage()
- setup.parse(sys.argv[1:])
-
- if setup['debug']:
- level = logging.DEBUG
- elif setup['verbose']:
- level = logging.INFO
- else:
- level = logging.WARNING
- Bcfg2.Logger.setup_logging("bcfg2-test",
- to_console=setup['verbose'] or setup['debug'],
- to_syslog=False,
- to_file=setup['logging'],
- level=level)
- logger = logging.getLogger(sys.argv[0])
- if (setup['debug'] or setup['verbose']) and "-v" not in setup['noseopts']:
- setup['noseopts'].append("-v")
-
- if setup['children'] and not HAS_MULTIPROC:
- logger.warning("Python multiprocessing library not found, running "
- "with no children")
- setup['children'] = 0
-
- if (setup['children'] and ('--with-xunit' in setup['noseopts'] or
- '--xunit-file' in setup['noseopts'])):
- logger.warning("Use the --xunit option to bcfg2-test instead of the "
- "--with-xunit or --xunit-file options to nosetest")
- xunitfile = None
- if '--with-xunit' in setup['noseopts']:
- setup['noseopts'].remove('--with-xunit')
- xunitfile = "nosetests.xml"
- if '--xunit-file' in setup['noseopts']:
- idx = setup['noseopts'].index('--xunit-file')
- try:
- setup['noseopts'].pop(idx) # remove --xunit-file
- # remove the argument to it
- xunitfile = setup['noseopts'].pop(idx)
- except IndexError:
- pass
- if xunitfile and not setup['xunit']:
- setup['xunit'] = xunitfile
- return setup
-
-
-def main():
- setup = parse_args()
- logger = logging.getLogger(sys.argv[0])
- core = get_core(setup)
- signal.signal(signal.SIGINT, get_sigint_handler(core))
-
- if setup['args']:
- clients = setup['args']
- else:
- clients = core.metadata.clients
-
- ignore = get_ignore(setup)
-
- if setup['children']:
- if setup['children'] > len(clients):
- logger.info("Refusing to spawn more children than clients to test,"
- " setting children=%s" % len(clients))
- setup['children'] = len(clients)
- perchild = int(ceil(len(clients) / float(setup['children'] + 1)))
- queue = Queue()
- for child in range(setup['children']):
- start = child * perchild
- end = (child + 1) * perchild
- child = Process(target=run_child,
- args=(setup, clients[start:end], queue))
- child.start()
-
- def generate_tests():
- """ Read test results for the clients """
- start = setup['children'] * perchild
- for client in clients[start:]:
- yield ClientTest(core, client, ignore)
-
- for i in range(start): # pylint: disable=W0612
- yield ClientTestFromQueue(*queue.get())
- else:
- def generate_tests():
- """ Run tests for the clients """
- for client in clients:
- yield ClientTest(core, client, ignore)
-
- result = TestProgram(argv=sys.argv[:1] + core.setup['noseopts'],
- suite=LazySuite(generate_tests), exit=False)
-
- # block until all children have completed -- should be
- # immediate since we've already gotten all the results we
- # expect
- for child in active_children():
- child.join()
-
- core.shutdown()
- if result.success:
- os._exit(0) # pylint: disable=W0212
- else:
- os._exit(1) # pylint: disable=W0212
-
+from Bcfg2.Server.Test import CLI
if __name__ == "__main__":
- sys.exit(main())
+ sys.exit(CLI().run())
diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper
index 49baeb9c3..95fb9889e 100755
--- a/src/sbin/bcfg2-yum-helper
+++ b/src/sbin/bcfg2-yum-helper
@@ -5,358 +5,8 @@ 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. """
-import os
import sys
-import yum
-import logging
-import Bcfg2.Logger
-from Bcfg2.Compat import wraps
-from lockfile import FileLock, LockTimeout
-from optparse import OptionParser
-try:
- import json
-except ImportError:
- import simplejson as json
-
-
-def pkg_to_tuple(package):
- """ json doesn't distinguish between tuples and lists, but yum
- does, so we convert a package in list format to one in tuple
- format """
- if isinstance(package, list):
- return tuple(package)
- else:
- return package
-
-
-def pkgtup_to_string(package):
- """ given a package tuple, return a human-readable string
- describing the package """
- if package[3] in ['auto', 'any']:
- return package[0]
-
- rv = [package[0], "-"]
- if package[2]:
- rv.extend([package[2], ':'])
- rv.extend([package[3], '-', package[4]])
- if package[1]:
- rv.extend(['.', package[1]])
- return ''.join(str(e) for e in rv)
-
-
-class YumHelper(object):
- """ Yum helper base object """
-
- def __init__(self, cfgfile, verbose=1):
- self.cfgfile = cfgfile
- self.yumbase = yum.YumBase()
- # pylint: disable=E1121,W0212
- try:
- self.yumbase.preconf.debuglevel = verbose
- self.yumbase.preconf.fn = cfgfile
- self.yumbase._getConfig()
- except AttributeError:
- self.yumbase._getConfig(cfgfile, debuglevel=verbose)
- # pylint: enable=E1121,W0212
- self.logger = logging.getLogger(self.__class__.__name__)
-
-
-class DepSolver(YumHelper):
- """ Yum dependency solver. This is used for operations that only
- read from the yum cache, and thus operates in cacheonly mode. """
-
- def __init__(self, cfgfile, verbose=1):
- YumHelper.__init__(self, cfgfile, verbose=verbose)
- # internally, yum uses an integer, not a boolean, for conf.cache
- self.yumbase.conf.cache = 1
- self._groups = None
-
- def get_groups(self):
- """ getter for the groups property """
- if self._groups is not None:
- return self._groups
- else:
- return ["noarch"]
-
- def set_groups(self, groups):
- """ setter for the groups property """
- self._groups = set(groups).union(["noarch"])
-
- groups = property(get_groups, set_groups)
-
- def get_package_object(self, pkgtup, silent=False):
- """ given a package tuple, get a yum package object """
- try:
- matches = yum.packageSack.packagesNewestByName(
- self.yumbase.pkgSack.searchPkgTuple(pkgtup))
- except yum.Errors.PackageSackError:
- if not silent:
- self.logger.warning("Package '%s' not found" %
- self.get_package_name(pkgtup))
- matches = []
- except yum.Errors.RepoError:
- err = sys.exc_info()[1]
- self.logger.error("Temporary failure loading metadata for %s: %s" %
- (self.get_package_name(pkgtup), err))
- matches = []
-
- pkgs = self._filter_arch(matches)
- if pkgs:
- return pkgs[0]
- else:
- return None
-
- def get_group(self, group, ptype="default"):
- """ Resolve a package group name into a list of packages """
- if group.startswith("@"):
- group = group[1:]
-
- try:
- if self.yumbase.comps.has_group(group):
- group = self.yumbase.comps.return_group(group)
- else:
- self.logger.error("%s is not a valid group" % group)
- return []
- except yum.Errors.GroupsError:
- err = sys.exc_info()[1]
- self.logger.warning(err)
- return []
-
- if ptype == "default":
- return [p
- for p, d in list(group.default_packages.items())
- if d]
- elif ptype == "mandatory":
- return [p
- for p, m in list(group.mandatory_packages.items())
- if m]
- elif ptype == "optional" or ptype == "all":
- return group.packages
- else:
- self.logger.warning("Unknown group package type '%s'" % ptype)
- return []
-
- def _filter_arch(self, packages):
- """ filter packages in the given list that do not have an
- architecture in the list of groups for this client """
- 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:
- # no packages match architecture; we'll assume that the
- # user knows what s/he is doing and this is a multiarch
- # box.
- return packages
-
- def get_package_name(self, package):
- """ get the name of a package or virtual package from the
- internal representation used by this Collection class """
- if isinstance(package, tuple):
- if len(package) == 3:
- return yum.misc.prco_tuple_to_string(package)
- else:
- return pkgtup_to_string(package)
- else:
- return str(package)
-
- def complete(self, packagelist):
- """ resolve dependencies and generate a complete package list
- from the given list of initial packages """
- packages = set()
- unknown = set()
- for pkg in packagelist:
- if isinstance(pkg, tuple):
- pkgtup = pkg
- else:
- pkgtup = (pkg, None, None, None, None)
- pkgobj = self.get_package_object(pkgtup)
- if not pkgobj:
- self.logger.debug("Unknown package %s" %
- self.get_package_name(pkg))
- unknown.add(pkg)
- else:
- if self.yumbase.tsInfo.exists(pkgtup=pkgobj.pkgtup):
- self.logger.debug("%s added to transaction multiple times"
- % pkgobj)
- else:
- self.logger.debug("Adding %s to transaction" % pkgobj)
- self.yumbase.tsInfo.addInstall(pkgobj)
- self.yumbase.resolveDeps()
-
- for txmbr in self.yumbase.tsInfo:
- packages.add(txmbr.pkgtup)
- return list(packages), list(unknown)
-
-
-def acquire_lock(func):
- """ decorator for CacheManager methods that gets and release a
- lock while the method runs """
- @wraps(func)
- def inner(self, *args, **kwargs):
- """ Get and release a lock while running the function this
- wraps. """
- self.logger.debug("Acquiring lock at %s" % self.lockfile)
- while not self.lock.i_am_locking():
- try:
- self.lock.acquire(timeout=60) # wait up to 60 seconds
- except LockTimeout:
- self.lock.break_lock()
- self.lock.acquire()
- try:
- func(self, *args, **kwargs)
- finally:
- self.lock.release()
- self.logger.debug("Released lock at %s" % self.lockfile)
-
- return inner
-
-
-class CacheManager(YumHelper):
- """ Yum cache manager. Unlike :class:`DepSolver`, this can write
- to the yum cache, and so is used for operations that muck with the
- cache. (Technically, :func:`CacheManager.clean_cache` could be in
- either DepSolver or CacheManager, but for consistency I've put it
- here.) """
-
- def __init__(self, cfgfile, verbose=1):
- YumHelper.__init__(self, cfgfile, verbose=verbose)
- self.lockfile = \
- os.path.join(os.path.dirname(self.yumbase.conf.config_file_path),
- "lock")
- self.lock = FileLock(self.lockfile)
-
- @acquire_lock
- def clean_cache(self):
- """ clean the yum cache """
- for mdtype in ["Headers", "Packages", "Sqlite", "Metadata",
- "ExpireCache"]:
- # for reasons that are entirely obvious, all of the yum
- # API clean* methods return a tuple of 0 (zero, always
- # zero) and a list containing a single message about how
- # many files were deleted. so useful. thanks, yum.
- msg = getattr(self.yumbase, "clean%s" % mdtype)()[1][0]
- if not msg.startswith("0 "):
- self.logger.info(msg)
-
- @acquire_lock
- def populate_cache(self):
- """ populate the yum cache """
- for repo in self.yumbase.repos.findRepos('*'):
- repo.metadata_expire = 0
- repo.mdpolicy = "group:all"
- self.yumbase.doRepoSetup()
- self.yumbase.repos.doSetup()
- for repo in self.yumbase.repos.listEnabled():
- # this populates the cache as a side effect
- repo.repoXML # pylint: disable=W0104
- try:
- repo.getGroups()
- except yum.Errors.RepoMDError:
- pass # this repo has no groups
- self.yumbase.repos.populateSack(mdtype='metadata', cacheonly=1)
- self.yumbase.repos.populateSack(mdtype='filelists', cacheonly=1)
- self.yumbase.repos.populateSack(mdtype='otherdata', cacheonly=1)
- # this does something with the groups cache as a side effect
- self.yumbase.comps # pylint: disable=W0104
-
-
-def main():
- parser = OptionParser()
- parser.add_option("-c", "--config", help="Config file")
- parser.add_option("-v", "--verbose", help="Verbosity level",
- action="count")
- (options, args) = parser.parse_args()
-
- if options.verbose:
- level = logging.DEBUG
- clevel = logging.DEBUG
- else:
- level = logging.WARNING
- clevel = logging.INFO
- Bcfg2.Logger.setup_logging('bcfg2-yum-helper', to_syslog=True,
- to_console=clevel, level=level)
- logger = logging.getLogger('bcfg2-yum-helper')
-
- try:
- cmd = args[0]
- except IndexError:
- logger.error("No command given")
- return 1
-
- if not os.path.exists(options.config):
- logger.error("Config file %s not found" % options.config)
- return 1
-
- # pylint: disable=W0702
- rv = 0
- if cmd == "clean":
- cachemgr = CacheManager(options.config, options.verbose)
- try:
- cachemgr.clean_cache()
- print(json.dumps(True))
- except:
- logger.error("Unexpected error cleaning cache: %s" %
- sys.exc_info()[1], exc_info=1)
- print(json.dumps(False))
- rv = 2
- elif cmd == "makecache":
- cachemgr = CacheManager(options.config, options.verbose)
- try:
- # this code copied from yumcommands.py
- cachemgr.populate_cache()
- print(json.dumps(True))
- except yum.Errors.YumBaseError:
- logger.error("Unexpected error creating cache: %s" %
- sys.exc_info()[1], exc_info=1)
- print(json.dumps(False))
- elif cmd == "complete":
- depsolver = DepSolver(options.config, options.verbose)
- try:
- data = json.loads(sys.stdin.read())
- except:
- logger.error("Unexpected error decoding JSON input: %s" %
- sys.exc_info()[1])
- rv = 2
- try:
- depsolver.groups = data['groups']
- (packages, unknown) = depsolver.complete(
- [pkg_to_tuple(p) for p in data['packages']])
- print(json.dumps(dict(packages=list(packages),
- unknown=list(unknown))))
- except:
- logger.error("Unexpected error completing package set: %s" %
- sys.exc_info()[1], exc_info=1)
- print(json.dumps(dict(packages=[], unknown=data['packages'])))
- rv = 2
- elif cmd == "get_groups":
- depsolver = DepSolver(options.config, options.verbose)
- try:
- data = json.loads(sys.stdin.read())
- rv = dict()
- for gdata in data:
- if "type" in gdata:
- packages = depsolver.get_group(gdata['group'],
- ptype=gdata['type'])
- else:
- packages = depsolver.get_group(gdata['group'])
- rv[gdata['group']] = list(packages)
- print(json.dumps(rv))
- except:
- logger.error("Unexpected error getting groups: %s" %
- sys.exc_info()[1], exc_info=1)
- print(json.dumps(dict()))
- rv = 2
- else:
- logger.error("Unknown command %s" % cmd)
- print(json.dumps(None))
- rv = 2
- return rv
+from Bcfg2.Server.Plugins.Packages.YumHelper import CLI
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(CLI().run())