summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/server/plugins/generators/packages.txt3
-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.py75
-rw-r--r--src/lib/Proxy.py14
-rw-r--r--src/lib/Server/Lint/GroupPatterns.py31
-rw-r--r--src/lib/Server/Lint/__init__.py5
-rw-r--r--src/lib/Server/Plugin.py11
-rw-r--r--src/lib/Server/Plugins/FileProbes.py2
-rw-r--r--src/lib/Server/Plugins/GroupPatterns.py25
-rw-r--r--src/lib/Server/Plugins/Packages/__init__.py3
-rw-r--r--src/lib/Server/Plugins/Properties.py2
-rw-r--r--src/lib/Server/Plugins/Rules.py8
-rw-r--r--src/lib/Server/Snapshots/model.py2
-rwxr-xr-xsrc/sbin/bcfg2-admin6
-rwxr-xr-xsrc/sbin/bcfg2-info13
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'])