summaryrefslogtreecommitdiffstats
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
parentc9ba92ef62376bbf01da1eac96cd5a2b5eb65a66 (diff)
parent4d6c2efbe2df7b516ef0dfcdaf2614adaa3fb53e (diff)
downloadbcfg2-6987584150cb9bcc8b178f1e714339f74669345b.tar.gz
bcfg2-6987584150cb9bcc8b178f1e714339f74669345b.tar.bz2
bcfg2-6987584150cb9bcc8b178f1e714339f74669345b.zip
Merge branch 'maint'
-rw-r--r--.travis.yml1
-rw-r--r--doc/reports/dynamic.txt22
-rw-r--r--doc/server/plugins/grouping/metadata.txt71
-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
-rw-r--r--testsuite/Testsrc/test_code_checks.py6
16 files changed, 196 insertions, 139 deletions
diff --git a/.travis.yml b/.travis.yml
index d0476c3c4..f5aade735 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,6 @@ python:
- "2.6"
- "2.7"
- "3.2"
- - "3.3"
env:
- WITH_OPTIONAL_DEPS=yes
- WITH_OPTIONAL_DEPS=no
diff --git a/doc/reports/dynamic.txt b/doc/reports/dynamic.txt
index 14eff6f54..b3028e9e1 100644
--- a/doc/reports/dynamic.txt
+++ b/doc/reports/dynamic.txt
@@ -56,7 +56,7 @@ Install
Be sure to include the specified fields included in the example
``bcfg2.conf`` file. These can be specified in either ``/etc/bcfg2.conf``,
if it is readable by the webserver user, or ``/etc/bcfg2-web.conf``. Any
-database supported by `Django <http://www.djangoproject.com>`_ can be used.
+database supported by `Django <http://www.djangoproject.com>`_ can be used.
As of version 1.3, `South <http://south.aeracode.org>`_ is used to control
schema changes. If your database is not supported by South, any updates
will need to be applied manually. Sqlite is configured by default.
@@ -78,11 +78,11 @@ databases.
After configuring your database be sure to run `bcfg2-admin reports init`
to create the schema.
-To enable statistics collection in the bcfg2-server, add
+To enable statistics collection in the bcfg2-server, add
:ref:`server-plugins-statistics-reporting` to the **plugins**
line in your ``bcfg2.conf`` and restart the bcfg2-server. A report collecting
-daemon should be run to import the collected statistics into the backend.
-Please see the section :ref:`Report Collector <report_collector>` for more
+daemon should be run to import the collected statistics into the backend.
+Please see the section :ref:`Report Collector <report_collector>` for more
information.
Detailed installation instructions can be found :ref:`here
@@ -155,7 +155,7 @@ http://localhost/bcfg2 and see the new reports.
Upgrading
============
-1. Convert database config
+1. Convert database config
Run `tools/upgrade/1.3/migrate_configs.py`
@@ -186,14 +186,14 @@ Upgrading
Configuring
===========
-Most of the configuration is handled through the ``/etc/bcfg2.conf`` or alternatively
-``/etc/bcfg2-web.conf``.
+Most of the configuration is handled through the ``/etc/bcfg2.conf``
+or alternatively ``/etc/bcfg2-web.conf``.
An example using the defaults is listed below::
[database]
engine = sqlite3
- name = '/var/lib/bcfg2/etc/bcfg2.sqlite'
+ name = /var/lib/bcfg2/etc/bcfg2.sqlite
user =
password =
host =
@@ -262,7 +262,7 @@ reporting
Statistics Transports
---------------------
-A transport is required to pass the data collected from the bcfg2-server
+A transport is required to pass the data collected from the bcfg2-server
to the bcfg2-report-collector. At the time of this writing two transports
are available:
@@ -273,7 +273,7 @@ are available:
Future transports will allow multiple servers to pass data to a single or multiple
bcfg2-report-collector processes. New installations default to and should use the
-LocalFilesystem transport. Upgrades will use DirectStore by default in the 1.3
+LocalFilesystem transport. Upgrades will use DirectStore by default in the 1.3
release.
.. Note::
@@ -295,7 +295,7 @@ An example configuration with the default values::
redis_port = 6379
redis_db = 0
-bcfg2-admin commands operate slightly differently in this mode. Instead of querying the
+bcfg2-admin commands operate slightly differently in this mode. Instead of querying the
database directly, rpc commands are issued to the report collectors. This only affects
the minestruct and pull commands.
diff --git a/doc/server/plugins/grouping/metadata.txt b/doc/server/plugins/grouping/metadata.txt
index f4c5cbcb3..fe0d2683e 100644
--- a/doc/server/plugins/grouping/metadata.txt
+++ b/doc/server/plugins/grouping/metadata.txt
@@ -233,6 +233,8 @@ the href is supposed to be a URL.)
For instance:
+.. code-block:: xml
+
<Groups xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="groups/*.xml"/>
</Groups>
@@ -270,74 +272,9 @@ A special client metadata class is available to
:ref:`server-plugins-generators-cfg-genshi` and
:ref:`server-plugins-generators-cfg-cheetah`.
-+------------+------------------------------------------------+---------------+
-| Attribute | Description | Value |
-+============+================================================+===============+
-| hostname | Client hostname | String |
-+------------+------------------------------------------------+---------------+
-| profile | Client profile | String |
-+------------+------------------------------------------------+---------------+
-| aliases | Client aliases | List |
-+------------+------------------------------------------------+---------------+
-| addresses | Adresses this client is known by | List |
-+------------+------------------------------------------------+---------------+
-| groups | Groups this client is a member of | List |
-+------------+------------------------------------------------+---------------+
-| categories | Categories of this clients groups | List |
-+------------+------------------------------------------------+---------------+
-| uuid | uuid identifier for this client | String |
-+------------+------------------------------------------------+---------------+
-| password | bcfg password for this client | String |
-+------------+------------------------------------------------+---------------+
-| connectors | connector plugins known to this client | List |
-+------------+------------------------------------------------+---------------+
-| query | `MetadataQuery`_ object | MetadataQuery |
-+------------+------------------------------------------------+---------------+
-
-
-+-----------------------------+------------------------------------------------+-------------------+
-| Method | Description | Value |
-+=============================+================================================+===================+
-| inGroup(group) | True if this client is a memnber of 'group' | Boolean |
-+-----------------------------+------------------------------------------------+-------------------+
-| group_in_category(category) | Returns the group in 'category' if the client | String |
-| | is a member of 'category', otherwise '' | |
-+-----------------------------+------------------------------------------------+-------------------+
+.. autoclass:: Bcfg2.Server.Plugins.Metadata.ClientMetadata
MetadataQuery
-------------
-This class provides query methods for the metadata of all clients
-known to the Bcfg2 server. 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.
-
-+------------------------------+------------------------------------------------+-------------------+
-| Method | Description | Value |
-+==============================+================================================+===================+
-| by_name(client) | Get ClientMetadata object for 'client' | ClientMetadata |
-+------------------------------+------------------------------------------------+-------------------+
-| by_groups(groups) | Get ClientMetadata object for clients in all | List of |
-| | listed groups | ClientMetadata |
-+------------------------------+------------------------------------------------+-------------------+
-| by_profiles(client) | Get ClientMetadata objects for clients whose | List of |
-| | profile matches any listed profile group | ClientMetadata |
-+------------------------------+------------------------------------------------+-------------------+
-| names_by_groups(groups) | Get the names of all clients in all listed | List of strings |
-| | groups | |
-+------------------------------+------------------------------------------------+-------------------+
-| names_by_profiles(profiles) | Get the names of clients whose profile matches | List of strings |
-| | any listed profile group | |
-+------------------------------+------------------------------------------------+-------------------+
-| all_clients() | All known client hostnames | List of strings |
-+------------------------------+------------------------------------------------+-------------------+
-| all_groups() | All known group names | List of strings |
-+------------------------------+------------------------------------------------+-------------------+
-| all_groups_in_category(cat) | The names of all groups in category 'cat' | List of strings |
-+------------------------------+------------------------------------------------+-------------------+
-| all() | Get ClientMetadata for all clients | List of |
-| | | ClientMetadata |
-+------------------------------+------------------------------------------------+-------------------+
+.. autoclass:: Bcfg2.Server.Plugins.Metadata.MetadataQuery
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()))
diff --git a/testsuite/Testsrc/test_code_checks.py b/testsuite/Testsrc/test_code_checks.py
index 85aea29d6..a38710fd4 100644
--- a/testsuite/Testsrc/test_code_checks.py
+++ b/testsuite/Testsrc/test_code_checks.py
@@ -160,10 +160,8 @@ class CodeTestCase(Bcfg2TestCase):
def has_exec(self):
if self.has_command is None:
try:
- proc = Popen(self.command,
- stdin=PIPE, stdout=PIPE, stderr=STDOUT)
- proc.communicate(input="\n")
- proc.wait()
+ Popen(self.command,
+ stdin=PIPE, stdout=PIPE, stderr=STDOUT).wait()
self.has_command = True
except OSError:
self.has_command = False