From 1108cbcc29017bf54a2a2ca844720ce56ba8e545 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 24 Apr 2013 15:50:47 -0400 Subject: Reporting: better error messages when transport fails to load --- src/lib/Bcfg2/Reporting/Transport/__init__.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Reporting/Transport/__init__.py b/src/lib/Bcfg2/Reporting/Transport/__init__.py index 5c51dad1e..687b86c5a 100644 --- a/src/lib/Bcfg2/Reporting/Transport/__init__.py +++ b/src/lib/Bcfg2/Reporting/Transport/__init__.py @@ -2,10 +2,9 @@ Public transport routines """ -import traceback +import sys +from Bcfg2.Reporting.Transport.base import TransportImportError -from Bcfg2.Reporting.Transport.base import TransportError, \ - TransportImportError def load_transport(transport_name, setup): """ @@ -18,13 +17,14 @@ def load_transport(transport_name, setup): try: mod = __import__(transport_name) except: - raise TransportImportError("Unavailable") + raise TransportImportError("Error importing transport %s: %s" % + (transport_name, sys.exc_info()[1])) try: - cls = getattr(mod, transport_name) - return cls(setup) + return getattr(mod, transport_name)(setup) except: - raise TransportImportError("Transport unavailable: %s" % - traceback.format_exc().splitlines()[-1]) + raise TransportImportError("Error instantiating transport %s: %s" % + (transport_name, sys.exc_info()[1])) + def load_transport_from_config(setup): """Load the transport in the config... eventually""" @@ -32,4 +32,3 @@ def load_transport_from_config(setup): return load_transport(setup['reporting_transport'], setup) except KeyError: raise TransportImportError('Transport missing in config') - -- cgit v1.2.3-1-g7c22 From d9b1ad47b2ad492ab2878c3b16588b9054604698 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Wed, 24 Apr 2013 17:38:59 -0500 Subject: Reporting: Add back TransportError import Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Reporting/Transport/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Reporting/Transport/__init__.py b/src/lib/Bcfg2/Reporting/Transport/__init__.py index 687b86c5a..73bdd0b3a 100644 --- a/src/lib/Bcfg2/Reporting/Transport/__init__.py +++ b/src/lib/Bcfg2/Reporting/Transport/__init__.py @@ -3,7 +3,8 @@ Public transport routines """ import sys -from Bcfg2.Reporting.Transport.base import TransportImportError +from Bcfg2.Reporting.Transport.base import TransportError, \ + TransportImportError def load_transport(transport_name, setup): -- cgit v1.2.3-1-g7c22 From a5f2babaf3ba298cac7f9babdf9b568f26d71a58 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Thu, 25 Apr 2013 09:04:08 -0500 Subject: Frame: Left-align entries without a type Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Client/Frame.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py index be5c37004..850e58d9d 100644 --- a/src/lib/Bcfg2/Client/Frame.py +++ b/src/lib/Bcfg2/Client/Frame.py @@ -458,8 +458,8 @@ class Frame(object): self.logger.info("%s:%s:%s" % (entry.tag, etype, entry.get('name'))) else: - self.logger.info(" %s:%s" % (entry.tag, - entry.get('name'))) + self.logger.info("%s:%s" % (entry.tag, + entry.get('name'))) self.logger.info('Total managed entries: %d' % len(list(self.states.values()))) self.logger.info('Unmanaged entries: %d' % len(self.extra)) @@ -471,8 +471,8 @@ class Frame(object): self.logger.info("%s:%s:%s" % (entry.tag, etype, entry.get('name'))) else: - self.logger.info(" %s:%s" % (entry.tag, - entry.get('name'))) + self.logger.info("%s:%s" % (entry.tag, + entry.get('name'))) if ((list(self.states.values()).count(False) == 0) and not self.extra): self.logger.info('All entries correct.') -- cgit v1.2.3-1-g7c22 From 92f321fd9ce5301fc2a0fe73f014e7aa721bf6d5 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 25 Apr 2013 11:18:24 -0400 Subject: SELinux: added MLS ranges to all entries that support them --- src/lib/Bcfg2/Client/Tools/SELinux.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/SELinux.py b/src/lib/Bcfg2/Client/Tools/SELinux.py index 19d3fa6fc..0b4aba60d 100644 --- a/src/lib/Bcfg2/Client/Tools/SELinux.py +++ b/src/lib/Bcfg2/Client/Tools/SELinux.py @@ -500,7 +500,8 @@ class SELinuxSeportHandler(SELinuxEntryHandler): def _defaultargs(self, entry): """ argument list for adding and modifying entries """ (port, proto) = entry.get("name").split("/") - return (port, proto, '', entry.get("selinuxtype")) + return (port, proto, entry.get("mlsrange", ""), + entry.get("selinuxtype")) def _deleteargs(self, entry): return tuple(entry.get("name").split("/")) @@ -573,7 +574,7 @@ class SELinuxSefcontextHandler(SELinuxEntryHandler): """ argument list for adding, modifying, and deleting entries """ return (entry.get("name"), entry.get("selinuxtype"), self.filetypeargs[entry.get("filetype", "all")], - '', '') + entry.get("mlsrange", ""), '') def primarykey(self, entry): return ":".join([entry.tag, entry.get("name"), @@ -608,7 +609,7 @@ class SELinuxSenodeHandler(SELinuxEntryHandler): def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ (addr, netmask) = entry.get("name").split("/") - return (addr, netmask, entry.get("proto"), "", + return (addr, netmask, entry.get("proto"), entry.get("mlsrange", ""), entry.get("selinuxtype")) @@ -620,7 +621,8 @@ class SELinuxSeloginHandler(SELinuxEntryHandler): def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ - return (entry.get("name"), entry.get("selinuxuser"), "") + return (entry.get("name"), entry.get("selinuxuser"), + entry.get("mlsrange", "")) class SELinuxSeuserHandler(SELinuxEntryHandler): @@ -660,15 +662,16 @@ class SELinuxSeuserHandler(SELinuxEntryHandler): # prefix. see the comment in Install() above for more # details. rv = [entry.get("name"), - entry.get("roles", "").replace(" ", ",").split(",")] + entry.get("roles", "").replace(" ", ",").split(","), + '', entry.get("mlsrange", "")] if self.needs_prefix: - rv.extend(['', '', entry.get("prefix")]) + rv.append(entry.get("prefix")) else: key = self._key(entry) if key in self.all_records: attrs = self._key2attrs(key) if attrs['prefix'] != entry.get("prefix"): - rv.extend(['', '', entry.get("prefix")]) + rv.append(entry.get("prefix")) return tuple(rv) @@ -680,7 +683,8 @@ class SELinuxSeinterfaceHandler(SELinuxEntryHandler): def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ - return (entry.get("name"), '', entry.get("selinuxtype")) + return (entry.get("name"), entry.get("mlsrange", ""), + entry.get("selinuxtype")) class SELinuxSepermissiveHandler(SELinuxEntryHandler): -- cgit v1.2.3-1-g7c22 From 3260ec92b2d9fba4b8d37559d8f6ceb49a57e4ff Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Wed, 17 Apr 2013 14:40:49 -0400 Subject: Adding option to force server to wait until all FAM events are processed --- src/lib/Bcfg2/Options.py | 5 +++++ src/lib/Bcfg2/Server/Core.py | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py index 55f799a29..401cb64b7 100644 --- a/src/lib/Bcfg2/Options.py +++ b/src/lib/Bcfg2/Options.py @@ -535,6 +535,11 @@ SERVER_FAM_IGNORE = \ 'SCCS', '.svn', '4913', '.gitignore'], cf=('server', 'ignore_files'), cook=list_split) +SERVER_FAM_BLOCK = \ + Option('FAM blocks on startup until all events are processed', + default=False, + cook=get_bool, + cf=('server', 'fam_blocking')) SERVER_LISTEN_ALL = \ Option('Listen on all interfaces', default=False, diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py index 59d67e566..b0197ed00 100644 --- a/src/lib/Bcfg2/Server/Core.py +++ b/src/lib/Bcfg2/Server/Core.py @@ -776,6 +776,11 @@ class BaseCore(object): self.shutdown() raise + if setup['fam_blocking']: + time.sleep(1) + while self.fam.pending() != 0: + time.sleep(1) + self.set_debug(None, self.debug_flag) self._block() -- cgit v1.2.3-1-g7c22 From 9c5aa1949b80c3fe96535ca82bee6f6c9b2d9248 Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Wed, 17 Apr 2013 14:49:19 -0400 Subject: minor fixes for FAM blocking --- src/lib/Bcfg2/Options.py | 1 + src/lib/Bcfg2/Server/Core.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py index 401cb64b7..c7604c5c4 100644 --- a/src/lib/Bcfg2/Options.py +++ b/src/lib/Bcfg2/Options.py @@ -1171,6 +1171,7 @@ SERVER_COMMON_OPTIONS = dict(repo=SERVER_REPOSITORY, password=SERVER_PASSWORD, filemonitor=SERVER_FILEMONITOR, ignore=SERVER_FAM_IGNORE, + fam_blocking=SERVER_FAM_BLOCK, location=SERVER_LOCATION, key=SERVER_KEY, cert=SERVER_CERT, diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py index b0197ed00..ab8cda3da 100644 --- a/src/lib/Bcfg2/Server/Core.py +++ b/src/lib/Bcfg2/Server/Core.py @@ -776,7 +776,7 @@ class BaseCore(object): self.shutdown() raise - if setup['fam_blocking']: + if self.setup['fam_blocking']: time.sleep(1) while self.fam.pending() != 0: time.sleep(1) -- cgit v1.2.3-1-g7c22 From 03ae03918aea6ed1a8f1ada1f02b7af0ad84c3ea Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 1 May 2013 08:10:29 -0400 Subject: Metadata: don't require all profile groups to be public when using metadata db --- src/lib/Bcfg2/Server/Plugins/Metadata.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py index bdf3b87fe..8aeda9f81 100644 --- a/src/lib/Bcfg2/Server/Plugins/Metadata.py +++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py @@ -945,7 +945,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, self.debug_log("Client %s set as nonexistent group %s" % (client, group)) - def set_profile(self, client, profile, addresspair): + def set_profile(self, client, profile, addresspair, require_public=True): """Set group parameter for provided client.""" self.logger.info("Asserting client %s profile to %s" % (client, profile)) @@ -957,7 +957,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, self.logger.error(msg) raise Bcfg2.Server.Plugin.MetadataConsistencyError(msg) group = self.groups[profile] - if not group.is_public: + if require_public and not group.is_public: msg = "Cannot set client %s to private group %s" % (client, profile) self.logger.error(msg) @@ -1128,7 +1128,8 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, pgroup = self.default if pgroup: - self.set_profile(client, pgroup, (None, None)) + self.set_profile(client, pgroup, (None, None), + require_public=False) profile = _add_group(pgroup) else: msg = "Cannot add new client %s; no default group set" % client -- cgit v1.2.3-1-g7c22 From 11fe02d3358aa7ff4634f924a04fc1f5274ed6d4 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 1 May 2013 09:34:56 -0400 Subject: fixed pylint test --- src/lib/Bcfg2/Server/Plugins/Metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py index 8aeda9f81..71e81c1fe 100644 --- a/src/lib/Bcfg2/Server/Plugins/Metadata.py +++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py @@ -945,7 +945,8 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, self.debug_log("Client %s set as nonexistent group %s" % (client, group)) - def set_profile(self, client, profile, addresspair, require_public=True): + def set_profile(self, client, profile, # pylint: disable=W0221 + addresspair, require_public=True): """Set group parameter for provided client.""" self.logger.info("Asserting client %s profile to %s" % (client, profile)) -- cgit v1.2.3-1-g7c22 From 8d2ea0ad333b023aed1be7e2059c4200aa80ee76 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Thu, 2 May 2013 13:05:15 +0200 Subject: SSHbase: add support for ipv6 addresses in known_hosts file --- src/lib/Bcfg2/Server/Plugins/SSHbase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py index fc07a90e9..5aa7c4d9e 100644 --- a/src/lib/Bcfg2/Server/Plugins/SSHbase.py +++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py @@ -172,7 +172,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, for name in names[cmeta.hostname]: newnames.add(name.split('.')[0]) try: - newips.add(self.get_ipcache_entry(name)[0]) + newips.update(self.get_ipcache_entry(name)[0]) except: # pylint: disable=W0702 continue names[cmeta.hostname].update(newnames) @@ -288,7 +288,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, else: # need to add entry try: - ipaddr = socket.gethostbyname(client) + ipaddr = set([addr[0] for (_, _, _, _, addr) in socket.getaddrinfo(client, None)]) self.ipcache[client] = (ipaddr, client) return (ipaddr, client) except socket.gaierror: -- cgit v1.2.3-1-g7c22 From c2660a4ddc3785e6e8e48b5e6fbf6e1602e16777 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Sat, 4 May 2013 18:57:49 -0500 Subject: Proxy: Fix HTTPConnection for >= python 3.2 Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Proxy.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Proxy.py b/src/lib/Bcfg2/Proxy.py index b2b9fcc2e..ca219af7e 100644 --- a/src/lib/Bcfg2/Proxy.py +++ b/src/lib/Bcfg2/Proxy.py @@ -24,6 +24,7 @@ from Bcfg2.Compat import httplib, xmlrpclib, urlparse, quote_plus version = sys.version_info[:2] has_py26 = version >= (2, 6) +has_py32 = version >= (3, 2) __all__ = ["ComponentProxy", "RetryMethod", @@ -173,8 +174,12 @@ class SSLHTTPConnection(httplib.HTTPConnection): """ if not has_py26: httplib.HTTPConnection.__init__(self, host, port, strict) - else: + elif not has_py32: httplib.HTTPConnection.__init__(self, host, port, strict, timeout) + else: + # the strict parameter is deprecated. + # HTTP 0.9-style “Simple Responses” are not supported anymore. + httplib.HTTPConnection.__init__(self, host, port, timeout=timeout) self.key = key self.cert = cert self.ca = ca -- cgit v1.2.3-1-g7c22 From 9a1fe832f52b68c4e61546171195cb53e4ff2585 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Sun, 5 May 2013 10:29:44 -0500 Subject: Proxy: Use ASCII quotes to make python 2 happy Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Proxy.py b/src/lib/Bcfg2/Proxy.py index ca219af7e..9246c0f87 100644 --- a/src/lib/Bcfg2/Proxy.py +++ b/src/lib/Bcfg2/Proxy.py @@ -178,7 +178,7 @@ class SSLHTTPConnection(httplib.HTTPConnection): httplib.HTTPConnection.__init__(self, host, port, strict, timeout) else: # the strict parameter is deprecated. - # HTTP 0.9-style “Simple Responses” are not supported anymore. + # HTTP 0.9-style "Simple Responses" are not supported anymore. httplib.HTTPConnection.__init__(self, host, port, timeout=timeout) self.key = key self.cert = cert -- cgit v1.2.3-1-g7c22 From f9fd18bd1089f7edd7ca6048e000c45375250eaa Mon Sep 17 00:00:00 2001 From: Michael Fenn Date: Mon, 6 May 2013 19:09:28 -0400 Subject: make chkconfig tool driver respect servicemode The bcfg2 man page states that -s disabled should stop bcfg2 from attempting to modify any services, but the Chkconfig driver (at least) does start the service during the Install phase even with -s disabled. This patch adds support to the Chkconfig driver for the servicemode config parameter. It still does chkconfig --add, which I think makes sense to happen as part of configuration, but it does not attempt to actually start the service. --- src/lib/Bcfg2/Client/Tools/Chkconfig.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py index ec7f462b3..b1f0b6fa1 100644 --- a/src/lib/Bcfg2/Client/Tools/Chkconfig.py +++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py @@ -65,16 +65,18 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool): self.cmd.run("/sbin/chkconfig --add %s" % (entry.attrib['name'])) self.logger.info("Installing Service %s" % (entry.get('name'))) rv = True - if entry.get('status') == 'off': + if entry.get('status') == 'off' or self.setup["servicemode"] == "build": rv &= self.cmd.run((rcmd + " --level 0123456") % (entry.get('name'), entry.get('status'))).success - if entry.get("current_status") == "on": + if entry.get("current_status") == "on" and \ + self.setup["servicemode"] != "disabled": rv &= self.stop_service(entry).success else: rv &= self.cmd.run(rcmd % (entry.get('name'), entry.get('status'))).success - if entry.get("current_status") == "off": + if entry.get("current_status") == "off" and \ + self.setup["servicemode"] != "disabled": rv &= self.start_service(entry).success return rv -- cgit v1.2.3-1-g7c22 From db63df1b0e839e7339197d9b13fcdcbcc2c7dc91 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 09:49:04 -0400 Subject: added remaining required Core.load_plugins() calls --- src/lib/Bcfg2/Server/Admin/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Admin/__init__.py b/src/lib/Bcfg2/Server/Admin/__init__.py index 7bba05eb3..8f12a940e 100644 --- a/src/lib/Bcfg2/Server/Admin/__init__.py +++ b/src/lib/Bcfg2/Server/Admin/__init__.py @@ -128,6 +128,7 @@ class MetadataCore(Mode): except Bcfg2.Server.Core.CoreInitError: msg = sys.exc_info()[1] self.errExit("Core load failed: %s" % msg) + self.bcore.load_plugins() self.bcore.fam.handle_event_set() self.metadata = self.bcore.metadata -- cgit v1.2.3-1-g7c22 From 14c8e50c14330ec605191b445c7e759d75e9fc8d Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 10:20:41 -0400 Subject: SSLServer: fixed typo --- src/lib/Bcfg2/SSLServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/SSLServer.py b/src/lib/Bcfg2/SSLServer.py index 990bcd512..141bd1282 100644 --- a/src/lib/Bcfg2/SSLServer.py +++ b/src/lib/Bcfg2/SSLServer.py @@ -337,7 +337,7 @@ class XMLRPCServer(SocketServer.ThreadingMixIn, SSLServer, :param register: Presence should be reported to service-location :type register: bool :param allow_none: Allow None values in XML-RPC - :type allow_non: bool + :type allow_none: bool :param encoding: Encoding to use for XML-RPC """ -- cgit v1.2.3-1-g7c22 From ef6f78b2ef85ffd6c7e2a55247ce2238c3f874f0 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 10:20:54 -0400 Subject: BuiltinCore: register server instance after plugins are loaded --- src/lib/Bcfg2/Server/BuiltinCore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/BuiltinCore.py b/src/lib/Bcfg2/Server/BuiltinCore.py index c3302f1d0..e69a92b64 100644 --- a/src/lib/Bcfg2/Server/BuiltinCore.py +++ b/src/lib/Bcfg2/Server/BuiltinCore.py @@ -117,11 +117,11 @@ class Core(BaseCore): self.logger.error("Server startup failed: %s" % err) self.context.close() return False - self.server.register_instance(self) return True def _block(self): """ Enter the blocking infinite loop. """ + self.server.register_instance(self) try: self.server.serve_forever() finally: -- cgit v1.2.3-1-g7c22 From 69f5a49af88de48a9c64f7c57abec8fc5dc41f45 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 10:46:27 -0400 Subject: fixed long lines --- src/lib/Bcfg2/Client/Tools/Chkconfig.py | 3 ++- src/lib/Bcfg2/Server/Plugins/SSHbase.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py index b1f0b6fa1..c3dcf7796 100644 --- a/src/lib/Bcfg2/Client/Tools/Chkconfig.py +++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py @@ -65,7 +65,8 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool): self.cmd.run("/sbin/chkconfig --add %s" % (entry.attrib['name'])) self.logger.info("Installing Service %s" % (entry.get('name'))) rv = True - if entry.get('status') == 'off' or self.setup["servicemode"] == "build": + if (entry.get('status') == 'off' or + self.setup["servicemode"] == "build"): rv &= self.cmd.run((rcmd + " --level 0123456") % (entry.get('name'), entry.get('status'))).success diff --git a/src/lib/Bcfg2/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py index 5aa7c4d9e..d8b3104b7 100644 --- a/src/lib/Bcfg2/Server/Plugins/SSHbase.py +++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py @@ -288,7 +288,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, else: # need to add entry try: - ipaddr = set([addr[0] for (_, _, _, _, addr) in socket.getaddrinfo(client, None)]) + ipaddr = set([info[4][0] + for info in socket.getaddrinfo(client, None)]) self.ipcache[client] = (ipaddr, client) return (ipaddr, client) except socket.gaierror: -- cgit v1.2.3-1-g7c22 From 5e2fcd850b334efce569db6b5a5199208520015f Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 12:00:54 -0400 Subject: Compat: b64encode for py3k that works on strings and bytes --- src/lib/Bcfg2/Compat.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py index 4e9239e26..2ceb7555b 100644 --- a/src/lib/Bcfg2/Compat.py +++ b/src/lib/Bcfg2/Compat.py @@ -92,7 +92,13 @@ def u_str(string, encoding=None): # base64 compat if sys.hexversion >= 0x03000000: from base64 import b64encode as _b64encode, b64decode as _b64decode - b64encode = lambda s: _b64encode(s.encode('UTF-8')).decode('UTF-8') + + def b64encode(val): + try: + return _b64encode(val) + except TypeError: + return _b64encode(val.encode('UTF-8')).decode('UTF-8') + b64decode = lambda s: _b64decode(s.encode('UTF-8')).decode('UTF-8') else: from base64 import b64encode, b64decode -- cgit v1.2.3-1-g7c22 From a430fb769d2c0c2bf880a8d572d5bb0c41435dd1 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 13:29:50 -0400 Subject: Compat: better b64encode/decode funcs for py3k --- src/lib/Bcfg2/Compat.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py index 2ceb7555b..c376b7a2d 100644 --- a/src/lib/Bcfg2/Compat.py +++ b/src/lib/Bcfg2/Compat.py @@ -89,17 +89,28 @@ def u_str(string, encoding=None): else: return unicode(string) +try: + from functools import wraps +except ImportError: + def wraps(wrapped): # pylint: disable=W0613 + """ implementation of functools.wraps() for python 2.4 """ + return lambda f: f + + # base64 compat if sys.hexversion >= 0x03000000: from base64 import b64encode as _b64encode, b64decode as _b64decode - def b64encode(val): + @wraps(_b64encode) + def b64encode(val, **kwargs): try: - return _b64encode(val) + return _b64encode(val, **kwargs) except TypeError: - return _b64encode(val.encode('UTF-8')).decode('UTF-8') + return _b64encode(val.encode('UTF-8'), **kwargs).decode('UTF-8') - b64decode = lambda s: _b64decode(s.encode('UTF-8')).decode('UTF-8') + @wraps(_b64decode) + def b64decode(val, **kwargs): + return _b64decode(val.encode('UTF-8'), **kwargs).decode('UTF-8') else: from base64 import b64encode, b64decode @@ -248,14 +259,6 @@ except ImportError: from md5 import md5 -try: - from functools import wraps -except ImportError: - def wraps(wrapped): # pylint: disable=W0613 - """ implementation of functools.wraps() for python 2.4 """ - return lambda f: f - - def oct_mode(mode): """ Convert a decimal number describing a POSIX permissions mode to a string giving the octal mode. In Python 2, this is a synonym -- cgit v1.2.3-1-g7c22 From 9d6387d66c863c8525a4521258ccda136c3d6817 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 14:17:04 -0400 Subject: POSIXUsers: strip whitespace from MemberOf tags --- src/lib/Bcfg2/Client/Tools/POSIXUsers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py index 99ed3c7d9..0250a1a42 100644 --- a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py +++ b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py @@ -154,7 +154,7 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool): if entry.get("current_exists", "true") == "true": # verify supplemental groups actual = [g[0] for g in self.user_supplementary_groups(entry)] - expected = [e.text for e in entry.findall("MemberOf")] + expected = [e.text.strip() for e in entry.findall("MemberOf")] if set(expected) != set(actual): entry.set('qtext', "\n".join([entry.get('qtext', '')] + @@ -252,7 +252,7 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool): if entry.get('uid'): cmd.extend(['-u', entry.get('uid')]) cmd.extend(['-g', entry.get('group')]) - extras = [e.text for e in entry.findall("MemberOf")] + extras = [e.text.strip() for e in entry.findall("MemberOf")] if extras: cmd.extend(['-G', ",".join(extras)]) cmd.extend(['-d', entry.get('home')]) -- cgit v1.2.3-1-g7c22 From 4cb722d650a7cc5d0f58141d309896b901d19784 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 14:28:22 -0400 Subject: POSIXUsers: allow better syntax --- src/lib/Bcfg2/Client/Tools/POSIXUsers.py | 6 ++++-- src/lib/Bcfg2/Server/Lint/RequiredAttrs.py | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py index 0250a1a42..8226392f9 100644 --- a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py +++ b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py @@ -154,7 +154,8 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool): if entry.get("current_exists", "true") == "true": # verify supplemental groups actual = [g[0] for g in self.user_supplementary_groups(entry)] - expected = [e.text.strip() for e in entry.findall("MemberOf")] + expected = [e.get("group", e.text).strip() + for e in entry.findall("MemberOf")] if set(expected) != set(actual): entry.set('qtext', "\n".join([entry.get('qtext', '')] + @@ -252,7 +253,8 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool): if entry.get('uid'): cmd.extend(['-u', entry.get('uid')]) cmd.extend(['-g', entry.get('group')]) - extras = [e.text.strip() for e in entry.findall("MemberOf")] + extras = [e.get("group", e.text).strip() + for e in entry.findall("MemberOf")] if extras: cmd.extend(['-G', ",".join(extras)]) cmd.extend(['-d', entry.get('home')]) diff --git a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py index 40ff71dbd..6e47acfc0 100644 --- a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py +++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py @@ -115,8 +115,7 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin): SEInterface={None: dict(name=None, selinuxtype=is_selinux_type)}, SEPermissive={None: dict(name=is_selinux_type)}, POSIXGroup={None: dict(name=is_username)}, - POSIXUser={None: dict(name=is_username)}, - MemberOf={None: dict(__text__=is_username)}) + POSIXUser={None: dict(name=is_username)}) def Run(self): self.check_packages() -- cgit v1.2.3-1-g7c22 From 3e9e7fe1a3602e0102a6db2291f5a7dcdfeff20b Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 7 May 2013 14:43:50 -0400 Subject: fixed unit tests --- src/lib/Bcfg2/Compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py index c376b7a2d..d034c0777 100644 --- a/src/lib/Bcfg2/Compat.py +++ b/src/lib/Bcfg2/Compat.py @@ -102,14 +102,14 @@ if sys.hexversion >= 0x03000000: from base64 import b64encode as _b64encode, b64decode as _b64decode @wraps(_b64encode) - def b64encode(val, **kwargs): + def b64encode(val, **kwargs): # pylint: disable=C0111 try: return _b64encode(val, **kwargs) except TypeError: return _b64encode(val.encode('UTF-8'), **kwargs).decode('UTF-8') @wraps(_b64decode) - def b64decode(val, **kwargs): + def b64decode(val, **kwargs): # pylint: disable=C0111 return _b64decode(val.encode('UTF-8'), **kwargs).decode('UTF-8') else: from base64 import b64encode, b64decode -- cgit v1.2.3-1-g7c22 From 690a18b5bb61516e5c11f6da3d788332373c196b Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Thu, 9 May 2013 08:28:44 -0500 Subject: Systemd: systemd is a replacement for chkconfig Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Client/Tools/Systemd.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/Systemd.py b/src/lib/Bcfg2/Client/Tools/Systemd.py index 027d91c71..20a172d3d 100644 --- a/src/lib/Bcfg2/Client/Tools/Systemd.py +++ b/src/lib/Bcfg2/Client/Tools/Systemd.py @@ -13,6 +13,8 @@ class Systemd(Bcfg2.Client.Tools.SvcTool): __handles__ = [('Service', 'systemd')] __req__ = {'Service': ['name', 'status']} + conflicts = ['Chkconfig'] + def get_svc_command(self, service, action): return "/bin/systemctl %s %s.service" % (action, service.get('name')) -- cgit v1.2.3-1-g7c22 From f4d504f24714e16e6f345c05518604b4a66eb373 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 9 May 2013 15:49:30 -0400 Subject: Portage: verify packages are the requested version, not the installed version --- src/lib/Bcfg2/Client/Tools/Portage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Tools/Portage.py b/src/lib/Bcfg2/Client/Tools/Portage.py index 17e7755a9..2d8b66ce5 100644 --- a/src/lib/Bcfg2/Client/Tools/Portage.py +++ b/src/lib/Bcfg2/Client/Tools/Portage.py @@ -74,10 +74,10 @@ class Portage(Bcfg2.Client.Tools.PkgTool): self.logger.debug('Running equery check on %s' % entry.get('name')) - for line in self.cmd.run(["/usr/bin/equery", "-N", "check", - '=%s-%s' % - (entry.get('name'), - version)]).stdout.splitlines(): + for line in self.cmd.run( + ["/usr/bin/equery", "-N", "check", + '=%s-%s' % (entry.get('name'), + entry.get('version'))]).stdout.splitlines(): if '!!!' in line and line.split()[1] not in modlist: return False -- cgit v1.2.3-1-g7c22 From 4f745cc2731f7035f02566ba8bc1a0e9ae1b1a71 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Fri, 10 May 2013 11:03:35 -0500 Subject: Probes: Fix failing nosetests Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Server/Plugins/Probes.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Plugins/Probes.py b/src/lib/Bcfg2/Server/Plugins/Probes.py index 309b96475..f8baddb4b 100644 --- a/src/lib/Bcfg2/Server/Plugins/Probes.py +++ b/src/lib/Bcfg2/Server/Plugins/Probes.py @@ -63,7 +63,7 @@ class ProbeData(str): # pylint: disable=E0012,R0924 .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.encode('utf-8')) + return str.__new__(cls, data) def __init__(self, data): # pylint: disable=W0613 str.__init__(self) @@ -222,15 +222,9 @@ class Probes(Bcfg2.Server.Plugin.Probing, lxml.etree.SubElement(top, 'Client', name=client, timestamp=str(int(probedata.timestamp))) for probe in sorted(probedata): - 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])) + lxml.etree.SubElement( + ctag, 'Probe', name=probe, + value=self.probedata[client][probe]) for group in sorted(self.cgroups[client]): lxml.etree.SubElement(ctag, "Group", name=group) try: -- cgit v1.2.3-1-g7c22 From e55b0dc3b96cdac318dcf07309d18e763ccb7229 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Fri, 10 May 2013 12:28:58 -0500 Subject: APT: Allow specification of deb-src lines (Resolves #1148) Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Server/Plugins/Packages/Apt.py | 5 +++++ src/lib/Bcfg2/Server/Plugins/Packages/Source.py | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py index bc2928fa6..a82a183d8 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py @@ -40,6 +40,11 @@ class AptCollection(Collection): else: lines.append("deb %s %s %s" % (source.url, source.version, " ".join(source.components))) + if source.debsrc: + lines.append("deb-src %s %s %s" % + (source.url, + source.version, + " ".join(source.components))) lines.append("") return "\n".join(lines) diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py index 7ba374dd3..22073493c 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py @@ -158,6 +158,10 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 #: this source self.whitelist = [item.text for item in xsource.findall('Whitelist')] + #: Whether or not to include deb-src lines in the generated APT + #: configuration + self.debsrc = xsource.get('debsrc', 'false') == 'true' + #: A dict of repository options that will be included in the #: configuration generated on the server side (if such is #: applicable; most backends do not generate any sort of -- cgit v1.2.3-1-g7c22 From 5852f61bc3b07d987f28d016dde9475eece5ba86 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Fri, 10 May 2013 15:31:29 -0500 Subject: Frame: Print the bundle name (not Element) Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Client/Frame.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py index 850e58d9d..7c998275e 100644 --- a/src/lib/Bcfg2/Client/Frame.py +++ b/src/lib/Bcfg2/Client/Frame.py @@ -418,10 +418,11 @@ class Frame(object): # prune out unspecified bundles when running with -b continue if bundle in mbundles: - self.logger.debug("Bundle %s was modified" % bundle) + self.logger.debug("Bundle %s was modified" % bundle.get('name')) func = "BundleUpdated" else: - self.logger.debug("Bundle %s was not modified" % bundle) + self.logger.debug("Bundle %s was not modified" % + bundle.get('name')) func = "BundleNotUpdated" for tool in self.tools: try: -- cgit v1.2.3-1-g7c22 From 0a109b0a4331acbf07d6b3452767b84285edb5e9 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Fri, 10 May 2013 14:16:21 -0400 Subject: fixed bundle names in debugging output --- src/lib/Bcfg2/Client/Frame.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py index 7c998275e..e7f9b401e 100644 --- a/src/lib/Bcfg2/Client/Frame.py +++ b/src/lib/Bcfg2/Client/Frame.py @@ -418,7 +418,8 @@ class Frame(object): # prune out unspecified bundles when running with -b continue if bundle in mbundles: - self.logger.debug("Bundle %s was modified" % bundle.get('name')) + self.logger.debug("Bundle %s was modified" % + bundle.get('name')) func = "BundleUpdated" else: self.logger.debug("Bundle %s was not modified" % -- cgit v1.2.3-1-g7c22 From 0887f4c929ba1354dd1fa78eccd1a7c5151a0ab7 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Fri, 10 May 2013 15:40:49 -0400 Subject: Packages: handle URLErrors (e.g., timeouts) when downloading GPG keys better --- src/lib/Bcfg2/Server/Plugins/Packages/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py index efbca28cd..d5773de97 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py @@ -9,7 +9,7 @@ import shutil import lxml.etree import Bcfg2.Logger import Bcfg2.Server.Plugin -from Bcfg2.Compat import ConfigParser, urlopen, HTTPError +from Bcfg2.Compat import ConfigParser, urlopen, HTTPError, URLError from Bcfg2.Server.Plugins.Packages.Collection import Collection, \ get_collection_class from Bcfg2.Server.Plugins.Packages.PackagesSources import PackagesSources @@ -459,7 +459,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin, try: open(localfile, 'w').write(urlopen(key).read()) keys.append(key) - except HTTPError: + except (URLError, HTTPError): err = sys.exc_info()[1] self.logger.error("Packages: Error downloading %s: %s" % (key, err)) -- cgit v1.2.3-1-g7c22 From d7280d986c8028d04009166e160d26ad3ae5c7e4 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Fri, 10 May 2013 16:50:32 -0400 Subject: Executor: split commands given as strings --- src/lib/Bcfg2/Utils.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 581445bf4..1c2dceed2 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -2,6 +2,7 @@ used by both client and server. Stuff that doesn't fit anywhere else. """ +import shlex import fcntl import logging import threading @@ -218,6 +219,7 @@ class Executor(object): """ if isinstance(command, str): cmdstr = command + command = shlex.split(cmdstr) else: cmdstr = " ".join(command) self.logger.debug("Running: %s" % cmdstr) -- cgit v1.2.3-1-g7c22 From 03c72d7544f83c3c4e92867db3b05399a2daaa7e Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Fri, 10 May 2013 15:59:01 -0500 Subject: models: Fix table name quoting for pgsql Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Reporting/models.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py index e63c180a8..2e75c1d1a 100644 --- a/src/lib/Bcfg2/Reporting/models.py +++ b/src/lib/Bcfg2/Reporting/models.py @@ -3,7 +3,7 @@ import sys from django.core.exceptions import ImproperlyConfigured try: - from django.db import models + from django.db import models, backend, connection except ImproperlyConfigured: e = sys.exc_info()[1] print("Reports: unable to import django models: %s" % e) @@ -49,6 +49,20 @@ def hash_entry(entry_dict): return hash(cPickle.dumps(dataset)) +_our_backend = None +def _quote(value): + """ + Quote a string to use as a table name or column + + Newer versions and various drivers require an argument + https://code.djangoproject.com/ticket/13630 + """ + global _our_backend + if not _our_backend: + _our_backend = backend.DatabaseOperations(connection) + return _our_backend.quote_name(value) + + class Client(models.Model): """Object representing every client we have seen stats for.""" creation = models.DateTimeField(auto_now_add=True) @@ -77,16 +91,20 @@ class InteractionManager(models.Manager): cursor = connection.cursor() cfilter = "expiration is null" - sql = 'select ri.id, x.client_id from (select client_id, MAX(timestamp) ' + \ - 'as timer from Reporting_interaction' + sql = 'select ri.id, x.client_id from ' + \ + '(select client_id, MAX(timestamp) as timer from ' + \ + _quote('Reporting_interaction') if maxdate: if not isinstance(maxdate, datetime): raise ValueError('Expected a datetime object') sql = sql + " where timestamp <= '%s' " % maxdate cfilter = "(expiration is null or expiration > '%s') and creation <= '%s'" % (maxdate, maxdate) - sql = sql + ' GROUP BY client_id) x, Reporting_interaction ri where ' + \ - 'ri.client_id = x.client_id AND ri.timestamp = x.timer' - sql = sql + " and x.client_id in (select id from Reporting_client where %s)" % cfilter + sql = sql + ' GROUP BY client_id) x, ' + \ + _quote('Reporting_interaction') + \ + ' ri where ri.client_id = x.client_id AND' + \ + ' ri.timestamp = x.timer and x.client_id in' + \ + ' (select id from %s where %s)' % \ + (_quote('Reporting_client'), cfilter) try: cursor.execute(sql) return [item[0] for item in cursor.fetchall()] -- cgit v1.2.3-1-g7c22 From f2f26ff7561c66656e41cfced999701bc84c06c5 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Fri, 10 May 2013 17:45:26 -0500 Subject: Lint: py3k fix Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Server/Lint/Validate.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py index ae7c75804..91cfe2a56 100644 --- a/src/lib/Bcfg2/Server/Lint/Validate.py +++ b/src/lib/Bcfg2/Server/Lint/Validate.py @@ -121,6 +121,9 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): cmd.extend(["--noout", "--schema", schemafile, filename]) lint = Popen(cmd, stdout=PIPE, stderr=STDOUT) output = lint.communicate()[0] + # py3k fix + if not isinstance(output, str): + output = output.decode('utf-8') if lint.wait(): self.LintError("xml-failed-to-verify", "%s fails to verify:\n%s" % (filename, output)) -- cgit v1.2.3-1-g7c22 From 073f1e6fe1b564b1f00cb802fc36c56f739082ec Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Sat, 11 May 2013 14:56:59 -0500 Subject: Lint: Fix for python 3 Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Server/Lint/MergeFiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/MergeFiles.py b/src/lib/Bcfg2/Server/Lint/MergeFiles.py index 44d02c2ff..2419c3d43 100644 --- a/src/lib/Bcfg2/Server/Lint/MergeFiles.py +++ b/src/lib/Bcfg2/Server/Lint/MergeFiles.py @@ -57,7 +57,7 @@ class MergeFiles(Bcfg2.Server.Lint.ServerPlugin): else: threshold = 0.75 rv = [] - elist = entries.items() + elist = list(entries.items()) while elist: result = self._find_similar(elist.pop(0), copy.copy(elist), threshold) -- cgit v1.2.3-1-g7c22 From 59f85e6da78d2192274b49216cbefdced7a24ab6 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Mon, 13 May 2013 09:02:44 -0500 Subject: Lint: Fix Properties Comments checker Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Server/Lint/Comments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/Comments.py b/src/lib/Bcfg2/Server/Lint/Comments.py index 8bfb76461..85c4467ba 100644 --- a/src/lib/Bcfg2/Server/Lint/Comments.py +++ b/src/lib/Bcfg2/Server/Lint/Comments.py @@ -81,7 +81,7 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin): """ check properties files for required headers """ if 'Properties' in self.core.plugins: props = self.core.plugins['Properties'] - for propfile, pdata in props.store.entries.items(): + for propfile, pdata in props.entries.items(): if os.path.splitext(propfile)[1] == ".xml": self.check_xml(pdata.name, pdata.xdata, 'properties') -- cgit v1.2.3-1-g7c22 From 7b1a1e45641bc6283d9db3b0f76a7ca6bf6fc871 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Mon, 13 May 2013 10:07:14 -0500 Subject: Client: Fix interactive prompt Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Client/Frame.py | 10 +--------- src/lib/Bcfg2/Client/__init__.py | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py index e7f9b401e..b7c3f7145 100644 --- a/src/lib/Bcfg2/Client/Frame.py +++ b/src/lib/Bcfg2/Client/Frame.py @@ -9,14 +9,6 @@ from Bcfg2.Client import prompt from Bcfg2.Compat import any, all, cmp # pylint: disable=W0622 -def cmpent(ent1, ent2): - """Sort entries.""" - if ent1.tag != ent2.tag: - return cmp(ent1.tag, ent2.tag) - else: - return cmp(ent1.get('name'), ent2.get('name')) - - def matches_entry(entryspec, entry): """ Determine if the Decisions-style entry specification matches the entry. Both are tuples of (tag, name). The entryspec can @@ -155,7 +147,7 @@ class Frame(object): def promptFilter(self, msg, entries): """Filter a supplied list based on user input.""" ret = [] - entries.sort(cmpent) + entries.sort(key=lambda e: e.tag + ":" + e.get('name')) for entry in entries[:]: if entry in self.unhandled: # don't prompt for entries that can't be installed diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index e40ef750b..3bc261f2f 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -19,7 +19,7 @@ def prompt(msg): while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0: os.read(sys.stdin.fileno(), 4096) try: - ans = input(msg.encode(sys.stdout.encoding, 'replace')) + ans = input(msg) return ans in ['y', 'Y'] except EOFError: # python 2.4.3 on CentOS doesn't like ^C for some reason -- cgit v1.2.3-1-g7c22 From a715128dbafc0dec3fa82359128ed33d8d242c1d Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Mon, 13 May 2013 10:27:20 -0500 Subject: Frame: Remove unused import Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Client/Frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py index b7c3f7145..d30708e83 100644 --- a/src/lib/Bcfg2/Client/Frame.py +++ b/src/lib/Bcfg2/Client/Frame.py @@ -6,7 +6,7 @@ import fnmatch import logging import Bcfg2.Client.Tools from Bcfg2.Client import prompt -from Bcfg2.Compat import any, all, cmp # pylint: disable=W0622 +from Bcfg2.Compat import any, all # pylint: disable=W0622 def matches_entry(entryspec, entry): -- cgit v1.2.3-1-g7c22 From 34aff9f18f7a8ee59e8e07ceaf89d79bd6e96509 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 14 May 2013 11:43:14 -0400 Subject: doc: added devel docs for bcfg2-lint plugins --- src/lib/Bcfg2/Server/Lint/Comments.py | 121 ++++++++++++++--- src/lib/Bcfg2/Server/Lint/Genshi.py | 11 +- src/lib/Bcfg2/Server/Lint/GroupNames.py | 28 +++- src/lib/Bcfg2/Server/Lint/InfoXML.py | 14 +- src/lib/Bcfg2/Server/Lint/RequiredAttrs.py | 25 ++-- src/lib/Bcfg2/Server/Lint/Validate.py | 55 ++++++-- src/lib/Bcfg2/Server/Lint/__init__.py | 181 +++++++++++++++++-------- src/lib/Bcfg2/Server/Plugins/Bundler.py | 13 +- src/lib/Bcfg2/Server/Plugins/GroupPatterns.py | 7 +- src/lib/Bcfg2/Server/Plugins/Metadata.py | 76 ++++++----- src/lib/Bcfg2/Server/Plugins/Pkgmgr.py | 16 ++- src/lib/Bcfg2/Server/Plugins/TemplateHelper.py | 19 ++- 12 files changed, 415 insertions(+), 151 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/Comments.py b/src/lib/Bcfg2/Server/Lint/Comments.py index 85c4467ba..7c3b2d9cc 100644 --- a/src/lib/Bcfg2/Server/Lint/Comments.py +++ b/src/lib/Bcfg2/Server/Lint/Comments.py @@ -1,8 +1,9 @@ -""" check files for various required comments """ +""" Check files for various required comments. """ import os import lxml.etree import Bcfg2.Server.Lint +from Bcfg2.Server import XI_NAMESPACE from Bcfg2.Server.Plugins.Cfg.CfgPlaintextGenerator \ import CfgPlaintextGenerator from Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator import CfgGenshiGenerator @@ -11,7 +12,10 @@ from Bcfg2.Server.Plugins.Cfg.CfgInfoXML import CfgInfoXML class Comments(Bcfg2.Server.Lint.ServerPlugin): - """ check files for various required headers """ + """ The Comments lint plugin checks files for header comments that + give information about the files. For instance, you can require + SVN keywords in a comment, or require the name of the maintainer + of a Genshi template, and so on. """ def __init__(self, *args, **kwargs): Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs) self.config_cache = {} @@ -27,21 +31,43 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin): def Errors(cls): return {"unexpanded-keywords": "warning", "keywords-not-found": "warning", - "comments-not-found": "warning"} + "comments-not-found": "warning", + "broken-xinclude-chain": "warning"} def required_keywords(self, rtype): - """ given a file type, fetch the list of required VCS keywords - from the bcfg2-lint config """ + """ Given a file type, fetch the list of required VCS keywords + from the bcfg2-lint config. Valid file types are documented + in :manpage:`bcfg2-lint.conf(5)`. + + :param rtype: The file type + :type rtype: string + :returns: list - the required items + """ return self.required_items(rtype, "keyword") def required_comments(self, rtype): - """ given a file type, fetch the list of required comments - from the bcfg2-lint config """ + """ Given a file type, fetch the list of required comments + from the bcfg2-lint config. Valid file types are documented + in :manpage:`bcfg2-lint.conf(5)`. + + :param rtype: The file type + :type rtype: string + :returns: list - the required items + """ return self.required_items(rtype, "comment") def required_items(self, rtype, itype): - """ given a file type and item type (comment or keyword), - fetch the list of required items from the bcfg2-lint config """ + """ Given a file type and item type (``comment`` or + ``keyword``), fetch the list of required items from the + bcfg2-lint config. Valid file types are documented in + :manpage:`bcfg2-lint.conf(5)`. + + :param rtype: The file type + :type rtype: string + :param itype: The item type (``comment`` or ``keyword``) + :type itype: string + :returns: list - the required items + """ if itype not in self.config_cache: self.config_cache[itype] = {} @@ -62,7 +88,7 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin): return self.config_cache[itype][rtype] def check_bundles(self): - """ check bundle files for required headers """ + """ Check bundle files for required comments. """ if 'Bundler' in self.core.plugins: for bundle in self.core.plugins['Bundler'].entries.values(): xdata = None @@ -78,15 +104,41 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin): self.check_xml(bundle.name, xdata, rtype) def check_properties(self): - """ check properties files for required headers """ + """ Check Properties files for required comments. """ if 'Properties' in self.core.plugins: props = self.core.plugins['Properties'] for propfile, pdata in props.entries.items(): if os.path.splitext(propfile)[1] == ".xml": self.check_xml(pdata.name, pdata.xdata, 'properties') + def has_all_xincludes(self, mfile): + """ Return True if :attr:`Bcfg2.Server.Lint.Plugin.files` + includes all XIncludes listed in the specified metadata type, + false otherwise. In other words, this returns True if + bcfg2-lint is dealing with complete metadata. + + :param mfile: The metadata file ("clients.xml" or + "groups.xml") to check for XIncludes + :type mfile: string + :returns: bool + """ + if self.files is None: + return True + else: + path = os.path.join(self.metadata.data, mfile) + if path in self.files: + xdata = lxml.etree.parse(path) + for el in xdata.findall('./%sinclude' % XI_NAMESPACE): + if not self.has_all_xincludes(el.get('href')): + self.LintError("broken-xinclude-chain", + "Broken XInclude chain: could not " + "include %s" % path) + return False + + return True + def check_metadata(self): - """ check metadata files for required headers """ + """ Check Metadata files for required comments. """ if self.has_all_xincludes("groups.xml"): self.check_xml(os.path.join(self.metadata.data, "groups.xml"), self.metadata.groups_xml.data, @@ -97,7 +149,8 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin): "metadata") def check_cfg(self): - """ check Cfg files and info.xml files for required headers """ + """ Check Cfg files and ``info.xml`` files for required + comments. """ if 'Cfg' in self.core.plugins: for entryset in self.core.plugins['Cfg'].entries.values(): for entry in entryset.entries.values(): @@ -117,29 +170,57 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin): self.check_plaintext(entry.name, entry.data, rtype) def check_probes(self): - """ check probes for required headers """ + """ Check Probes for required comments """ if 'Probes' in self.core.plugins: for probe in self.core.plugins['Probes'].probes.entries.values(): self.check_plaintext(probe.name, probe.data, "probes") def check_xml(self, filename, xdata, rtype): - """ check generic XML files for required headers """ + """ Generic check to check an XML file for required comments. + + :param filename: The filename + :type filename: string + :param xdata: The file data + :type xdata: lxml.etree._Element + :param rtype: The type of file. Available types are + documented in :manpage:`bcfg2-lint.conf(5)`. + :type rtype: string + """ self.check_lines(filename, [str(el) for el in xdata.getiterator(lxml.etree.Comment)], rtype) def check_plaintext(self, filename, data, rtype): - """ check generic plaintext files for required headers """ + """ Generic check to check a plain text file for required + comments. + + :param filename: The filename + :type filename: string + :param data: The file data + :type data: string + :param rtype: The type of file. Available types are + documented in :manpage:`bcfg2-lint.conf(5)`. + :type rtype: string + """ self.check_lines(filename, data.splitlines(), rtype) def check_lines(self, filename, lines, rtype): - """ generic header check for a set of lines """ + """ Generic header check for a set of lines. + + :param filename: The filename + :type filename: string + :param lines: The data to check + :type lines: list of strings + :param rtype: The type of file. Available types are + documented in :manpage:`bcfg2-lint.conf(5)`. + :type rtype: string + """ if self.HandlesFile(filename): # found is trivalent: - # False == not found - # None == found but not expanded - # True == found and expanded + # False == keyword not found + # None == keyword found but not expanded + # True == keyword found and expanded found = dict((k, False) for k in self.required_keywords(rtype)) for line in lines: diff --git a/src/lib/Bcfg2/Server/Lint/Genshi.py b/src/lib/Bcfg2/Server/Lint/Genshi.py index c045c2ca2..7edeb8a49 100755 --- a/src/lib/Bcfg2/Server/Lint/Genshi.py +++ b/src/lib/Bcfg2/Server/Lint/Genshi.py @@ -1,4 +1,4 @@ -""" Check Genshi templates for syntax errors """ +""" Check Genshi templates for syntax errors. """ import sys import Bcfg2.Server.Lint @@ -9,10 +9,9 @@ from Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator import CfgGenshiGenerator class Genshi(Bcfg2.Server.Lint.ServerPlugin): - """ Check Genshi templates for syntax errors """ + """ Check Genshi templates for syntax errors. """ def Run(self): - """ run plugin """ if 'Cfg' in self.core.plugins: self.check_cfg() if 'TGenshi' in self.core.plugins: @@ -25,7 +24,7 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin): return {"genshi-syntax-error": "error"} def check_cfg(self): - """ Check genshi templates in Cfg for syntax errors """ + """ Check genshi templates in Cfg for syntax errors. """ for entryset in self.core.plugins['Cfg'].entries.values(): for entry in entryset.entries.values(): if (self.HandlesFile(entry.name) and @@ -40,7 +39,7 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin): "Genshi syntax error: %s" % err) def check_tgenshi(self): - """ Check templates in TGenshi for syntax errors """ + """ Check templates in TGenshi for syntax errors. """ loader = TemplateLoader() for eset in self.core.plugins['TGenshi'].entries.values(): @@ -54,7 +53,7 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin): "Genshi syntax error: %s" % err) def check_bundler(self): - """ Check templates in Bundler for syntax errors """ + """ Check templates in Bundler for syntax errors. """ loader = TemplateLoader() for entry in self.core.plugins['Bundler'].entries.values(): diff --git a/src/lib/Bcfg2/Server/Lint/GroupNames.py b/src/lib/Bcfg2/Server/Lint/GroupNames.py index 52e42aa7b..b180083d5 100644 --- a/src/lib/Bcfg2/Server/Lint/GroupNames.py +++ b/src/lib/Bcfg2/Server/Lint/GroupNames.py @@ -1,4 +1,4 @@ -""" ensure that all named groups are valid group names """ +""" Ensure that all named groups are valid group names. """ import os import re @@ -11,8 +11,15 @@ except ImportError: class GroupNames(Bcfg2.Server.Lint.ServerPlugin): - """ ensure that all named groups are valid group names """ + """ Ensure that all named groups are valid group names. """ + + #: A string regex that matches only valid group names. Currently, + #: a group name is considered valid if it contains only + #: non-whitespace characters. pattern = r'\S+$' + + #: A compiled regex for + #: :attr:`Bcfg2.Server.Lint.GroupNames.GroupNames.pattern` valid = re.compile(r'^' + pattern) def Run(self): @@ -31,7 +38,7 @@ class GroupNames(Bcfg2.Server.Lint.ServerPlugin): return {"invalid-group-name": "error"} def check_rules(self): - """ Check groups used in the Rules plugin for validity """ + """ Check groups used in the Rules plugin for validity. """ for rules in self.core.plugins['Rules'].entries.values(): if not self.HandlesFile(rules.name): continue @@ -40,7 +47,7 @@ class GroupNames(Bcfg2.Server.Lint.ServerPlugin): os.path.join(self.config['repo'], rules.name)) def check_bundles(self): - """ Check groups used in the Bundler plugin for validity """ + """ Check groups used in the Bundler plugin for validity. """ for bundle in self.core.plugins['Bundler'].entries.values(): if (self.HandlesFile(bundle.name) and (not HAS_GENSHI or @@ -50,7 +57,7 @@ class GroupNames(Bcfg2.Server.Lint.ServerPlugin): def check_metadata(self): """ Check groups used or declared in the Metadata plugin for - validity """ + validity. """ self.check_entries(self.metadata.groups_xml.xdata.xpath("//Group"), os.path.join(self.config['repo'], self.metadata.groups_xml.name)) @@ -68,7 +75,7 @@ class GroupNames(Bcfg2.Server.Lint.ServerPlugin): def check_cfg(self): """ Check groups used in group-specific files in the Cfg - plugin for validity """ + plugin for validity. """ for root, _, files in os.walk(self.core.plugins['Cfg'].data): for fname in files: basename = os.path.basename(root) @@ -81,7 +88,14 @@ class GroupNames(Bcfg2.Server.Lint.ServerPlugin): def check_entries(self, entries, fname): """ Check a generic list of XML entries for tags with - invalid name attributes """ + invalid name attributes. + + :param entries: A list of XML tags whose ``name`` + attributes will be validated. + :type entries: list of lxml.etree._Element + :param fname: The filename the entry list came from + :type fname: string + """ for grp in entries: if not self.valid.search(grp.get("name")): self.LintError("invalid-group-name", diff --git a/src/lib/Bcfg2/Server/Lint/InfoXML.py b/src/lib/Bcfg2/Server/Lint/InfoXML.py index e34f387ff..95657317e 100644 --- a/src/lib/Bcfg2/Server/Lint/InfoXML.py +++ b/src/lib/Bcfg2/Server/Lint/InfoXML.py @@ -1,4 +1,4 @@ -""" ensure that all config files have an info.xml file""" +""" Ensure that all config files have a valid info.xml file. """ import os import Bcfg2.Options @@ -8,7 +8,14 @@ from Bcfg2.Server.Plugins.Cfg.CfgLegacyInfo import CfgLegacyInfo class InfoXML(Bcfg2.Server.Lint.ServerPlugin): - """ ensure that all config files have an info.xml file""" + """ Ensure that all config files have a valid info.xml file. This + plugin can check for: + + * Missing ``info.xml`` files; + * Use of deprecated ``info``/``:info`` files; + * Paranoid mode disabled in an ``info.xml`` file; + * Required attributes missing from ``info.xml`` + """ def Run(self): if 'Cfg' not in self.core.plugins: return @@ -40,11 +47,10 @@ class InfoXML(Bcfg2.Server.Lint.ServerPlugin): return {"no-infoxml": "warning", "deprecated-info-file": "warning", "paranoid-false": "warning", - "broken-xinclude-chain": "warning", "required-infoxml-attrs-missing": "error"} def check_infoxml(self, fname, xdata): - """ verify that info.xml contains everything it should """ + """ Verify that info.xml contains everything it should. """ for info in xdata.getroottree().findall("//Info"): required = [] if "required_attrs" in self.config: diff --git a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py index 6e47acfc0..6ffdd33a0 100644 --- a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py +++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py @@ -1,5 +1,5 @@ -""" verify attributes for configuration entries that cannot be -verified with an XML schema alone""" +""" Verify attributes for configuration entries that cannot be +verified with an XML schema alone. """ import os import re @@ -15,7 +15,8 @@ except ImportError: HAS_GENSHI = False -# format verifying functions +# format verifying functions. TODO: These should be moved into XML +# schemas where possible. def is_filename(val): """ Return True if val is a string describing a valid full path """ @@ -53,8 +54,8 @@ def is_device_mode(val): class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin): - """ verify attributes for configuration entries that cannot be - verified with an XML schema alone """ + """ Verify attributes for configuration entries that cannot be + verified with an XML schema alone. """ def __init__(self, *args, **kwargs): Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs) self.required_attrs = dict( @@ -135,7 +136,8 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin): "extra-attrs": "warning"} def check_packages(self): - """ check package sources for Source entries with missing attrs """ + """ Check Packages sources for Source entries with missing + attributes. """ if 'Packages' not in self.core.plugins: return @@ -175,7 +177,8 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin): rules.name)) def check_bundles(self): - """ check bundles for BoundPath entries with missing attrs """ + """ Check bundles for BoundPath entries with missing + attrs. """ if 'Bundler' not in self.core.plugins: return @@ -194,7 +197,13 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin): self.check_entry(path, bundle.name) def check_entry(self, entry, filename): - """ generic entry check """ + """ Generic entry check. + + :param entry: The XML entry to check for missing attributes. + :type entry: lxml.etree._Element + :param filename: The filename the entry came from + :type filename: string + """ if self.HandlesFile(filename): name = entry.get('name') tag = entry.tag diff --git a/src/lib/Bcfg2/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py index 91cfe2a56..09f3f3d25 100644 --- a/src/lib/Bcfg2/Server/Lint/Validate.py +++ b/src/lib/Bcfg2/Server/Lint/Validate.py @@ -1,4 +1,5 @@ -""" Ensure that the repo validates """ +""" Ensure that all XML files in the Bcfg2 repository validate +according to their respective schemas. """ import os import sys @@ -10,10 +11,19 @@ import Bcfg2.Server.Lint class Validate(Bcfg2.Server.Lint.ServerlessPlugin): - """ Ensure that the repo validates """ + """ Ensure that all XML files in the Bcfg2 repository validate + according to their respective schemas. """ def __init__(self, *args, **kwargs): Bcfg2.Server.Lint.ServerlessPlugin.__init__(self, *args, **kwargs) + + #: A dict of : that maps files in the + #: Bcfg2 specification to their schemas. The globs are + #: extended :mod:`fnmatch` globs that also support ``**``, + #: which matches any number of any characters, including + #: forward slashes. The schema files are relative to the + #: schema directory, which can be controlled by the + #: ``bcfg2-lint --schema`` option. self.filesets = \ {"Metadata/groups.xml": "metadata.xsd", "Metadata/clients.xml": "clients.xsd", @@ -76,7 +86,7 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): "input-output-error": "error"} def check_properties(self): - """ check Properties files against their schemas """ + """ Check Properties files against their schemas. """ for filename in self.filelists['props']: schemafile = "%s.xsd" % os.path.splitext(filename)[0] if os.path.exists(schemafile): @@ -90,7 +100,11 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): def parse(self, filename): """ Parse an XML file, raising the appropriate LintErrors if it can't be parsed or read. Return the - lxml.etree._ElementTree parsed from the file. """ + lxml.etree._ElementTree parsed from the file. + + :param filename: The full path to the file to parse + :type filename: string + :returns: lxml.etree._ElementTree - the parsed data""" try: return lxml.etree.parse(filename) except SyntaxError: @@ -106,8 +120,20 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): return False def validate(self, filename, schemafile, schema=None): - """validate a file against the given lxml.etree.Schema. - return True on success, False on failure """ + """ Validate a file against the given schema. + + :param filename: The full path to the file to validate + :type filename: string + :param schemafile: The full path to the schema file to + validate against + :type schemafile: string + :param schema: The loaded schema to validate against. This + can be used to avoid parsing a single schema + file for every file that needs to be validate + against it. + :type schema: lxml.etree.Schema + :returns: bool - True if the file validates, false otherwise + """ if schema is None: # if no schema object was provided, instantiate one schema = self._load_schema(schemafile) @@ -131,7 +157,14 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): return True def get_filelists(self): - """ get lists of different kinds of files to validate """ + """ Get lists of different kinds of files to validate. This + doesn't return anything, but it sets + :attr:`Bcfg2.Server.Lint.Validate.Validate.filelists` to a + dict whose keys are path globs given in + :attr:`Bcfg2.Server.Lint.Validate.Validate.filesets` and whose + values are lists of the full paths to all files in the Bcfg2 + repository (or given with ``bcfg2-lint --stdin``) that match + the glob.""" if self.files is not None: listfiles = lambda p: fnmatch.filter(self.files, os.path.join('*', p)) @@ -158,7 +191,13 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): self.filelists['props'] = listfiles("Properties/*.xml") def _load_schema(self, filename): - """ load an XML schema document, returning the Schema object """ + """ Load an XML schema document, returning the Schema object + and raising appropriate lint errors on failure. + + :param filename: The full path to the schema file to load. + :type filename: string + :returns: lxml.etree.Schema - The loaded schema data + """ try: return lxml.etree.XMLSchema(lxml.etree.parse(filename)) except IOError: diff --git a/src/lib/Bcfg2/Server/Lint/__init__.py b/src/lib/Bcfg2/Server/Lint/__init__.py index 11afdd75d..d06347cf6 100644 --- a/src/lib/Bcfg2/Server/Lint/__init__.py +++ b/src/lib/Bcfg2/Server/Lint/__init__.py @@ -9,10 +9,6 @@ import lxml.etree import fcntl import termios import struct -from Bcfg2.Server import XI_NAMESPACE -from Bcfg2.Compat import walk_packages - -__all__ = [m[1] for m in walk_packages(path=__path__)] def _ioctl_GWINSZ(fd): # pylint: disable=C0103 @@ -45,30 +41,56 @@ def get_termsize(): class Plugin(object): - """ base class for ServerlessPlugin and ServerPlugin """ + """ Base class for all bcfg2-lint plugins """ def __init__(self, config, errorhandler=None, files=None): + """ + :param config: A :mod:`Bcfg2.Options` setup dict + :type config: dict + :param errorhandler: A :class:`Bcfg2.Server.Lint.ErrorHandler` + that will be used to handle lint errors. + If one is not provided, a new one will be + instantiated. + :type errorhandler: Bcfg2.Server.Lint.ErrorHandler + :param files: A list of files to run bcfg2-lint against. (See + the bcfg2-lint ``--stdin`` option.) + :type files: list of strings + """ + + #: The list of files that bcfg2-lint should be run against self.files = files + + #: The Bcfg2.Options setup dict self.config = config + self.logger = logging.getLogger('bcfg2-lint') if errorhandler is None: + #: The error handler self.errorhandler = ErrorHandler() else: self.errorhandler = errorhandler self.errorhandler.RegisterErrors(self.Errors()) def Run(self): - """ run the plugin. must be overloaded by child classes """ - pass + """ Run the plugin. Must be overloaded by child classes. """ + raise NotImplementedError @classmethod def Errors(cls): - """ returns a dict of errors the plugin supplies. must be - overloaded by child classes """ + """ Returns a dict of errors the plugin supplies, in a format + suitable for passing to + :func:`Bcfg2.Server.Lint.ErrorHandler.RegisterErrors`. + + Must be overloaded by child classes. + + :returns: dict + """ + raise NotImplementedError def HandlesFile(self, fname): - """ returns true if the given file should be handled by the - plugin according to the files list, false otherwise """ + """ Returns True if the given file should be handled by the + plugin according to :attr:`Bcfg2.Server.Lint.Plugin.files`, + False otherwise. """ return (self.files is None or fname in self.files or os.path.join(self.config['repo'], fname) in self.files or @@ -77,12 +99,27 @@ class Plugin(object): fname)) in self.files) def LintError(self, err, msg): - """ record an error in the lint process """ + """ Raise an error from the lint process. + + :param err: The name of the error being raised. This name + must be a key in the dict returned by + :func:`Bcfg2.Server.Lint.Plugin.Errors`. + :type err: string + :param msg: The freeform message to display to the end user. + :type msg: string + """ self.errorhandler.dispatch(err, msg) def RenderXML(self, element, keep_text=False): - """render an XML element for error output -- line number - prefixed, no children""" + """ Render an XML element for error output. This prefixes the + line number and removes children for nicer display. + + :param element: The element to render + :type element: lxml.etree._Element + :param keep_text: Do not discard text content from the element + for display + :type keep_text: boolean + """ xml = None if len(element) or element.text: el = copy(element) @@ -100,11 +137,18 @@ class Plugin(object): return " line %s: %s" % (element.sourceline, xml) -class ErrorHandler (object): - """ a class to handle errors for bcfg2-lint plugins """ +class ErrorHandler(object): + """ A class to handle errors for bcfg2-lint plugins """ - def __init__(self, config=None): + def __init__(self, errors=None): + """ + :param config: An initial dict of errors to register + :type config: dict + """ + #: The number of errors passed to this error handler self.errors = 0 + + #: The number of warnings passed to this error handler self.warnings = 0 self.logger = logging.getLogger('bcfg2-lint') @@ -114,17 +158,25 @@ class ErrorHandler (object): twrap = textwrap.TextWrapper(initial_indent=" ", subsequent_indent=" ", width=termsize[0]) + #: A function to wrap text to the width of the terminal self._wrapper = twrap.wrap else: self._wrapper = lambda s: [s] + #: A dict of registered errors self.errortypes = dict() - if config is not None: - self.RegisterErrors(dict(config.items())) + if errors is not None: + self.RegisterErrors(dict(errors.items())) def RegisterErrors(self, errors): - """ Register a dict of errors (name: default level) that a - plugin may raise """ + """ Register a dict of errors that a plugin may raise. The + keys of the dict are short strings that describe each error; + the values are the default error handling for that error + ("error", "warning", or "silent"). + + :param errors: The error dict + :type errors: dict + """ for err, action in errors.items(): if err not in self.errortypes: if "warn" in action: @@ -135,7 +187,16 @@ class ErrorHandler (object): self.errortypes[err] = self.debug def dispatch(self, err, msg): - """ Dispatch an error to the correct handler """ + """ Dispatch an error to the correct handler. + + :param err: The name of the error being raised. This name + must be a key in + :attr:`Bcfg2.Server.Lint.ErrorHandler.errortypes`, + the dict of registered errors. + :type err: string + :param msg: The freeform message to display to the end user. + :type msg: string + """ if err in self.errortypes: self.errortypes[err](msg) self.logger.debug(" (%s)" % err) @@ -145,22 +206,34 @@ class ErrorHandler (object): self.logger.warning("Unknown error %s" % err) def error(self, msg): - """ log an error condition """ + """ Log an error condition. + + :param msg: The freeform message to display to the end user. + :type msg: string + """ self.errors += 1 self._log(msg, self.logger.error, prefix="ERROR: ") def warn(self, msg): - """ log a warning condition """ + """ Log a warning condition. + + :param msg: The freeform message to display to the end user. + :type msg: string + """ self.warnings += 1 self._log(msg, self.logger.warning, prefix="WARNING: ") def debug(self, msg): - """ log a silent/debug condition """ + """ Log a silent/debug condition. + + :param msg: The freeform message to display to the end user. + :type msg: string + """ self._log(msg, self.logger.debug) def _log(self, msg, logfunc, prefix=""): """ Generic log function that logs a message with the given - function after wrapping it for the terminal width """ + function after wrapping it for the terminal width. """ # a message may itself consist of multiple lines. wrap() will # elide them all into a single paragraph, which we don't want. # so we split the message into its paragraphs and wrap each @@ -180,37 +253,37 @@ class ErrorHandler (object): logfunc(line) -class ServerlessPlugin (Plugin): - """ base class for plugins that are run before the server starts - up (i.e., plugins that check things that may prevent the server - from starting up) """ +class ServerlessPlugin(Plugin): + """ Base class for bcfg2-lint plugins that are run before the + server starts up (i.e., plugins that check things that may prevent + the server from starting up). """ pass -class ServerPlugin (Plugin): - """ base class for plugins that check things that require the - running Bcfg2 server """ - def __init__(self, core, config, **kwargs): - Plugin.__init__(self, config, **kwargs) +class ServerPlugin(Plugin): + """ Base class for bcfg2-lint plugins that check things that + require the running Bcfg2 server. """ + + def __init__(self, core, config, errorhandler=None, files=None): + """ + :param core: The Bcfg2 server core + :type core: Bcfg2.Server.Core.BaseCore + :param config: A :mod:`Bcfg2.Options` setup dict + :type config: dict + :param errorhandler: A :class:`Bcfg2.Server.Lint.ErrorHandler` + that will be used to handle lint errors. + If one is not provided, a new one will be + instantiated. + :type errorhandler: Bcfg2.Server.Lint.ErrorHandler + :param files: A list of files to run bcfg2-lint against. (See + the bcfg2-lint ``--stdin`` option.) + :type files: list of strings + """ + Plugin.__init__(self, config, errorhandler=errorhandler, files=files) + + #: The server core self.core = core self.logger = self.core.logger - self.metadata = self.core.metadata - self.errorhandler.RegisterErrors({"broken-xinclude-chain": "warning"}) - def has_all_xincludes(self, mfile): - """ return true if self.files includes all XIncludes listed in - the specified metadata type, false otherwise""" - if self.files is None: - return True - else: - path = os.path.join(self.metadata.data, mfile) - if path in self.files: - xdata = lxml.etree.parse(path) - for el in xdata.findall('./%sinclude' % XI_NAMESPACE): - if not self.has_all_xincludes(el.get('href')): - self.LintError("broken-xinclude-chain", - "Broken XInclude chain: could not " - "include %s" % path) - return False - - return True + #: The metadata plugin + self.metadata = self.core.metadata diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py index 5c5e3da0c..eef176cca 100644 --- a/src/lib/Bcfg2/Server/Plugins/Bundler.py +++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py @@ -144,10 +144,10 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): - """ Perform various bundle checks """ + """ Perform various :ref:`Bundler + ` checks. """ def Run(self): - """ run plugin """ self.missing_bundles() for bundle in self.core.plugins['Bundler'].entries.values(): if (self.HandlesFile(bundle.name) and @@ -161,7 +161,8 @@ class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): "inconsistent-bundle-name": "warning"} def missing_bundles(self): - """ find bundles listed in Metadata but not implemented in Bundler """ + """ Find bundles listed in Metadata but not implemented in + Bundler. """ if self.files is None: # when given a list of files on stdin, this check is # useless, so skip it @@ -180,7 +181,11 @@ class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): bundle) def bundle_names(self, bundle): - """ verify bundle name attribute matches filename """ + """ Verify bundle name attribute matches filename. + + :param bundle: The bundle to verify + :type bundle: Bcfg2.Server.Plugins.Bundler.BundleFile + """ try: xdata = lxml.etree.XML(bundle.data) except AttributeError: diff --git a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py index 8d1e50526..09685d972 100644 --- a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py +++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py @@ -129,7 +129,12 @@ class GroupPatterns(Bcfg2.Server.Plugin.Plugin, class GroupPatternsLint(Bcfg2.Server.Lint.ServerPlugin): - """ bcfg2-lint plugin for GroupPatterns """ + """ ``bcfg2-lint`` plugin to check all given :ref:`GroupPatterns + ` patterns for validity. + This is simply done by trying to create a + :class:`Bcfg2.Server.Plugins.GroupPatterns.PatternMap` object for + each pattern, and catching exceptions and presenting them as + ``bcfg2-lint`` errors.""" def Run(self): cfg = self.core.plugins['GroupPatterns'].config diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py index 71e81c1fe..ceb1d9080 100644 --- a/src/lib/Bcfg2/Server/Plugins/Metadata.py +++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py @@ -1479,7 +1479,16 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): - """ bcfg2-lint plugin for Metadata """ + """ ``bcfg2-lint`` plugin for :ref:`Metadata + `. This checks for several things: + + * ```` tags nested inside other ```` tags; + * Deprecated options (like ``location="floating"``); + * Profiles that don't exist, or that aren't profile groups; + * Groups or clients that are defined multiple times; + * Multiple default groups or a default group that isn't a profile + group. + """ def Run(self): self.nested_clients() @@ -1502,8 +1511,8 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): "default-is-not-profile": "error"} def deprecated_options(self): - """ check for the location='floating' option, which has been - deprecated in favor of floating='true' """ + """ 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 @@ -1521,8 +1530,8 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): (loc, floating, self.RenderXML(el))) def nested_clients(self): - """ check for a Client tag inside a Client tag, which doesn't - make any sense """ + """ Check for a ```` tag inside a ```` tag, + which is either redundant or will never match. """ groupdata = self.metadata.groups_xml.xdata for el in groupdata.xpath("//Client//Client"): self.LintError("nested-client-tags", @@ -1530,8 +1539,8 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): (el.get("name"), self.RenderXML(el))) 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 """ + """ Check for clients that have profiles that are either not + flagged as profile groups in ``groups.xml``, or don't exist. """ if not hasattr(self.metadata, "clients_xml"): # using metadata database return @@ -1549,20 +1558,8 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): (profile, client.get("name"), profile, self.RenderXML(client))) - def duplicate_groups(self): - """ check for groups that are defined twice. We count a group - 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"), - "group", - include=lambda g: (g.get("profile") or - g.get("public") or - g.getchildren())) - def duplicate_default_groups(self): - """ check for multiple default groups """ + """ Check for multiple default groups. """ defaults = [] for grp in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \ self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"): @@ -1574,7 +1571,7 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): "\n".join(defaults)) def duplicate_clients(self): - """ check for clients that are defined twice. """ + """ Check for clients that are defined more than once. """ if not hasattr(self.metadata, "clients_xml"): # using metadata database return @@ -1582,17 +1579,34 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): self.metadata.clients_xml.xdata.xpath("//Client"), "client") - def duplicate_entries(self, allentries, etype, include=None): - """ generic duplicate entry finder """ - if include is None: - include = lambda e: True + def duplicate_groups(self): + """ Check for groups that are defined more than once. We + count a group tag as a definition if it a) has profile or + public set; or b) has any children.""" + allgroups = [ + g + for g in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + + self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group") + if g.get("profile") or g.get("public") or g.getchildren()] + self.duplicate_entries(allgroups, "group") + + def duplicate_entries(self, allentries, etype): + """ Generic duplicate entry finder. + + :param allentries: A list of all entries to check for + duplicates. + :type allentries: list of lxml.etree._Element + :param etype: The entry type. This will be used to determine + the error name (``duplicate-``) and for + display to the end user. + :type etype: string + """ entries = dict() for el in allentries: - if include(el): - if el.get("name") in entries: - entries[el.get("name")].append(self.RenderXML(el)) - else: - entries[el.get("name")] = [self.RenderXML(el)] + if el.get("name") in entries: + entries[el.get("name")].append(self.RenderXML(el)) + else: + entries[el.get("name")] = [self.RenderXML(el)] for ename, els in entries.items(): if len(els) > 1: self.LintError("duplicate-%s" % etype, @@ -1600,7 +1614,7 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin): (etype.title(), ename, "\n".join(els))) def default_is_profile(self): - """ ensure that the default group is a profile group """ + """ Ensure that the default group is a profile group. """ if (self.metadata.default and not self.metadata.groups[self.metadata.default].is_profile): xdata = \ diff --git a/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py b/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py index 7dac907e1..a1dcb575f 100644 --- a/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py +++ b/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py @@ -177,7 +177,10 @@ class Pkgmgr(Bcfg2.Server.Plugin.PrioDir): class PkgmgrLint(Bcfg2.Server.Lint.ServerlessPlugin): - """ find duplicate Pkgmgr entries with the same priority """ + """ Find duplicate :ref:`Pkgmgr + ` entries with the same + priority. """ + def Run(self): pset = set() for pfile in glob.glob(os.path.join(self.config['repo'], 'Pkgmgr', @@ -202,12 +205,13 @@ class PkgmgrLint(Bcfg2.Server.Lint.ServerlessPlugin): # check if package is already listed with same # priority, type, grp if ptuple in pset: - self.LintError("duplicate-package", - "Duplicate Package %s, priority:%s, type:%s" % - (pkg.get('name'), priority, ptype)) + self.LintError( + "duplicate-package", + "Duplicate Package %s, priority:%s, type:%s" % + (pkg.get('name'), priority, ptype)) else: pset.add(ptuple) - + @classmethod def Errors(cls): - return {"duplicate-packages":"error"} + return {"duplicate-packages": "error"} diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py index 7dd15f7b5..fcd73bae2 100644 --- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py +++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py @@ -97,7 +97,18 @@ class TemplateHelper(Bcfg2.Server.Plugin.Plugin, class TemplateHelperLint(Bcfg2.Server.Lint.ServerPlugin): - """ find duplicate Pkgmgr entries with the same priority """ + """ ``bcfg2-lint`` plugin to ensure that all :ref:`TemplateHelper + ` modules are valid. + This can check for: + + * A TemplateHelper module that cannot be imported due to syntax or + other compile-time errors; + * A TemplateHelper module that does not have an ``__export__`` + attribute, or whose ``__export__`` is not a list; + * Bogus symbols listed in ``__export__``, including symbols that + don't exist, that are reserved, or that start with underscores. + """ + def __init__(self, *args, **kwargs): Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs) self.reserved_keywords = dir(HelperModule("foo.py")) @@ -108,7 +119,11 @@ class TemplateHelperLint(Bcfg2.Server.Lint.ServerPlugin): self.check_helper(helper.name) def check_helper(self, helper): - """ check a helper module for export errors """ + """ Check a single helper module. + + :param helper: The filename of the helper module + :type helper: string + """ module_name = MODULE_RE.search(helper).group(1) try: -- cgit v1.2.3-1-g7c22 From d76a7373abe8e9413f30eeb2fbaf54d1d4136d39 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 14 May 2013 13:30:06 -0400 Subject: bcfg2-lint: fixed unit tests --- src/lib/Bcfg2/Server/Lint/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/__init__.py b/src/lib/Bcfg2/Server/Lint/__init__.py index d06347cf6..390ec208f 100644 --- a/src/lib/Bcfg2/Server/Lint/__init__.py +++ b/src/lib/Bcfg2/Server/Lint/__init__.py @@ -9,6 +9,9 @@ import lxml.etree import fcntl import termios import struct +from Bcfg2.Compat import walk_packages + +plugins = [m[1] for m in walk_packages(path=__path__)] def _ioctl_GWINSZ(fd): # pylint: disable=C0103 @@ -253,14 +256,14 @@ class ErrorHandler(object): logfunc(line) -class ServerlessPlugin(Plugin): +class ServerlessPlugin(Plugin): # pylint: disable=W0223 """ Base class for bcfg2-lint plugins that are run before the server starts up (i.e., plugins that check things that may prevent the server from starting up). """ pass -class ServerPlugin(Plugin): +class ServerPlugin(Plugin): # pylint: disable=W0223 """ Base class for bcfg2-lint plugins that check things that require the running Bcfg2 server. """ -- cgit v1.2.3-1-g7c22 From 7e93fe741c17203fa63f60a7d1f66bfcdfb90d03 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 14 May 2013 14:03:33 -0400 Subject: bcfg2-lint: fixed unit tests --- src/lib/Bcfg2/Server/Lint/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/__init__.py b/src/lib/Bcfg2/Server/Lint/__init__.py index 390ec208f..28644263f 100644 --- a/src/lib/Bcfg2/Server/Lint/__init__.py +++ b/src/lib/Bcfg2/Server/Lint/__init__.py @@ -11,7 +11,7 @@ import termios import struct from Bcfg2.Compat import walk_packages -plugins = [m[1] for m in walk_packages(path=__path__)] +plugins = [m[1] for m in walk_packages(path=__path__)] # pylint: disable=C0103 def _ioctl_GWINSZ(fd): # pylint: disable=C0103 -- cgit v1.2.3-1-g7c22