diff options
-rw-r--r-- | doc/server/plugins/generators/packages.txt | 3 | ||||
-rw-r--r-- | gentoo/bcfg2-1.2.0.ebuild (renamed from gentoo/bcfg2-1.1.2.ebuild) | 15 | ||||
-rw-r--r-- | src/lib/Client/Tools/YUMng.py | 75 | ||||
-rw-r--r-- | src/lib/Proxy.py | 14 | ||||
-rw-r--r-- | src/lib/Server/Lint/GroupPatterns.py | 31 | ||||
-rw-r--r-- | src/lib/Server/Lint/__init__.py | 5 | ||||
-rw-r--r-- | src/lib/Server/Plugin.py | 11 | ||||
-rw-r--r-- | src/lib/Server/Plugins/FileProbes.py | 2 | ||||
-rw-r--r-- | src/lib/Server/Plugins/GroupPatterns.py | 25 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Packages/__init__.py | 3 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Properties.py | 2 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Rules.py | 8 | ||||
-rw-r--r-- | src/lib/Server/Snapshots/model.py | 2 | ||||
-rwxr-xr-x | src/sbin/bcfg2-admin | 6 | ||||
-rwxr-xr-x | src/sbin/bcfg2-info | 13 |
15 files changed, 149 insertions, 66 deletions
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt index 0f4a9bb96..b566b6fbb 100644 --- a/doc/server/plugins/generators/packages.txt +++ b/doc/server/plugins/generators/packages.txt @@ -216,6 +216,9 @@ something like this:: <Source type="apt" recommended="true" ...> + .. warning:: You must regenerate the Packages cache when adding or + removing the recommended attribute. + Yum sources can be similarly specified:: <Sources> diff --git a/gentoo/bcfg2-1.1.2.ebuild b/gentoo/bcfg2-1.2.0.ebuild index 4da67d865..cd1d368d1 100644 --- a/gentoo/bcfg2-1.1.2.ebuild +++ b/gentoo/bcfg2-1.2.0.ebuild @@ -10,7 +10,7 @@ RESTRICT_PYTHON_ABIS="2.4 2.5 3.*" inherit distutils -DESCRIPTION="Bcfg2 is a configuration management tool." +DESCRIPTION="configuration management tool" HOMEPAGE="http://bcfg2.org" # handle the "pre" case @@ -23,11 +23,11 @@ SLOT="0" KEYWORDS="~amd64 ~x86 ~amd64-linux ~x86-linux ~x64-solaris" IUSE="server" -DEPEND="app-portage/gentoolkit +DEPEND="" +RDEPEND="app-portage/gentoolkit server? ( dev-python/lxml - app-admin/gam-server )" -RDEPEND="${DEPEND}" + dev-libs/libgamin[python] )" PYTHON_MODNAME="Bcfg2" @@ -40,14 +40,11 @@ distutils_src_install_post_hook() { src_install() { distutils_src_install --record=PY_SERVER_LIBS --install-scripts "${EPREFIX}/usr/sbin" - # Remove files only necessary for a server installation if ! use server; then + # Remove files only necessary for a server installation rm -rf "${ED}usr/share/bcfg2" rm -rf "${ED}usr/share/man/man8" - fi - - # Install a server init.d script - if use server; then + else newinitd "${FILESDIR}/bcfg2-server.rc" bcfg2-server fi diff --git a/src/lib/Client/Tools/YUMng.py b/src/lib/Client/Tools/YUMng.py index a018e68fb..5206ecc3c 100644 --- a/src/lib/Client/Tools/YUMng.py +++ b/src/lib/Client/Tools/YUMng.py @@ -146,21 +146,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool): conflicts = ['YUM24', 'RPMng'] def __init__(self, logger, setup, config): - self.yb = yum.YumBase() - - if setup['debug']: - debuglevel = 3 - elif setup['verbose']: - debuglevel = 2 - else: - debuglevel = 1 - - try: - self.yb.preconf.debuglevel = debuglevel - except AttributeError: - self.yb._getConfig(self.yb.config_file_path, - debuglevel=debuglevel) - + self._loadYumBase(setup=setup, logger=logger) Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) self.ignores = [entry.get('name') for struct in config \ for entry in struct \ @@ -179,18 +165,6 @@ class YUMng(Bcfg2.Client.Tools.PkgTool): or entry.get('name') == '/etc/yum.conf'] self.yum_avail = dict() self.yum_installed = dict() - try: - self.yb.doConfigSetup() - self.yb.doTsSetup() - self.yb.doRpmDBSetup() - except yum.Errors.RepoError: - e = sys.exc_info()[1] - self.logger.error("YUMng Repository error: %s" % e) - raise Bcfg2.Client.Tools.toolInstantiationError - except Exception: - e = sys.exc_info()[1] - self.logger.error("YUMng error: %s" % e) - raise Bcfg2.Client.Tools.toolInstantiationError yup = self.yb.doPackageLists(pkgnarrow='updates') if hasattr(self.yb.rpmdb, 'pkglist'): @@ -211,6 +185,49 @@ class YUMng(Bcfg2.Client.Tools.PkgTool): else: dest[pname] = dict(data) + def _loadYumBase(self, setup=None, logger=None): + ''' this may be called before PkgTool.__init__() is called on + this object (when the YUMng object is first instantiated; + PkgTool.__init__() calls RefreshPackages(), which requires a + YumBase object already exist), or after __init__() has + completed, when we reload the yum config before installing + packages. Consequently, we support both methods by allowing + setup and logger, the only object properties we use in this + function, to be passed as keyword arguments or to be omitted + and drawn from the object itself.''' + self.yb = yum.YumBase() + + if setup is None: + setup = self.setup + if logger is None: + logger = self.logger + + if setup['debug']: + debuglevel = 3 + elif setup['verbose']: + debuglevel = 2 + else: + debuglevel = 1 + + try: + self.yb.preconf.debuglevel = debuglevel + except AttributeError: + self.yb._getConfig(self.yb.conf.config_file_path, + debuglevel=debuglevel) + + try: + self.yb.doConfigSetup() + self.yb.doTsSetup() + self.yb.doRpmDBSetup() + except yum.Errors.RepoError: + err = sys.exc_info()[1] + logger.error("YUMng Repository error: %s" % err) + raise Bcfg2.Client.Tools.toolInstantiationError + except Exception: + err = sys.exc_info()[1] + logger.error("YUMng error: %s" % err) + raise Bcfg2.Client.Tools.toolInstantiationError + def _loadConfig(self): # Process the YUMng section from the config file. CP = Parser() @@ -841,6 +858,10 @@ class YUMng(Bcfg2.Client.Tools.PkgTool): pkg = self.instance_status[gpg_keys[0]].get('pkg') states[pkg] = self.VerifyPackage(pkg, []) + # We want to reload all Yum configuration in case we've + # deployed new .repo files we should consider + self._loadYumBase() + # Install packages. if len(install_pkgs) > 0: self.logger.info("Attempting to install packages") diff --git a/src/lib/Proxy.py b/src/lib/Proxy.py index e1406bd99..9d0197798 100644 --- a/src/lib/Proxy.py +++ b/src/lib/Proxy.py @@ -49,18 +49,20 @@ class ProxyError(Exception): the various xmlrpclib errors that might arise (mainly ProtocolError and Fault) """ def __init__(self, err): + msg = None if isinstance(err, xmlrpclib.ProtocolError): # cut out the password in the URL url = re.sub(r'([^:]+):(.*?)@([^@]+:\d+/)', r'\1:******@\3', err.url) - self.message = "XML-RPC Protocol Error for %s: %s (%s)" % \ - (url, err.errmsg, err.errcode) + msg = "XML-RPC Protocol Error for %s: %s (%s)" % (url, + err.errmsg, + err.errcode) elif isinstance(err, xmlrpclib.Fault): - self.message = "XML-RPC Fault: %s (%s)" % (err.faultString, - err.faultCode) + msg = "XML-RPC Fault: %s (%s)" % (err.faultString, + err.faultCode) else: - self.message = str(err) - self.args = (self.message, ) + msg = str(err) + Exception(self, msg) class CertificateError(Exception): def __init__(self, commonName): diff --git a/src/lib/Server/Lint/GroupPatterns.py b/src/lib/Server/Lint/GroupPatterns.py new file mode 100644 index 000000000..b69d7a5d8 --- /dev/null +++ b/src/lib/Server/Lint/GroupPatterns.py @@ -0,0 +1,31 @@ +import sys +import Bcfg2.Server.Lint +from Bcfg2.Server.Plugins.GroupPatterns import PatternMap + +class GroupPatterns(Bcfg2.Server.Lint.ServerPlugin): + """ Check Genshi templates for syntax errors """ + + def Run(self): + """ run plugin """ + if 'GroupPatterns' in self.core.plugins: + cfg = self.core.plugins['GroupPatterns'].config + for entry in cfg.xdata.xpath('//GroupPattern'): + groups = [g.text for g in entry.findall('Group')] + self.check(entry, groups, ptype='NamePattern') + self.check(entry, groups, ptype='NameRange') + + def check(self, entry, groups, ptype="NamePattern"): + if ptype == "NamePattern": + pmap = lambda p: PatternMap(p, None, groups) + else: + pmap = lambda p: PatternMap(None, p, groups) + + for el in entry.findall(ptype): + pat = el.text + try: + pmap(pat) + except: + err = sys.exc_info()[1] + self.LintError("pattern-fails-to-initialize", + "Failed to initialize %s %s for %s: %s" % + (ptype, pat, entry.get('pattern'), err)) diff --git a/src/lib/Server/Lint/__init__.py b/src/lib/Server/Lint/__init__.py index f47059ac4..a9d57c46c 100644 --- a/src/lib/Server/Lint/__init__.py +++ b/src/lib/Server/Lint/__init__.py @@ -117,8 +117,9 @@ class ErrorHandler (object): "xml-failed-to-verify":"error", "merge-cfg":"warning", "merge-probes":"warning", - "input-output-error": "error", - "genshi-syntax-error": "error"} + "input-output-error":"error", + "genshi-syntax-error":"error", + "pattern-fails-to-initialize":"error"} def __init__(self, config=None): self.errors = 0 diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index 41ac9dc20..4bfa3fdf5 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -357,7 +357,7 @@ class FileBacked(object): object.__init__(self) self.data = '' self.name = name - + def HandleEvent(self, event=None): """Read file upon update.""" if event and event.code2str() not in ['exists', 'changed', 'created']: @@ -373,6 +373,12 @@ class FileBacked(object): """Update local data structures based on current file state""" pass + def __repr__(self): + return "%s: %s" % (self.__class__.__name__, str(self)) + + def __str__(self): + return "%s: %s" % (self.name, self.data) + class DirectoryBacked(object): """This object is a coherent cache for a filesystem hierarchy of files.""" @@ -568,6 +574,9 @@ class XMLFileBacked(FileBacked): def __iter__(self): return iter(self.entries) + def __str__(self): + return "%s: %s" % (self.name, lxml.etree.tostring(self.xdata)) + class SingleXMLFileBacked(XMLFileBacked): """This object is a coherent cache for an independent XML file.""" diff --git a/src/lib/Server/Plugins/FileProbes.py b/src/lib/Server/Plugins/FileProbes.py index 0c1a0d897..269664ef4 100644 --- a/src/lib/Server/Plugins/FileProbes.py +++ b/src/lib/Server/Plugins/FileProbes.py @@ -133,7 +133,7 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin, create = True # get current entry data - if entry.get("encoding") == "base64": + if entry.text and entry.get("encoding") == "base64": entrydata = binascii.a2b_base64(entry.text) else: entrydata = entry.text diff --git a/src/lib/Server/Plugins/GroupPatterns.py b/src/lib/Server/Plugins/GroupPatterns.py index 76a628931..58b4d4afb 100644 --- a/src/lib/Server/Plugins/GroupPatterns.py +++ b/src/lib/Server/Plugins/GroupPatterns.py @@ -1,9 +1,8 @@ -import lxml.etree import re - +import logging +import lxml.etree import Bcfg2.Server.Plugin - class PackedDigitRange(object): def __init__(self, digit_range): self.sparse = list() @@ -25,7 +24,7 @@ class PackedDigitRange(object): class PatternMap(object): - range_finder = '\\[\\[[\d\-,]+\\]\\]' + range_finder = r'\[\[[\d\-,]+\]\]' def __init__(self, pattern, rangestr, groups): self.pattern = pattern @@ -35,15 +34,18 @@ class PatternMap(object): self.re = re.compile(pattern) self.process = self.process_re elif rangestr != None: + if '\\' in rangestr: + raise Exception("Backslashes are not allowed in NameRanges") self.process = self.process_range - self.re = re.compile('^' + re.subn(self.range_finder, '(\d+)', - rangestr)[0]) - dmatcher = re.compile(re.subn(self.range_finder, - '\\[\\[([\d\-,]+)\\]\\]', - rangestr)[0]) - self.dranges = [PackedDigitRange(x) for x in dmatcher.match(rangestr).groups()] + self.re = re.compile('^' + re.sub(self.range_finder, '(\d+)', + rangestr)) + dmatcher = re.compile(re.sub(self.range_finder, + r'\[\[([\d\-,]+)\]\]', + rangestr)) + self.dranges = [PackedDigitRange(x) + for x in dmatcher.match(rangestr).groups()] else: - raise Exception + raise Exception("No pattern or range given") def process_range(self, name): match = self.re.match(name) @@ -75,6 +77,7 @@ class PatternFile(Bcfg2.Server.Plugin.SingleXMLFileBacked): def __init__(self, filename, fam): Bcfg2.Server.Plugin.SingleXMLFileBacked.__init__(self, filename, fam) self.patterns = [] + self.logger = logging.getLogger(self.__class__.__name__) def Index(self): Bcfg2.Server.Plugin.SingleXMLFileBacked.Index(self) diff --git a/src/lib/Server/Plugins/Packages/__init__.py b/src/lib/Server/Plugins/Packages/__init__.py index b8babfac2..b12d633f3 100644 --- a/src/lib/Server/Plugins/Packages/__init__.py +++ b/src/lib/Server/Plugins/Packages/__init__.py @@ -214,7 +214,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, keys = [] for source in self.sources: for key in source.gpgkeys: - localfile = os.path.join(self.keypath, os.path.basename(key)) + localfile = os.path.join(self.keypath, + os.path.basename(key.rstrip("/"))) if localfile not in keyfiles: keyfiles.append(localfile) if ((force_update and key not in keys) or diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py index 58f7215c9..9b44942cd 100644 --- a/src/lib/Server/Plugins/Properties.py +++ b/src/lib/Server/Plugins/Properties.py @@ -1,4 +1,5 @@ import os +import re import sys import copy import logging @@ -49,6 +50,7 @@ class PropertyFile(Bcfg2.Server.Plugin.StructFile): class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked): __child__ = PropertyFile + patterns = re.compile(r'.*\.xml$') class Properties(Bcfg2.Server.Plugin.Plugin, diff --git a/src/lib/Server/Plugins/Rules.py b/src/lib/Server/Plugins/Rules.py index fde0f3d59..c66276179 100644 --- a/src/lib/Server/Plugins/Rules.py +++ b/src/lib/Server/Plugins/Rules.py @@ -36,6 +36,14 @@ class Rules(Bcfg2.Server.Plugin.PrioDir): def _matches(self, entry, metadata, rules): if Bcfg2.Server.Plugin.PrioDir._matches(self, entry, metadata, rules): return True + elif (entry.tag == "Path" and + ((entry.get('name').endswith("/") and + entry.get('name').rstrip("/") in rules) or + (not entry.get('name').endswith("/") and + entry.get('name') + '/' in rules))): + # special case for Path tags: + # http://trac.mcs.anl.gov/projects/bcfg2/ticket/967 + return True elif self._regex_enabled: # attempt regular expression matching for rule in rules: diff --git a/src/lib/Server/Snapshots/model.py b/src/lib/Server/Snapshots/model.py index f30c38a05..5d7973c16 100644 --- a/src/lib/Server/Snapshots/model.py +++ b/src/lib/Server/Snapshots/model.py @@ -211,7 +211,7 @@ class File(Base, Uniquer): type = Column(Unicode(12)) owner = Column(Unicode(12)) group = Column(Unicode(16)) - perms = Column(Integer(5)) + perms = Column(Integer) contents = Column(UnicodeText) diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin index 09117a3f4..386c8dd47 100755 --- a/src/sbin/bcfg2-admin +++ b/src/sbin/bcfg2-admin @@ -44,6 +44,9 @@ def main(): 'verbose': Bcfg2.Options.VERBOSE, } setup = Bcfg2.Options.OptionParser(optinfo) + # override default help message to include description of all modes + setup.hm = "Usage:\n %s\n%s" % (setup.buildHelpMessage(), + create_description()) setup.parse(sys.argv[1:]) log_args = dict(to_syslog=False, to_console=logging.WARNING) @@ -58,8 +61,7 @@ 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(): diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info index 4412c712a..c73efa23e 100755 --- a/src/sbin/bcfg2-info +++ b/src/sbin/bcfg2-info @@ -103,11 +103,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: @@ -518,20 +518,23 @@ if __name__ == '__main__': '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']) |