summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-04-24 13:47:31 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-04-24 13:47:31 -0400
commit0ff6b2788de683dd89203c7ae1393ea922a62c32 (patch)
tree54ce843377ab26c6336de7f1abf3ec906d49aa69 /src/lib/Bcfg2/Server/Plugins
parent46a47b4120b3d892b8149a5e181e4d976ad87f99 (diff)
parent29399cbc599919fd9c88448bde692132c803e69b (diff)
downloadbcfg2-0ff6b2788de683dd89203c7ae1393ea922a62c32.tar.gz
bcfg2-0ff6b2788de683dd89203c7ae1393ea922a62c32.tar.bz2
bcfg2-0ff6b2788de683dd89203c7ae1393ea922a62c32.zip
Merge branch 'maint'
Conflicts: src/lib/Bcfg2/Client/Client.py src/lib/Bcfg2/Client/Frame.py src/lib/Bcfg2/Client/Tools/YUM.py src/lib/Bcfg2/Options.py src/lib/Bcfg2/Server/Admin/Perf.py src/lib/Bcfg2/Server/Admin/Xcmd.py src/lib/Bcfg2/Server/Admin/__init__.py src/lib/Bcfg2/Server/Core.py src/lib/Bcfg2/Server/FileMonitor/Fam.py src/lib/Bcfg2/Server/Lint/RequiredAttrs.py src/lib/Bcfg2/Server/Plugin/helpers.py src/lib/Bcfg2/Server/Plugins/Base.py src/lib/Bcfg2/Server/Plugins/Bundler.py src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py src/lib/Bcfg2/Server/Plugins/Cvs.py src/lib/Bcfg2/Server/Plugins/Darcs.py src/lib/Bcfg2/Server/Plugins/Decisions.py src/lib/Bcfg2/Server/Plugins/FileProbes.py src/lib/Bcfg2/Server/Plugins/Fossil.py src/lib/Bcfg2/Server/Plugins/Git.py src/lib/Bcfg2/Server/Plugins/Metadata.py src/lib/Bcfg2/Server/Plugins/NagiosGen.py src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py src/lib/Bcfg2/Server/Plugins/Packages/Source.py src/lib/Bcfg2/Server/Plugins/Packages/Yum.py src/lib/Bcfg2/Server/Plugins/Properties.py src/lib/Bcfg2/Server/Plugins/__init__.py src/lib/Bcfg2/Server/__init__.py src/sbin/bcfg2-build-reports src/sbin/bcfg2-crypt testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestProperties.py
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugins')
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py18
-rw-r--r--src/lib/Bcfg2/Server/Plugins/FileProbes.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Git.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/GroupLogic.py47
-rw-r--r--src/lib/Bcfg2/Server/Plugins/GroupPatterns.py11
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py145
-rw-r--r--src/lib/Bcfg2/Server/Plugins/NagiosGen.py21
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Ohai.py31
-rw-r--r--src/lib/Bcfg2/Server/Plugins/POSIXCompat.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Apt.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py16
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Source.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py14
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/__init__.py30
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Probes.py52
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Properties.py39
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Reporting.py7
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSHbase.py7
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSLCA.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/ServiceCompat.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TemplateHelper.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/__init__.py6
24 files changed, 306 insertions, 191 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index b3781e299..e056c871a 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -68,7 +68,7 @@ class CfgGenshiGenerator(CfgGenerator):
#: exception in a Genshi template so we can provide a decent error
#: message that actually tells the end user where an error
#: occurred.
- pyerror_re = re.compile('<\w+ u?[\'"](.*?)\s*\.\.\.[\'"]>')
+ pyerror_re = re.compile(r'<\w+ u?[\'"](.*?)\s*\.\.\.[\'"]>')
def __init__(self, fname, spec, encoding):
CfgGenerator.__init__(self, fname, spec, encoding)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
index 7ebce192c..735f23a1c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
@@ -9,7 +9,7 @@ from Bcfg2.Server.Plugin import StructFile
from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError
from Bcfg2.Server.Plugins.Cfg.CfgPublicKeyCreator import CfgPublicKeyCreator
try:
- from Bcfg2.Server.Encryption import get_passphrases, ssl_encrypt
+ import Bcfg2.Server.Encryption
HAS_CRYPTO = True
except ImportError:
HAS_CRYPTO = False
@@ -51,8 +51,8 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
if (HAS_CRYPTO and
self.setup.cfp.has_section("sshkeys") and
self.setup.cfp.has_option("sshkeys", "passphrase")):
- return get_passphrases()[self.setup.cfp.get("sshkeys",
- "passphrase")]
+ return Bcfg2.Encrypption.get_passphrases()[
+ self.setup.cfp.get("sshkeys", "passphrase")]
return None
def handle_event(self, event):
@@ -72,7 +72,7 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
the given client metadata, and may be obtained by
doing ``self.XMLMatch(metadata)``
:type spec: lxml.etree._Element
- :returns: None
+ :returns: string - The filename of the private key
"""
if spec is None:
spec = self.XMLMatch(metadata)
@@ -142,7 +142,6 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
if spec is None:
spec = self.XMLMatch(metadata)
category = spec.get("category", self.category)
- print("category=%s" % category)
if category is None:
per_host_default = "true"
else:
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 2301de725..3e464af49 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -77,7 +77,7 @@ class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData,
self.encoding = encoding
self.setup = Bcfg2.Options.get_option_parser()
__init__.__doc__ = Bcfg2.Server.Plugin.SpecificData.__init__.__doc__ + \
-"""
+ """
.. -----
.. autoattribute:: CfgBaseFileMatcher.__basenames__
.. autoattribute:: CfgBaseFileMatcher.__extensions__
@@ -101,12 +101,12 @@ class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData,
components = ['^(?P<basename>%s)' % '|'.join(re.escape(b)
for b in basenames)]
if cls.__specific__:
- components.append('(|\\.H_(?P<hostname>\S+?)|' +
- '\.G(?P<prio>\d+)_(?P<group>\S+?))')
+ components.append(r'(|\.H_(?P<hostname>\S+?)|' +
+ r'\.G(?P<prio>\d+)_(?P<group>\S+?))')
if cls.__extensions__:
- components.append('\\.(?P<extension>%s)' %
- '|'.join(cls.__extensions__))
- components.append('$')
+ components.append(r'\.(?P<extension>%s)' %
+ r'|'.join(cls.__extensions__))
+ components.append(r'$')
return re.compile("".join(components))
@classmethod
@@ -563,6 +563,8 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
else:
try:
if not isinstance(data, unicode):
+ if not isinstance(data, str):
+ data = data.decode('utf-8')
data = u_str(data, self.encoding)
except UnicodeDecodeError:
msg = "Failed to decode %s: %s" % (entry.get('name'),
@@ -715,8 +717,8 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
pass
if not rv or not rv[0].hostname:
- rv.append(Bcfg2.Server.Plugin.Specificity(
- hostname=metadata.hostname))
+ rv.append(
+ Bcfg2.Server.Plugin.Specificity(hostname=metadata.hostname))
return rv
def build_filename(self, specific):
diff --git a/src/lib/Bcfg2/Server/Plugins/FileProbes.py b/src/lib/Bcfg2/Server/Plugins/FileProbes.py
index 33914bd45..316e4bc53 100644
--- a/src/lib/Bcfg2/Server/Plugins/FileProbes.py
+++ b/src/lib/Bcfg2/Server/Plugins/FileProbes.py
@@ -67,7 +67,8 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
self.config = \
Bcfg2.Server.Plugin.StructFile(os.path.join(self.data,
'config.xml'),
- should_monitor=True)
+ should_monitor=True,
+ create=self.name)
self.entries = dict()
self.probes = dict()
@@ -225,11 +226,8 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
root = lxml.etree.Element("FileInfo")
root.append(info)
try:
- open(infoxml,
- "w").write(
- lxml.etree.tostring(root,
- xml_declaration=False,
- pretty_print=True).decode('UTF-8'))
+ root.getroottree().write(infoxml, xml_declaration=False,
+ pretty_print=True)
except IOError:
err = sys.exc_info()[1]
self.logger.error("Could not write %s: %s" % (infoxml, err))
diff --git a/src/lib/Bcfg2/Server/Plugins/Git.py b/src/lib/Bcfg2/Server/Plugins/Git.py
index 781413e1a..58a5c58f0 100644
--- a/src/lib/Bcfg2/Server/Plugins/Git.py
+++ b/src/lib/Bcfg2/Server/Plugins/Git.py
@@ -46,7 +46,7 @@ class Git(Version):
else:
cmd = ["git", "--git-dir", self.vcs_path,
"--work-tree", self.vcs_root, "rev-parse", "HEAD"]
- self.debug_log("Git: Running cmd")
+ self.debug_log("Git: Running %s" % cmd)
result = self.cmd.run(cmd)
if not result.success:
raise Exception(result.stderr)
diff --git a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
new file mode 100644
index 000000000..810b273af
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
@@ -0,0 +1,47 @@
+""" GroupLogic is a connector plugin that lets you use an XML Genshi
+template to dynamically set additional groups for clients. """
+
+import os
+import lxml.etree
+import Bcfg2.Server.Plugin
+try:
+ from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile
+except ImportError:
+ # BundleTemplateFile missing means that genshi is missing. we
+ # import genshi to get the _real_ error
+ import genshi # pylint: disable=W0611
+
+
+class GroupLogicConfig(BundleTemplateFile):
+ """ Representation of the GroupLogic groups.xml file """
+ create = lxml.etree.Element("GroupLogic",
+ nsmap=dict(py="http://genshi.edgewall.org/"))
+
+ def __init__(self, name, fam):
+ BundleTemplateFile.__init__(self, name,
+ Bcfg2.Server.Plugin.Specificity(), None)
+ self.fam = fam
+ self.should_monitor = True
+ self.fam.AddMonitor(self.name, self)
+
+ def _match(self, item, metadata):
+ if item.tag == 'Group' and not len(item.getchildren()):
+ return [item]
+ return BundleTemplateFile._match(self, item, metadata)
+
+
+class GroupLogic(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.Connector):
+ """ GroupLogic is a connector plugin that lets you use an XML
+ Genshi template to dynamically set additional groups for
+ clients. """
+
+ def __init__(self, core, datastore):
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ Bcfg2.Server.Plugin.Connector.__init__(self)
+ self.config = GroupLogicConfig(os.path.join(self.data, "groups.xml"),
+ core.fam)
+
+ def get_additional_groups(self, metadata):
+ return [el.get("name")
+ for el in self.config.get_xml_value(metadata).findall("Group")]
diff --git a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py
index 99f01201b..9042a979e 100644
--- a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py
+++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py
@@ -3,7 +3,6 @@
import os
import re
import sys
-import logging
import Bcfg2.Server.Lint
import Bcfg2.Server.Plugin
from Bcfg2.Utils import PackedDigitRange
@@ -16,16 +15,16 @@ class PatternMap(object):
self.pattern = pattern
self.rangestr = rangestr
self.groups = groups
- if pattern != None:
+ if pattern is not None:
self.re = re.compile(pattern)
self.process = self.process_re
- elif rangestr != None:
+ elif rangestr is not None:
if '\\' in rangestr:
raise Exception("Backslashes are not allowed in NameRanges")
range_finder = r'\[\[[\d\-,]+\]\]'
self.process = self.process_range
- self.re = re.compile('^' + re.sub(range_finder, '(\d+)',
- rangestr))
+ self.re = re.compile(r'^' + re.sub(range_finder, r'(\d+)',
+ rangestr))
dmatcher = re.compile(re.sub(range_finder,
r'\[\[([\d\-,]+)\]\]',
rangestr))
@@ -67,13 +66,13 @@ class PatternMap(object):
class PatternFile(Bcfg2.Server.Plugin.XMLFileBacked):
""" representation of GroupPatterns config.xml """
__identifier__ = None
+ create = 'GroupPatterns'
def __init__(self, filename, core=None):
Bcfg2.Server.Plugin.XMLFileBacked.__init__(self, filename,
should_monitor=True)
self.core = core
self.patterns = []
- self.logger = logging.getLogger(self.__class__.__name__)
def Index(self):
Bcfg2.Server.Plugin.XMLFileBacked.Index(self)
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index 7f8db7b6d..49e36f72b 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -75,6 +75,7 @@ if HAS_DJANGO:
yield client.hostname
def keys(self):
+ """ Get keys for the mapping """
return [c.hostname for c in MetadataClientModel.objects.all()]
def __contains__(self, key):
@@ -94,8 +95,10 @@ class XMLMetadataConfig(Bcfg2.Server.Plugin.XMLFileBacked):
# then we immediately set should_monitor to the proper value,
# so that XInclude'd files get properly watched
fpath = os.path.join(metadata.data, basefile)
+ toptag = os.path.splitext(basefile)[0].title()
Bcfg2.Server.Plugin.XMLFileBacked.__init__(self, fpath,
- should_monitor=False)
+ should_monitor=False,
+ create=toptag)
self.should_monitor = watch_clients
self.metadata = metadata
self.basefile = basefile
@@ -325,6 +328,11 @@ class ClientMetadata(object):
return grp
return ''
+ def __repr__(self):
+ return "%s(%s, profile=%s, groups=%s)" % (self.__class__.__name__,
+ self.hostname,
+ self.profile, self.groups)
+
class MetadataQuery(object):
""" This class provides query methods for the metadata of all
@@ -438,7 +446,7 @@ class MetadataQuery(object):
return [self.by_name(name) for name in self.all_clients()]
-class MetadataGroup(tuple):
+class MetadataGroup(tuple): # pylint: disable=E0012,R0924
""" representation of a metadata group. basically just a named tuple """
# pylint: disable=R0913,W0613
@@ -595,7 +603,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
def _add_xdata(self, config, tag, name, attribs=None, alias=False):
""" Generic method to add XML data (group, client, etc.) """
node = self._search_xdata(tag, name, config.xdata, alias=alias)
- if node != None:
+ if node is not None:
raise Bcfg2.Server.Plugin.MetadataConsistencyError("%s \"%s\" "
"already exists"
% (tag, name))
@@ -655,7 +663,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
def _update_xdata(self, config, tag, name, attribs, alias=False):
""" Generic method to modify XML data (group, client, etc.) """
node = self._search_xdata(tag, name, config.xdata, alias=alias)
- if node == None:
+ if node is None:
self.logger.error("%s \"%s\" does not exist" % (tag, name))
raise Bcfg2.Server.Plugin.MetadataConsistencyError
xdict = config.find_xml_for_xpath('.//%s[@name="%s"]' %
@@ -672,7 +680,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
"""Update a groups attributes."""
if self._use_db:
msg = "Metadata does not support updating groups with " + \
- "use_database enabled"
+ "use_database enabled"
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
else:
@@ -700,7 +708,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
def _remove_xdata(self, config, tag, name):
""" Generic method to remove XML data (group, client, etc.) """
node = self._search_xdata(tag, name, config.xdata)
- if node == None:
+ if node is None:
self.logger.error("%s \"%s\" does not exist" % (tag, name))
raise Bcfg2.Server.Plugin.MetadataConsistencyError
xdict = config.find_xml_for_xpath('.//%s[@name="%s"]' %
@@ -936,16 +944,11 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
if group not in self.groups:
self.debug_log("Client %s set as nonexistent group %s"
% (client, group))
- for gname, ginfo in list(self.groups.items()):
- for group in ginfo.groups:
- if group not in self.groups:
- self.debug_log("Group %s set as nonexistent group %s" %
- (gname, group))
def set_profile(self, client, profile, addresspair):
"""Set group parameter for provided client."""
- self.logger.info("Asserting client %s profile to %s" %
- (client, profile))
+ self.logger.info("Asserting client %s profile to %s" % (client,
+ profile))
if False in list(self.states.values()):
raise Bcfg2.Server.Plugin.MetadataRuntimeError("Metadata has not "
"been read yet")
@@ -996,19 +999,18 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
self.clients_xml.write()
def set_version(self, client, version):
- """Set group parameter for provided client."""
- if client in self.clients:
- if client not in self.versions or version != self.versions[client]:
- self.logger.info("Setting client %s version to %s" %
- (client, version))
- if not self._use_db:
- self.update_client(client, dict(version=version))
- self.clients_xml.write()
- self.versions[client] = version
- else:
- msg = "Cannot set version on non-existent client %s" % client
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.MetadataConsistencyError(msg)
+ """Set version for provided client."""
+ if client not in self.clients:
+ # this creates the client as a side effect
+ self.get_initial_metadata(client)
+
+ if client not in self.versions or version != self.versions[client]:
+ self.logger.info("Setting client %s version to %s" % (client,
+ version))
+ if not self._use_db:
+ self.update_client(client, dict(version=version))
+ self.clients_xml.write()
+ self.versions[client] = version
def resolve_client(self, addresspair, cleanup_cache=False):
"""Lookup address locally or in DNS to get a hostname."""
@@ -1085,7 +1087,6 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
raise Bcfg2.Server.Plugin.MetadataRuntimeError("Metadata has not "
"been read yet")
client = client.lower()
-
if client in self.core.metadata_cache:
return self.core.metadata_cache[client]
@@ -1096,6 +1097,29 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
categories = dict()
profile = None
+ def _add_group(grpname):
+ """ Add a group to the set of groups for this client.
+ Handles setting categories and category suppression.
+ Returns the new profile for the client (which might be
+ unchanged). """
+ groups.add(grpname)
+ if grpname in self.groups:
+ group = self.groups[grpname]
+ category = group.category
+ if category:
+ if category in categories:
+ self.logger.warning("%s: Group %s suppressed by "
+ "category %s; %s already a member "
+ "of %s" %
+ (self.name, grpname, category,
+ client, categories[category]))
+ return
+ categories[category] = grpname
+ if not profile and group.is_profile:
+ return grpname
+ else:
+ return profile
+
if client not in self.clients:
pgroup = None
if client in self.clientgroups:
@@ -1105,41 +1129,28 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
if pgroup:
self.set_profile(client, pgroup, (None, None))
- groups.add(pgroup)
- category = self.groups[pgroup].category
- if category:
- categories[category] = pgroup
- if (pgroup in self.groups and self.groups[pgroup].is_profile):
- profile = pgroup
+ profile = _add_group(pgroup)
else:
msg = "Cannot add new client %s; no default group set" % client
self.logger.error(msg)
raise Bcfg2.Server.Plugin.MetadataConsistencyError(msg)
- if client in self.clientgroups:
- for cgroup in self.clientgroups[client]:
- if cgroup in groups:
- continue
- if cgroup not in self.groups:
- self.groups[cgroup] = MetadataGroup(cgroup)
- category = self.groups[cgroup].category
- if category and category in categories:
- self.logger.warning("%s: Group %s suppressed by "
- "category %s; %s already a member "
- "of %s" %
- (self.name, cgroup, category,
- client, categories[category]))
- continue
- if category:
- categories[category] = cgroup
- groups.add(cgroup)
- # favor client groups for setting profile
- if not profile and self.groups[cgroup].is_profile:
- profile = cgroup
+ for cgroup in self.clientgroups.get(client, []):
+ if cgroup in groups:
+ continue
+ if cgroup not in self.groups:
+ self.groups[cgroup] = MetadataGroup(cgroup)
+ profile = _add_group(cgroup)
groups, categories = self._merge_groups(client, groups,
categories=categories)
+ if len(groups) == 0 and self.default:
+ # no initial groups; add the default profile
+ profile = _add_group(self.default)
+ groups, categories = self._merge_groups(client, groups,
+ categories=categories)
+
bundles = set()
for group in groups:
try:
@@ -1475,6 +1486,7 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
self.duplicate_groups()
self.duplicate_default_groups()
self.duplicate_clients()
+ self.default_is_profile()
@classmethod
def Errors(cls):
@@ -1484,11 +1496,15 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
"non-profile-set-as-profile": "error",
"duplicate-group": "error",
"duplicate-client": "error",
- "multiple-default-groups": "error"}
+ "multiple-default-groups": "error",
+ "default-is-not-profile": "error"}
def deprecated_options(self):
""" check for the location='floating' option, which has been
deprecated in favor of floating='true' """
+ if not hasattr(self.metadata, "clients_xml"):
+ # using metadata database
+ return
clientdata = self.metadata.clients_xml.xdata
for el in clientdata.xpath("//Client"):
loc = el.get("location")
@@ -1514,6 +1530,9 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
def bogus_profiles(self):
""" check for clients that have profiles that are either not
flagged as public groups in groups.xml, or don't exist """
+ if not hasattr(self.metadata, "clients_xml"):
+ # using metadata database
+ return
for client in self.metadata.clients_xml.xdata.findall('.//Client'):
profile = client.get("profile")
if profile not in self.metadata.groups:
@@ -1533,8 +1552,8 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
tag as a definition if it a) has profile or public set; or b)
has any children. """
self.duplicate_entries(
- self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \
- self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"),
+ self.metadata.groups_xml.xdata.xpath("//Groups/Group") +
+ self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"),
"group",
include=lambda g: (g.get("profile") or
g.get("public") or
@@ -1554,6 +1573,9 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
def duplicate_clients(self):
""" check for clients that are defined twice. """
+ if not hasattr(self.metadata, "clients_xml"):
+ # using metadata database
+ return
self.duplicate_entries(
self.metadata.clients_xml.xdata.xpath("//Client"),
"client")
@@ -1574,3 +1596,14 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
self.LintError("duplicate-%s" % etype,
"%s %s is defined multiple times:\n%s" %
(etype.title(), ename, "\n".join(els)))
+
+ def default_is_profile(self):
+ """ ensure that the default group is a profile group """
+ if (self.metadata.default and
+ not self.metadata.groups[self.metadata.default].is_profile):
+ xdata = \
+ self.metadata.groups_xml.xdata.xpath("//Group[@name='%s']" %
+ self.metadata.default)[0]
+ self.LintError("default-is-not-profile",
+ "Default group is not a profile group:\n%s" %
+ self.RenderXML(xdata))
diff --git a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
index d5ea0cb24..9603cd518 100644
--- a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
+++ b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
@@ -5,23 +5,22 @@ import re
import sys
import glob
import socket
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import Plugin, Generator, StructFile, \
+ PluginExecutionError
-class NagiosGen(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Generator):
+class NagiosGen(Plugin, Generator):
""" NagiosGen is a Bcfg2 plugin that dynamically generates Nagios
configuration file based on Bcfg2 data. """
__author__ = 'bcfg-dev@mcs.anl.gov'
line_fmt = '\t%-32s %s'
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Generator.__init__(self)
+ Plugin.__init__(self, core, datastore)
+ Generator.__init__(self)
self.config = \
- Bcfg2.Server.Plugin.StructFile(os.path.join(self.data,
- 'config.xml'),
- should_monitor=True)
+ StructFile(os.path.join(self.data, 'config.xml'),
+ should_monitor=True, create=self.name)
self.Entries = {'Path':
{'/etc/nagiosgen.status': self.createhostconfig,
'/etc/nagios/nagiosgen.cfg': self.createserverconfig}}
@@ -42,9 +41,9 @@ class NagiosGen(Bcfg2.Server.Plugin.Plugin,
try:
host_address = socket.gethostbyname(metadata.hostname)
except socket.gaierror:
- self.logger.error("Failed to find IP address for %s" %
- metadata.hostname)
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ self.logger.error()
+ raise PluginExecutionError("Failed to find IP address for %s" %
+ metadata.hostname)
host_groups = [grp for grp in metadata.groups
if os.path.isfile('%s/%s-group.cfg' % (self.data, grp))]
host_config = ['define host {',
diff --git a/src/lib/Bcfg2/Server/Plugins/Ohai.py b/src/lib/Bcfg2/Server/Plugins/Ohai.py
index 8b73f0ba7..18261be10 100644
--- a/src/lib/Bcfg2/Server/Plugins/Ohai.py
+++ b/src/lib/Bcfg2/Server/Plugins/Ohai.py
@@ -2,8 +2,10 @@
operating system using ohai
(http://wiki.opscode.com/display/chef/Ohai) """
-import lxml.etree
import os
+import sys
+import glob
+import lxml.etree
import Bcfg2.Server.Plugin
try:
@@ -31,22 +33,39 @@ class OhaiCache(object):
self.dirname = dirname
self.cache = dict()
+ def hostpath(self, host):
+ """ Get the path to the file that contains Ohai data for the
+ given host """
+ return os.path.join(self.dirname, "%s.json" % host)
+
def __setitem__(self, item, value):
- if value == None:
+ if value is None:
# simply return if the client returned nothing
return
self.cache[item] = json.loads(value)
- open("%s/%s.json" % (self.dirname, item), 'w').write(value)
+ open(self.hostpath(item), 'w').write(value)
def __getitem__(self, item):
if item not in self.cache:
try:
- data = open("%s/%s.json" % (self.dirname, item)).read()
+ data = open(self.hostpath(item)).read()
except:
raise KeyError(item)
self.cache[item] = json.loads(data)
return self.cache[item]
+ def __delitem__(self, item):
+ if item in self.cache:
+ del self.cache[item]
+ try:
+ os.unlink(self.hostpath(item))
+ except:
+ raise IndexError("Could not unlink %s: %s" % (self.hostpath(item),
+ sys.exc_info()[1]))
+
+ def __len__(self):
+ return len(glob.glob(self.hostpath('*')))
+
def __iter__(self):
data = list(self.cache.keys())
data.extend([x[:-5] for x in os.listdir(self.dirname)])
@@ -67,10 +86,6 @@ class Ohai(Bcfg2.Server.Plugin.Plugin,
self.probe = lxml.etree.Element('probe', name='Ohai', source='Ohai',
interpreter='/bin/sh')
self.probe.text = PROBECODE
- try:
- os.stat(self.data)
- except OSError:
- os.makedirs(self.data)
self.cache = OhaiCache(self.data)
def GetProbes(self, _):
diff --git a/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py b/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py
index 490ee6f20..1736becc7 100644
--- a/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py
+++ b/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py
@@ -15,7 +15,7 @@ class POSIXCompat(Bcfg2.Server.Plugin.Plugin,
def validate_goals(self, metadata, goals):
"""Verify that we are generating correct old POSIX entries."""
- if metadata.version_info and metadata.version_info > (1, 3, 0, '', 0):
+ if metadata.version_info and metadata.version_info >= (1, 3, 0, '', 0):
# do not care about a client that is _any_ 1.3.0 release
# (including prereleases and RCs)
return
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
index 57f802bb5..48c580be1 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
@@ -29,8 +29,8 @@ class AptCollection(Collection):
""" Get an APT configuration file (i.e., ``sources.list``).
:returns: string """
- lines = ["# This config was generated automatically by the Bcfg2 " \
- "Packages plugin", '']
+ lines = ["# This config was generated automatically by the Bcfg2 "
+ "Packages plugin", '']
for source in self:
if source.rawurl:
@@ -88,6 +88,8 @@ class AptSource(Source):
self.logger.error("Packages: Failed to read file %s" % fname)
raise
for line in reader.readlines():
+ if not isinstance(line, str):
+ line = line.decode('utf-8')
words = str(line.strip()).split(':', 1)
if words[0] == 'Package':
pkgname = words[1].strip().rstrip()
@@ -99,8 +101,8 @@ class AptSource(Source):
vindex = 0
for dep in words[1].split(','):
if '|' in dep:
- cdeps = [re.sub('\s+', '',
- re.sub('\(.*\)', '', cdep))
+ cdeps = [re.sub(r'\s+', '',
+ re.sub(r'\(.*\)', '', cdep))
for cdep in dep.split('|')]
dyn_dname = "choice-%s-%s-%s" % (pkgname,
barch,
@@ -109,7 +111,7 @@ class AptSource(Source):
bdeps[barch][pkgname].append(dyn_dname)
bprov[barch][dyn_dname] = set(cdeps)
else:
- raw_dep = re.sub('\(.*\)', '', dep)
+ raw_dep = re.sub(r'\(.*\)', '', dep)
raw_dep = raw_dep.rstrip().strip()
bdeps[barch][pkgname].append(raw_dep)
elif words[0] == 'Provides':
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
index e9744c777..aa6127f57 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
@@ -9,6 +9,7 @@ from Bcfg2.Server.Statistics import track_statistics
from Bcfg2.Server.Plugins.Packages.Source import SourceInitError
+# pylint: disable=E0012,R0924
class PackagesSources(Bcfg2.Server.Plugin.StructFile,
Bcfg2.Server.Plugin.Debuggable):
""" PackagesSources handles parsing of the
@@ -17,6 +18,9 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile,
:class:`Bcfg2.Server.Plugins.Packages.Source.Source` object for
each ``Source`` tag. """
+ __identifier__ = None
+ create = "Sources"
+
def __init__(self, filename, cachepath, packages):
"""
:param filename: The full path to ``sources.xml``
@@ -34,14 +38,8 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile,
If ``sources.xml`` cannot be read
"""
Bcfg2.Server.Plugin.Debuggable.__init__(self)
- try:
- Bcfg2.Server.Plugin.StructFile.__init__(self, filename,
- should_monitor=True)
- except OSError:
- err = sys.exc_info()[1]
- msg = "Packages: Failed to read configuration file: %s" % err
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginInitError(msg)
+ Bcfg2.Server.Plugin.StructFile.__init__(self, filename,
+ should_monitor=True)
#: The full path to the directory where
#: :class:`Bcfg2.Server.Plugins.Packages.Source.Source` data
@@ -124,7 +122,7 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile,
""" Create a
:class:`Bcfg2.Server.Plugins.Packages.Source.Source` subclass
object from XML representation of a source in ``sources.xml``.
- ``source_from-xml`` determines the appropriate subclass of
+ ``source_from_xml`` determines the appropriate subclass of
``Source`` to instantiate according to the ``type`` attribute
of the ``Source`` tag.
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
index 5cf90e188..3319dda78 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
@@ -52,8 +52,8 @@ import sys
import Bcfg2.Server.Plugin
from Bcfg2.Options import get_option_parser
from Bcfg2.Compat import HTTPError, HTTPBasicAuthHandler, \
- HTTPPasswordMgrWithDefaultRealm, install_opener, build_opener, \
- urlopen, cPickle, md5
+ HTTPPasswordMgrWithDefaultRealm, install_opener, build_opener, urlopen, \
+ cPickle, md5
from Bcfg2.Server.Statistics import track_statistics
@@ -66,7 +66,7 @@ def fetch_url(url):
:raises: URLError - Failure fetching URL
:returns: string - the content of the page at the given URL """
if '@' in url:
- mobj = re.match('(\w+://)([^:]+):([^@]+)@(.*)$', url)
+ mobj = re.match(r'(\w+://)([^:]+):([^@]+)@(.*)$', url)
if not mobj:
raise ValueError("Invalid URL")
user = mobj.group(2)
@@ -308,7 +308,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902
:raises: OSError - If the saved data cannot be read
:raises: cPickle.UnpicklingError - If the saved data is corrupt """
- data = open(self.cachefile)
+ data = open(self.cachefile, 'rb')
(self.pkgnames, self.deps, self.provides,
self.essentialpkgs) = cPickle.load(data)
@@ -608,7 +608,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902
self.logger.info("Packages: Updating %s" % url)
fname = self.escape_url(url)
try:
- open(fname, 'w').write(fetch_url(url))
+ open(fname, 'wb').write(fetch_url(url))
except ValueError:
self.logger.error("Packages: Bad url string %s" % url)
raise
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index 4535fb76d..ab96d3f59 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -68,7 +68,7 @@ from Bcfg2.Compat import StringIO, cPickle, HTTPError, URLError, \
# pylint: enable=W0622
from Bcfg2.Server.Plugins.Packages.Collection import Collection
from Bcfg2.Server.Plugins.Packages.Source import SourceInitError, Source, \
- fetch_url
+ fetch_url
from Bcfg2.Server.Statistics import track_statistics
LOGGER = logging.getLogger(__name__)
@@ -279,7 +279,7 @@ class YumCollection(Collection):
#: Define a unique cache file for this collection to use
#: for cached yum metadata
self.cachefile = os.path.join(self.cachepath,
- "cache-%s" % self.cachekey)
+ "cache-%s" % self.cachekey)
if not os.path.exists(self.cachefile):
os.mkdir(self.cachefile)
@@ -420,7 +420,7 @@ class YumCollection(Collection):
config.add_section(reponame)
added = True
except ConfigParser.DuplicateSectionError:
- match = re.search("-(\d+)", reponame)
+ match = re.search(r'-(\d+)', reponame)
if match:
rid = int(match.group(1)) + 1
else:
@@ -1153,7 +1153,7 @@ class YumSource(Source):
if entry.get('name').startswith('/'):
self.needed_paths.add(entry.get('name'))
pro = pdata.find(RP + 'provides')
- if pro != None:
+ if pro is not None:
for entry in pro.getchildren():
prov = entry.get('name')
if prov not in self.provides[arch]:
@@ -1169,9 +1169,9 @@ class YumSource(Source):
try:
groupid = group.xpath('id')[0].text
self.yumgroups[groupid] = {'mandatory': list(),
- 'default': list(),
- 'optional': list(),
- 'conditional': list()}
+ 'default': list(),
+ 'optional': list(),
+ 'conditional': list()}
except IndexError:
continue
try:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
index 2175cf0aa..052c362ab 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
@@ -5,7 +5,6 @@ determine the completeness of the client configuration. """
import os
import sys
import glob
-import copy
import shutil
import lxml.etree
import Bcfg2.Logger
@@ -20,7 +19,8 @@ from Bcfg2.Server.Statistics import track_statistics
YUM_CONFIG_DEFAULT = "/etc/yum.repos.d/bcfg2.repo"
#: The default path for generated apt configs
-APT_CONFIG_DEFAULT = "/etc/apt/sources.d/bcfg2"
+APT_CONFIG_DEFAULT = \
+ "/etc/apt/sources.list.d/bcfg2-packages-generated-sources.list"
class Packages(Bcfg2.Server.Plugin.Plugin,
@@ -177,6 +177,14 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
for (key, value) in list(attrib.items()):
entry.attrib.__setitem__(key, value)
+ def get_config(self, metadata):
+ """ Get yum/apt config, as a string, for the specified client.
+
+ :param metadata: The client to create the config for.
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ """
+ return self.get_collection(metadata).get_config()
+
def HandleEntry(self, entry, metadata):
""" Bind configuration entries. ``HandleEntry`` handles
entries two different ways:
@@ -226,14 +234,14 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
return True
elif entry.tag == 'Path':
# managed entries for yum/apt configs
- if (entry.get("name") == \
- self.core.setup.cfp.get("packages",
- "yum_config",
- default=YUM_CONFIG_DEFAULT) or
- entry.get("name") == \
- self.core.setup.cfp.get("packages",
- "apt_config",
- default=APT_CONFIG_DEFAULT)):
+ if (entry.get("name") ==
+ self.core.setup.cfp.get("packages",
+ "yum_config",
+ default=YUM_CONFIG_DEFAULT) or
+ entry.get("name") ==
+ self.core.setup.cfp.get("packages",
+ "apt_config",
+ default=APT_CONFIG_DEFAULT)):
return True
return False
@@ -523,7 +531,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
"""
collection = self.get_collection(metadata)
return dict(sources=collection.get_additional_data(),
- allsources=copy.deepcopy(self.sources))
+ get_config=self.get_config)
def end_client_run(self, metadata):
""" Hook to clear the cache for this client in
diff --git a/src/lib/Bcfg2/Server/Plugins/Probes.py b/src/lib/Bcfg2/Server/Plugins/Probes.py
index 2ea5088de..7e4935d74 100644
--- a/src/lib/Bcfg2/Server/Plugins/Probes.py
+++ b/src/lib/Bcfg2/Server/Plugins/Probes.py
@@ -60,12 +60,12 @@ class ClientProbeDataSet(dict):
dict.__init__(self, *args, **kwargs)
-class ProbeData(str):
+class ProbeData(str): # pylint: disable=E0012,R0924
""" a ProbeData object emulates a str object, but also has .xdata,
.json, and .yaml properties to provide convenient ways to use
ProbeData objects as XML, JSON, or YAML data """
def __new__(cls, data):
- return str.__new__(cls, data)
+ return str.__new__(cls, data.encode('utf-8'))
def __init__(self, data): # pylint: disable=W0613
str.__init__(self)
@@ -113,15 +113,15 @@ class ProbeData(str):
class ProbeSet(Bcfg2.Server.Plugin.EntrySet):
""" Handle universal and group- and host-specific probe files """
- ignore = re.compile("^(\.#.*|.*~|\\..*\\.(tmp|sw[px])|probed\\.xml)$")
+ ignore = re.compile(r'^(\.#.*|.*~|\..*\.(tmp|sw[px])|probed\.xml)$')
probename = \
- re.compile("(.*/)?(?P<basename>\S+?)(\.(?P<mode>(?:G\d\d)|H)_\S+)?$")
- bangline = re.compile('^#!\s*(?P<interpreter>.*)$')
+ re.compile(r'(.*/)?(?P<basename>\S+?)(\.(?P<mode>(?:G\d\d)|H)_\S+)?$')
+ bangline = re.compile(r'^#!\s*(?P<interpreter>.*)$')
basename_is_regex = True
def __init__(self, path, encoding, plugin_name):
self.plugin_name = plugin_name
- Bcfg2.Server.Plugin.EntrySet.__init__(self, '[0-9A-Za-z_\-]+', path,
+ Bcfg2.Server.Plugin.EntrySet.__init__(self, r'[0-9A-Za-z_\-]+', path,
Bcfg2.Server.Plugin.SpecificData,
encoding)
Bcfg2.Server.FileMonitor.get_fam().AddMonitor(path, self)
@@ -155,7 +155,20 @@ class ProbeSet(Bcfg2.Server.Plugin.EntrySet):
probe = lxml.etree.Element('probe')
probe.set('name', os.path.basename(name))
probe.set('source', self.plugin_name)
- probe.text = entry.data
+ if (metadata.version_info and
+ metadata.version_info > (1, 3, 1, '', 0)):
+ try:
+ probe.text = entry.data.decode('utf-8')
+ except AttributeError:
+ probe.text = entry.data
+ else:
+ try:
+ probe.text = entry.data
+ except: # pylint: disable=W0702
+ self.logger.error("Client unable to handle unicode "
+ "probes. Skipping %s" %
+ probe.get('name'))
+ continue
match = self.bangline.match(entry.data.split('\n')[0])
if match:
probe.set('interpreter', match.group('interpreter'))
@@ -211,15 +224,21 @@ class Probes(Bcfg2.Server.Plugin.Probing,
lxml.etree.SubElement(top, 'Client', name=client,
timestamp=str(int(probedata.timestamp)))
for probe in sorted(probedata):
- lxml.etree.SubElement(ctag, 'Probe', name=probe,
- value=str(self.probedata[client][probe]))
+ try:
+ lxml.etree.SubElement(
+ ctag, 'Probe', name=probe,
+ value=str(
+ self.probedata[client][probe]).decode('utf-8'))
+ except AttributeError:
+ lxml.etree.SubElement(
+ ctag, 'Probe', name=probe,
+ value=str(self.probedata[client][probe]))
for group in sorted(self.cgroups[client]):
lxml.etree.SubElement(ctag, "Group", name=group)
try:
- datafile = open(os.path.join(self.data, 'probed.xml'), 'w')
- datafile.write(lxml.etree.tostring(
- top, xml_declaration=False,
- pretty_print='true').decode('UTF-8'))
+ top.getroottree().write(os.path.join(self.data, 'probed.xml'),
+ xml_declaration=False,
+ pretty_print='true')
except IOError:
err = sys.exc_info()[1]
self.logger.error("Failed to write probed.xml: %s" % err)
@@ -234,9 +253,10 @@ class Probes(Bcfg2.Server.Plugin.Probing,
if pdata.data != data:
pdata.data = data
pdata.save()
+
ProbesDataModel.objects.filter(
hostname=client.hostname).exclude(
- probe__in=self.probedata[client.hostname]).delete()
+ probe__in=self.probedata[client.hostname]).delete()
for group in self.cgroups[client.hostname]:
try:
@@ -248,7 +268,7 @@ class Probes(Bcfg2.Server.Plugin.Probing,
grp.save()
ProbesGroupsModel.objects.filter(
hostname=client.hostname).exclude(
- group__in=self.cgroups[client.hostname]).delete()
+ group__in=self.cgroups[client.hostname]).delete()
def load_data(self):
""" Load probe data from the appropriate backend (probed.xml
@@ -322,7 +342,7 @@ class Probes(Bcfg2.Server.Plugin.Probing,
def ReceiveDataItem(self, client, data, cgroups, cprobedata):
"""Receive probe results pertaining to client."""
- if data.text == None:
+ if data.text is None:
self.logger.info("Got null response to probe %s from %s" %
(data.get('name'), client.hostname))
cprobedata[data.get('name')] = ProbeData('')
diff --git a/src/lib/Bcfg2/Server/Plugins/Properties.py b/src/lib/Bcfg2/Server/Plugins/Properties.py
index 762f9f8f0..f091acf01 100644
--- a/src/lib/Bcfg2/Server/Plugins/Properties.py
+++ b/src/lib/Bcfg2/Server/Plugins/Properties.py
@@ -216,8 +216,13 @@ class XMLPropertyFile(Bcfg2.Server.Plugin.StructFile, PropertyFile):
return repr(self.xdata)
-class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked):
- """ A collection of properties files. """
+class Properties(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.Connector,
+ Bcfg2.Server.Plugin.DirectoryBacked):
+ """ The properties plugin maps property files into client metadata
+ instances. """
+
+ #: Extensions that are understood by Properties.
extensions = ["xml"]
if HAS_JSON:
extensions.append("json")
@@ -234,14 +239,17 @@ class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked):
#: Ignore XML schema (``.xsd``) files
ignore = re.compile(r'.*\.xsd$')
- def __init__(self, data):
- Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, data)
+ def __init__(self, core, datastore):
+ global SETUP # pylint: disable=W0603
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ Bcfg2.Server.Plugin.Connector.__init__(self)
+ Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data)
#: Instead of creating children of this object with a static
#: object, we use :func:`property_dispatcher` to create a
#: child of the appropriate subclass of :class:`PropertyFile`
self.__child__ = self.property_dispatcher
- __init__.__doc__ = Bcfg2.Server.Plugin.DirectoryBacked.__init__.__doc__
+ __init__.__doc__ = Bcfg2.Server.Plugin.Plugin.__init__.__doc__
def property_dispatcher(self, fname):
""" Dispatch an event on a Properties file to the
@@ -262,28 +270,9 @@ class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked):
raise Bcfg2.Server.Plugin.PluginExecutionError(
"Properties: Unknown extension %s" % fname)
-
-class Properties(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Connector):
- """ The properties plugin maps property files into client metadata
- instances. """
-
- def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Connector.__init__(self)
- try:
- self.store = PropDirectoryBacked(self.data)
- except OSError:
- err = sys.exc_info()[1]
- self.logger.error("Error while creating Properties store: %s" %
- err)
- raise Bcfg2.Server.Plugin.PluginInitError
-
- __init__.__doc__ = Bcfg2.Server.Plugin.Plugin.__init__.__doc__
-
def get_additional_data(self, metadata):
rv = dict()
- for fname, pfile in self.store.entries.items():
+ for fname, pfile in self.entries.items():
rv[fname] = pfile.get_additional_data(metadata)
return rv
get_additional_data.__doc__ = \
diff --git a/src/lib/Bcfg2/Server/Plugins/Reporting.py b/src/lib/Bcfg2/Server/Plugins/Reporting.py
index a6dc2c1ef..3354763d4 100644
--- a/src/lib/Bcfg2/Server/Plugins/Reporting.py
+++ b/src/lib/Bcfg2/Server/Plugins/Reporting.py
@@ -92,10 +92,11 @@ class Reporting(Statistics, Threaded, PullSource, Debuggable):
# try 3 times to store the data
for i in [1, 2, 3]:
try:
- self.transport.store(client.hostname, cdata,
- lxml.etree.tostring(
+ self.transport.store(
+ client.hostname, cdata,
+ lxml.etree.tostring(
stats,
- xml_declaration=False).decode('UTF-8'))
+ xml_declaration=False))
self.debug_log("%s: Queued statistics data for %s" %
(self.__class__.__name__, client.hostname))
return
diff --git a/src/lib/Bcfg2/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
index fb5bd50bf..1264fd1cf 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSHbase.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
@@ -202,10 +202,11 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
if specific.hostname and specific.hostname in names:
hostnames = names[specific.hostname]
elif specific.group:
- hostnames = list(chain(
+ hostnames = list(
+ chain(
*[names[cmeta.hostname]
- for cmeta in \
- mquery.by_groups([specific.group])]))
+ for cmeta in
+ mquery.by_groups([specific.group])]))
elif specific.all:
# a generic key for all hosts? really?
hostnames = list(chain(*list(names.values())))
diff --git a/src/lib/Bcfg2/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
index d52d9325c..b21732666 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSLCA.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
@@ -69,7 +69,7 @@ class SSLCACertSpec(SSLCAXMLSpec):
def get_spec(self, metadata):
rv = SSLCAXMLSpec.get_spec(self, metadata)
rv['subjectaltname'] = [e.text for e in self.Match(metadata)
- if e.tag == "SubjectAltName"]
+ if e.tag == "subjectAltName"]
return rv
diff --git a/src/lib/Bcfg2/Server/Plugins/ServiceCompat.py b/src/lib/Bcfg2/Server/Plugins/ServiceCompat.py
index 0aea439f9..c3a2221f6 100644
--- a/src/lib/Bcfg2/Server/Plugins/ServiceCompat.py
+++ b/src/lib/Bcfg2/Server/Plugins/ServiceCompat.py
@@ -14,7 +14,7 @@ class ServiceCompat(Bcfg2.Server.Plugin.Plugin,
def validate_goals(self, metadata, config):
""" Apply defaults """
- if metadata.version_info and metadata.version_info > (1, 3, 0, '', 0):
+ if metadata.version_info and metadata.version_info >= (1, 3, 0, '', 0):
# do not care about a client that is _any_ 1.3.0 release
# (including prereleases and RCs)
return
diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
index ad3eb65bc..e834759c2 100644
--- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
+++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
@@ -81,7 +81,7 @@ class TemplateHelper(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.DirectoryBacked):
""" A plugin to provide helper classes and functions to templates """
__author__ = 'chris.a.st.pierre@gmail.com'
- ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\.py[co])$")
+ ignore = re.compile(r'^(\.#.*|.*~|\..*\.(sw[px])|.*\.py[co])$')
patterns = MODULE_RE
__child__ = HelperModule
diff --git a/src/lib/Bcfg2/Server/Plugins/__init__.py b/src/lib/Bcfg2/Server/Plugins/__init__.py
index 1f85702f0..ad51cf368 100644
--- a/src/lib/Bcfg2/Server/Plugins/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/__init__.py
@@ -1 +1,5 @@
-""" Bcfg2 Plugins """
+"""Imports for Bcfg2.Server.Plugins."""
+
+from Bcfg2.Compat import walk_packages
+
+__all__ = [m[1] for m in walk_packages(path=__path__)]