summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-03-24 04:51:44 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-03-24 04:51:44 -0400
commit6987584150cb9bcc8b178f1e714339f74669345b (patch)
treeaadc07bf59671eceda565517a8b737e6305ce2ef /src
parentc9ba92ef62376bbf01da1eac96cd5a2b5eb65a66 (diff)
parent4d6c2efbe2df7b516ef0dfcdaf2614adaa3fb53e (diff)
downloadbcfg2-6987584150cb9bcc8b178f1e714339f74669345b.tar.gz
bcfg2-6987584150cb9bcc8b178f1e714339f74669345b.tar.bz2
bcfg2-6987584150cb9bcc8b178f1e714339f74669345b.zip
Merge branch 'maint'
Diffstat (limited to 'src')
-rw-r--r--src/lib/Bcfg2/Client/Client.py1
-rw-r--r--src/lib/Bcfg2/Client/Tools/APT.py9
-rw-r--r--src/lib/Bcfg2/Client/Tools/Portage.py3
-rw-r--r--src/lib/Bcfg2/Client/Tools/RcUpdate.py12
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py12
-rw-r--r--src/lib/Bcfg2/Logger.py15
-rw-r--r--src/lib/Bcfg2/Reporting/Transport/LocalFilesystem.py6
-rw-r--r--src/lib/Bcfg2/Server/Core.py7
-rw-r--r--src/lib/Bcfg2/Server/Plugin/helpers.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py115
-rw-r--r--src/lib/Bcfg2/Utils.py14
-rwxr-xr-xsrc/sbin/bcfg2-info40
12 files changed, 179 insertions, 56 deletions
diff --git a/src/lib/Bcfg2/Client/Client.py b/src/lib/Bcfg2/Client/Client.py
index 88f3bd6ef..2df0b11cd 100644
--- a/src/lib/Bcfg2/Client/Client.py
+++ b/src/lib/Bcfg2/Client/Client.py
@@ -105,6 +105,7 @@ class Client(object):
self._probe_failure(name, "Return value %s" % rv)
self.logger.info("Probe %s has result:" % name)
self.logger.info(rv.stdout)
+ ret.text = rv.stdout
finally:
os.unlink(scriptname)
except SystemExit:
diff --git a/src/lib/Bcfg2/Client/Tools/APT.py b/src/lib/Bcfg2/Client/Tools/APT.py
index 0cdefa613..39816403a 100644
--- a/src/lib/Bcfg2/Client/Tools/APT.py
+++ b/src/lib/Bcfg2/Client/Tools/APT.py
@@ -228,8 +228,13 @@ class APT(Bcfg2.Client.Tools.Tool):
continue
if pkg.get('version') in ['auto', 'any']:
if self._newapi:
- ipkgs.append("%s=%s" % (pkg.get('name'),
- self.pkg_cache[pkg.get('name')].candidate.version))
+ try:
+ ipkgs.append("%s=%s" % (pkg.get('name'),
+ self.pkg_cache[pkg.get('name')].candidate.version))
+ except AttributeError:
+ self.logger.error("Failed to find %s in apt package cache" %
+ pkg.get('name'))
+ continue
else:
ipkgs.append("%s=%s" % (pkg.get('name'),
self.pkg_cache[pkg.get('name')].candidateVersion))
diff --git a/src/lib/Bcfg2/Client/Tools/Portage.py b/src/lib/Bcfg2/Client/Tools/Portage.py
index 6cbcff2e0..6b38d7dec 100644
--- a/src/lib/Bcfg2/Client/Tools/Portage.py
+++ b/src/lib/Bcfg2/Client/Tools/Portage.py
@@ -37,7 +37,8 @@ class Portage(Bcfg2.Client.Tools.PkgTool):
return
self.logger.info('Getting list of installed packages')
self.installed = {}
- for pkg in self.cmd.run("equery -q list '*'").stdout.splitlines():
+ for pkg in self.cmd.run(["equery", "-q",
+ "list", "*"]).stdout.splitlines():
if self._pkg_pattern.match(pkg):
name = self._pkg_pattern.match(pkg).group(1)
version = self._pkg_pattern.match(pkg).group(2)
diff --git a/src/lib/Bcfg2/Client/Tools/RcUpdate.py b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
index 2e58f2564..552b27842 100644
--- a/src/lib/Bcfg2/Client/Tools/RcUpdate.py
+++ b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
@@ -22,8 +22,8 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
return True
# check if service is enabled
- cmd = '/sbin/rc-update show default | grep %s'
- is_enabled = self.cmd.run(cmd % entry.get('name')).success
+ result = self.cmd.run(["/sbin/rc-update", "show", "default"])
+ is_enabled = entry.get("name") in result.stdout
# check if init script exists
try:
@@ -34,8 +34,8 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
return False
# check if service is enabled
- cmd = '/etc/init.d/%s status | grep started'
- is_running = self.cmd.run(cmd % entry.attrib['name']).success
+ result = self.cmd.run(self.get_svc_command(entry, "status"))
+ is_running = "started" in result.stdout
if entry.get('status') == 'on' and not (is_enabled and is_running):
entry.set('current_status', 'off')
@@ -70,9 +70,9 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
def FindExtra(self):
"""Locate extra rc-update services."""
- cmd = '/bin/rc-status -s'
allsrv = [line.split()[0]
- for line in self.cmd.run(cmd).stdout.splitlines()
+ for line in self.cmd.run(['/bin/rc-status',
+ '-s']).stdout.splitlines()
if 'started' in line]
self.logger.debug('Found active services:')
self.logger.debug(allsrv)
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index 27ca92472..c5a5ee4d6 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -524,8 +524,8 @@ class SvcTool(Tool):
:param service: The service entry to modify
:type service: lxml.etree._Element
- :returns: tuple - The return value from
- :class:`Bcfg2.Client.Tools.Executor.run`
+ :returns: Bcfg2.Utils.ExecutorResult - The return value from
+ :class:`Bcfg2.Utils.Executor.run`
"""
self.logger.debug('Starting service %s' % service.get('name'))
return self.cmd.run(self.get_svc_command(service, 'start'))
@@ -535,8 +535,8 @@ class SvcTool(Tool):
:param service: The service entry to modify
:type service: lxml.etree._Element
- :returns: tuple - The return value from
- :class:`Bcfg2.Client.Tools.Executor.run`
+ :returns: Bcfg2.Utils.ExecutorResult - The return value from
+ :class:`Bcfg2.Utils.Executor.run`
"""
self.logger.debug('Stopping service %s' % service.get('name'))
return self.cmd.run(self.get_svc_command(service, 'stop'))
@@ -546,8 +546,8 @@ class SvcTool(Tool):
:param service: The service entry to modify
:type service: lxml.etree._Element
- :returns: tuple - The return value from
- :class:`Bcfg2.Client.Tools.Executor.run`
+ :returns: Bcfg2.Utils.ExecutorResult - The return value from
+ :class:`Bcfg2.Utils.Executor.run`
"""
self.logger.debug('Restarting service %s' % service.get('name'))
restart_target = service.get('target', 'restart')
diff --git a/src/lib/Bcfg2/Logger.py b/src/lib/Bcfg2/Logger.py
index 8f7bb14f8..5bbc9ff96 100644
--- a/src/lib/Bcfg2/Logger.py
+++ b/src/lib/Bcfg2/Logger.py
@@ -143,7 +143,10 @@ def add_console_handler(level=logging.DEBUG):
console.setLevel(level)
# tell the handler to use this format
console.setFormatter(TermiosFormatter())
- console.set_name("console")
+ try:
+ console.set_name("console")
+ except AttributeError:
+ console.name = "console"
logging.root.addHandler(console)
@@ -158,7 +161,10 @@ def add_syslog_handler(procname, syslog_facility, level=logging.DEBUG):
syslog = FragmentingSysLogHandler(procname,
('localhost', 514),
syslog_facility)
- syslog.set_name("syslog")
+ try:
+ syslog.set_name("syslog")
+ except AttributeError:
+ syslog.name = "syslog"
syslog.setLevel(level)
syslog.setFormatter(
logging.Formatter('%(name)s[%(process)d]: %(message)s'))
@@ -172,7 +178,10 @@ def add_syslog_handler(procname, syslog_facility, level=logging.DEBUG):
def add_file_handler(to_file, level=logging.DEBUG):
"""Add a logging handler that logs to to_file."""
filelog = logging.FileHandler(to_file)
- filelog.set_name("file")
+ try:
+ filelog.set_name("file")
+ except AttributeError:
+ filelog.name = "file"
filelog.setLevel(level)
filelog.setFormatter(
logging.Formatter('%(asctime)s %(name)s[%(process)d]: %(message)s'))
diff --git a/src/lib/Bcfg2/Reporting/Transport/LocalFilesystem.py b/src/lib/Bcfg2/Reporting/Transport/LocalFilesystem.py
index 30ea39263..0a0f032e5 100644
--- a/src/lib/Bcfg2/Reporting/Transport/LocalFilesystem.py
+++ b/src/lib/Bcfg2/Reporting/Transport/LocalFilesystem.py
@@ -36,7 +36,8 @@ class LocalFilesystem(TransportBase):
def set_debug(self, debug):
rv = TransportBase.set_debug(self, debug)
- self.fmon.set_debug(debug)
+ if self.fmon is not None:
+ self.fmon.set_debug(debug)
return rv
def start_monitor(self, collector):
@@ -48,7 +49,8 @@ class LocalFilesystem(TransportBase):
self.logger.error("File monitor driver %s not available; "
"forcing to default" % setup['filemonitor'])
fmon = Bcfg2.Server.FileMonitor.available['default']
-
+ if self.debug_flag:
+ self.fmon.set_debug(self.debug_flag)
try:
self.fmon = fmon(debug=self.debug_flag)
self.logger.info("Using the %s file monitor" %
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 06dfe7f25..382f11e50 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -1214,7 +1214,7 @@ class BaseCore(object):
for plugin in self.plugins.values():
plugin.set_debug(debug)
rv = self.set_core_debug(address, debug)
- return self.set_fam_debug(address, debug) and rv
+ return self.fam.set_debug(debug) and rv
@exposed
def set_core_debug(self, _, debug):
@@ -1232,10 +1232,9 @@ class BaseCore(object):
self.logger.info("Core: debug = %s" % debug)
levels = self._loglevels[self.debug_flag]
for handler in logging.root.handlers:
- level = levels.get(handler.get_name(), levels['default'])
+ level = levels.get(handler.name, levels['default'])
self.logger.debug("Setting %s log handler to %s" %
- (handler.get_name(),
- logging.getLevelName(level)))
+ (handler.name, logging.getLevelName(level)))
handler.setLevel(level)
return self.debug_flag
diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py
index c2252f956..0b81077a3 100644
--- a/src/lib/Bcfg2/Server/Plugin/helpers.py
+++ b/src/lib/Bcfg2/Server/Plugin/helpers.py
@@ -543,6 +543,7 @@ class XMLFileBacked(FileBacked):
def Index(self):
self.xdata = lxml.etree.XML(self.data, base_url=self.name,
parser=Bcfg2.Server.XMLParser)
+ self.extras = []
self._follow_xincludes()
if self.extras:
try:
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index a81139b5d..8fb3a0998 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -264,31 +264,63 @@ class ClientMetadata(object):
# pylint: disable=R0913
def __init__(self, client, profile, groups, bundles, aliases, addresses,
categories, uuid, password, version, query):
+ #: The client hostname (as a string)
self.hostname = client
+
+ #: The client profile (as a string)
self.profile = profile
+
+ #: The set of all bundles this client gets
self.bundles = bundles
+
+ #: A list of all client aliases
self.aliases = aliases
+
+ #: A list of all addresses this client is known by
self.addresses = addresses
+
+ #: A list of groups this client is a member of
self.groups = groups
+
+ #: A dict of categories of this client's groups. Keys are
+ #: category names, values are corresponding group names.
self.categories = categories
+
+ #: The UUID identifier for this client
self.uuid = uuid
+
+ #: The Bcfg2 password for this client
self.password = password
+
+ #: Connector plugins known to this client
self.connectors = []
+
+ #: The version of the Bcfg2 client this client is running, as
+ #: a string
self.version = version
try:
+ #: The version of the Bcfg2 client this client is running,
+ #: as a :class:`Bcfg2.version.Bcfg2VersionInfo` object.
self.version_info = Bcfg2VersionInfo(version)
except (ValueError, AttributeError):
self.version_info = None
+
+ #: A :class:`Bcfg2.Server.Plugins.Metadata.MetadataQuery`
+ #: object for this client.
self.query = query
# pylint: enable=R0913
def inGroup(self, group):
- """Test to see if client is a member of group."""
+ """Test to see if client is a member of group.
+
+ :returns: bool """
return group in self.groups
def group_in_category(self, category):
- """ return the group in the given category that the client is
- a member of, or the empty string """
+ """ Return the group in the given category that the client is
+ a member of, or an empty string.
+
+ :returns: string """
for grp in self.query.all_groups_in_category(category):
if grp in self.groups:
return grp
@@ -296,17 +328,59 @@ class ClientMetadata(object):
class MetadataQuery(object):
- """ object supplied to client metadata to allow client metadata
- objects to query metadata without being able to modify it """
+ """ This class provides query methods for the metadata of all
+ clients known to the Bcfg2 server, without being able to modify
+ that data.
+
+ Note that ``*by_groups()`` and ``*by_profiles()`` behave
+ differently; for a client to be included in the return value of a
+ ``*by_groups()`` method, it must be a member of *all* groups
+ listed in the argument; for a client to be included in the return
+ value of a ``*by_profiles()`` method, it must have *any* group
+ listed as its profile group. """
def __init__(self, by_name, get_clients, by_groups, by_profiles,
all_groups, all_groups_in_category):
- # resolver is set later
+ #: Get :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata`
+ #: object for the given hostname.
+ #:
+ #: :returns: Bcfg2.Server.Plugins.Metadata.ClientMetadata
self.by_name = by_name
+
+ #: Get a list of hostnames of clients that are in all given
+ #: groups.
+ #:
+ #: :param groups: The groups to check clients for membership in
+ #: :type groups: list
+ #:
+ #: :returns: list of strings
self.names_by_groups = self._warn_string(by_groups)
+
+ #: Get a list of hostnames of clients whose profile matches
+ #: any given profile group.
+ #:
+ #: :param profiles: The profiles to check clients for
+ #: membership in.
+ #: :type profiles: list
+ #: :returns: list of strings
self.names_by_profiles = self._warn_string(by_profiles)
+
+ #: Get all known client hostnames.
+ #:
+ #: :returns: list of strings
self.all_clients = get_clients
+
+ #: Get all known group names.
+ #:
+ #: :returns: list of strings
self.all_groups = all_groups
+
+ #: Get the names of all groups in the given category.
+ #:
+ #: :param category: The category to query for groups that
+ #: belong to it.
+ #: :type category: string
+ #: :returns: list of strings
self.all_groups_in_category = all_groups_in_category
def _warn_string(self, func):
@@ -327,22 +401,41 @@ class MetadataQuery(object):
return inner
def by_groups(self, groups):
- """ get a list of ClientMetadata objects that are in all given
- groups """
+ """ Get a list of
+ :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata` objects
+ that are in all given groups.
+
+ :param groups: The groups to check clients for membership in.
+ :type groups: list
+ :returns: list of Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ objects
+ """
# don't need to decorate this with _warn_string because
# names_by_groups is decorated
return [self.by_name(name) for name in self.names_by_groups(groups)]
def by_profiles(self, profiles):
- """ get a list of ClientMetadata objects that are in any of
- the given profiles """
+ """ Get a list of
+ :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata` objects
+ that have any of the given groups as their profile.
+
+ :param profiles: The profiles to check clients for membership
+ in.
+ :type profiles: list
+ :returns: list of Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ objects
+ """
# don't need to decorate this with _warn_string because
# names_by_profiles is decorated
return [self.by_name(name)
for name in self.names_by_profiles(profiles)]
def all(self):
- """ get a list of all ClientMetadata objects """
+ """ Get a list of all
+ :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata` objects.
+
+ :returns: list of Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ """
return [self.by_name(name) for name in self.all_clients()]
diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py
index 7d7d26d5d..33da8bd71 100644
--- a/src/lib/Bcfg2/Utils.py
+++ b/src/lib/Bcfg2/Utils.py
@@ -197,14 +197,20 @@ class Executor(object):
:type timeout: float
:returns: :class:`Bcfg2.Utils.ExecutorResult`
"""
- if isinstance(command, str):
+ if isinstance(command, basestring):
cmdstr = command
else:
cmdstr = " ".join(command)
self.logger.debug("Running: %s" % cmdstr)
- proc = subprocess.Popen(command, shell=shell, bufsize=16384,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, close_fds=True)
+ try:
+ proc = subprocess.Popen(command, shell=shell, bufsize=16384,
+ close_fds=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except OSError:
+ return ExecutorResult('', 'No such command: %s' % cmdstr,
+ 127)
if timeout is None:
timeout = self.timeout
if timeout is not None:
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 311784606..cfcc95be2 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -641,21 +641,24 @@ Bcfg2 client itself.""")
if 'Packages' not in self.plugins:
print("Packages plugin not enabled")
return
+ self.plugins['Packages'].toggle_debug()
+
+ indep = lxml.etree.Element("Independent")
+ structures = [lxml.etree.Element("Bundle", name="packages")]
+ for arg in arglist[1:]:
+ lxml.etree.SubElement(structures[0], "Package", name=arg)
+
hostname = arglist[0]
- initial = arglist[1:]
metadata = self.build_metadata(hostname)
- self.plugins['Packages'].toggle_debug()
- collection = self.plugins['Packages'].get_collection(metadata)
- packages, unknown = collection.complete(initial)
- newpkgs = list(packages.difference(initial))
- print("%d initial packages" % len(initial))
- print(" %s" % "\n ".join(initial))
- print("%d new packages added" % len(newpkgs))
- if newpkgs:
- print(" %s" % "\n ".join(newpkgs))
- print("%d unknown packages" % len(unknown))
- if unknown:
- print(" %s" % "\n ".join(unknown))
+
+ # pylint: disable=W0212
+ self.plugins['Packages']._build_packages(metadata, indep, structures)
+ # pylint: enable=W0212
+
+ print("%d new packages added" % len(indep.getchildren()))
+ if len(indep.getchildren()):
+ print(" %s" % "\n ".join(lxml.etree.tostring(p)
+ for p in indep.getchildren()))
def do_packagesources(self, args):
""" packagesources <hostname> - Show package sources """
@@ -733,10 +736,13 @@ def build_usage():
usage = dict()
for attrname in dir(InfoCore):
attr = getattr(InfoCore, attrname)
- if (hasattr(attr, "__func__") and
- attr.__func__.func_name not in cmd_blacklist and
- attr.__func__.func_name.startswith("do_") and
- attr.__func__.func_doc):
+
+ # shim for python 2.4, __func__ is im_func
+ funcattr = getattr(attr, "__func__", getattr(attr, "im_func", None))
+ if (funcattr != 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()))