summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Client
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2/Client')
-rw-r--r--src/lib/Bcfg2/Client/Client.py52
-rw-r--r--src/lib/Bcfg2/Client/Frame.py62
-rw-r--r--src/lib/Bcfg2/Client/Proxy.py358
-rw-r--r--src/lib/Bcfg2/Client/Tools/APK.py7
-rw-r--r--src/lib/Bcfg2/Client/Tools/APT.py16
-rw-r--r--src/lib/Bcfg2/Client/Tools/Action.py34
-rw-r--r--src/lib/Bcfg2/Client/Tools/Chkconfig.py34
-rw-r--r--src/lib/Bcfg2/Client/Tools/DebInit.py59
-rw-r--r--src/lib/Bcfg2/Client/Tools/Encap.py11
-rw-r--r--src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/MacPorts.py5
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIXUsers.py121
-rw-r--r--src/lib/Bcfg2/Client/Tools/Pacman.py5
-rw-r--r--src/lib/Bcfg2/Client/Tools/Portage.py16
-rw-r--r--src/lib/Bcfg2/Client/Tools/RPM.py69
-rw-r--r--src/lib/Bcfg2/Client/Tools/RPMng.py9
-rw-r--r--src/lib/Bcfg2/Client/Tools/RcUpdate.py24
-rw-r--r--src/lib/Bcfg2/Client/Tools/SELinux.py32
-rw-r--r--src/lib/Bcfg2/Client/Tools/SMF.py73
-rw-r--r--src/lib/Bcfg2/Client/Tools/SYSV.py58
-rw-r--r--src/lib/Bcfg2/Client/Tools/Systemd.py31
-rw-r--r--src/lib/Bcfg2/Client/Tools/Upstart.py10
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM24.py404
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUMng.py9
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py76
-rw-r--r--src/lib/Bcfg2/Client/Tools/launchd.py110
27 files changed, 727 insertions, 962 deletions
diff --git a/src/lib/Bcfg2/Client/Client.py b/src/lib/Bcfg2/Client/Client.py
index 45e0b64e6..0488fcf21 100644
--- a/src/lib/Bcfg2/Client/Client.py
+++ b/src/lib/Bcfg2/Client/Client.py
@@ -8,26 +8,26 @@ import fcntl
import socket
import logging
import tempfile
-import Bcfg2.Proxy
import Bcfg2.Logger
import Bcfg2.Options
import Bcfg2.Client.XML
+import Bcfg2.Client.Proxy
import Bcfg2.Client.Frame
import Bcfg2.Client.Tools
+from Bcfg2.Utils import locked, Executor
from Bcfg2.Compat import xmlrpclib
from Bcfg2.version import __version__
-from subprocess import Popen, PIPE
class Client(object):
""" The main Bcfg2 client class """
- def __init__(self, setup):
+ def __init__(self):
self.toolset = None
self.tools = None
self.config = None
self._proxy = None
- self.setup = setup
+ self.setup = Bcfg2.Options.get_option_parser()
if self.setup['debug']:
level = logging.DEBUG
@@ -41,6 +41,9 @@ class Client(object):
to_file=self.setup['logging'])
self.logger = logging.getLogger('bcfg2')
self.logger.debug(self.setup)
+
+ self.cmd = Executor(self.setup['command_timeout'])
+
if self.setup['bundle_quick']:
if not self.setup['bundle'] and not self.setup['skipbundle']:
self.logger.error("-Q option requires -b or -B")
@@ -94,16 +97,14 @@ class Client(object):
stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
stat.S_IWUSR) # 0755
- proc = Popen(scriptname, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- ret.text, err = proc.communicate()
- rv = proc.wait()
- if err:
+ rv = self.cmd.run(scriptname, timeout=self.setup['timeout'])
+ if rv.stderr:
self.logger.warning("Probe %s has error output: %s" %
- (name, err))
- if rv:
+ (name, rv.stderr))
+ if not rv.success:
self._probe_failure(name, "Return value %s" % rv)
self.logger.info("Probe %s has result:" % name)
- self.logger.info(ret.text)
+ self.logger.info(rv.stdout)
finally:
os.unlink(scriptname)
except SystemExit:
@@ -121,7 +122,7 @@ class Client(object):
def proxy(self):
""" get an XML-RPC proxy to the server """
if self._proxy is None:
- self._proxy = Bcfg2.Proxy.ComponentProxy(
+ self._proxy = Bcfg2.Client.Proxy.ComponentProxy(
self.setup['server'],
self.setup['user'],
self.setup['password'],
@@ -141,8 +142,8 @@ class Client(object):
try:
probes = Bcfg2.Client.XML.XML(str(self.proxy.GetProbes()))
- except (Bcfg2.Proxy.ProxyError,
- Bcfg2.Proxy.CertificateError,
+ except (Bcfg2.Client.Proxy.ProxyError,
+ Bcfg2.Client.Proxy.CertificateError,
socket.gaierror,
socket.error):
err = sys.exc_info()[1]
@@ -165,7 +166,7 @@ class Client(object):
self.proxy.RecvProbeData(Bcfg2.Client.XML.tostring(
probedata,
xml_declaration=False).decode('UTF-8'))
- except Bcfg2.Proxy.ProxyError:
+ except Bcfg2.Client.Proxy.ProxyError:
err = sys.exc_info()[1]
self.fatal_error("Failed to upload probe data: %s" % err)
@@ -191,7 +192,7 @@ class Client(object):
if self.setup['profile']:
try:
self.proxy.AssertProfile(self.setup['profile'])
- except Bcfg2.Proxy.ProxyError:
+ except Bcfg2.Client.Proxy.ProxyError:
err = sys.exc_info()[1]
self.fatal_error("Failed to set client profile: %s" % err)
@@ -206,8 +207,8 @@ class Client(object):
"client version")
else:
self.logger.error("Failed to declare version: %s" % err)
- except (Bcfg2.Proxy.ProxyError,
- Bcfg2.Proxy.CertificateError,
+ except (Bcfg2.Client.Proxy.ProxyError,
+ Bcfg2.Client.Proxy.CertificateError,
socket.gaierror,
socket.error):
err = sys.exc_info()[1]
@@ -221,13 +222,13 @@ class Client(object):
self.proxy.GetDecisionList(self.setup['decision'])
self.logger.info("Got decision list from server:")
self.logger.info(self.setup['decision_list'])
- except Bcfg2.Proxy.ProxyError:
+ except Bcfg2.Client.Proxy.ProxyError:
err = sys.exc_info()[1]
self.fatal_error("Failed to get decision list: %s" % err)
try:
rawconfig = self.proxy.GetConfig().encode('UTF-8')
- except Bcfg2.Proxy.ProxyError:
+ except Bcfg2.Client.Proxy.ProxyError:
err = sys.exc_info()[1]
self.fatal_error("Failed to download configuration from "
"Bcfg2: %s" % err)
@@ -288,11 +289,7 @@ class Client(object):
#check lock here
try:
lockfile = open(self.setup['lockfile'], 'w')
- try:
- fcntl.lockf(lockfile.fileno(),
- fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError:
- # otherwise exit and give a warning to the user
+ if locked(lockfile.fileno()):
self.fatal_error("Another instance of Bcfg2 is running. "
"If you want to bypass the check, run "
"with the %s option" %
@@ -301,7 +298,8 @@ class Client(object):
raise
except:
lockfile = None
- self.logger.error("Failed to open lockfile")
+ self.logger.error("Failed to open lockfile %s: %s" %
+ (self.setup['lockfile'], sys.exc_info()[1]))
# execute the configuration
self.tools.Execute()
@@ -324,7 +322,7 @@ class Client(object):
self.proxy.RecvStats(Bcfg2.Client.XML.tostring(
feedback,
xml_declaration=False).decode('UTF-8'))
- except Bcfg2.Proxy.ProxyError:
+ except Bcfg2.Client.Proxy.ProxyError:
err = sys.exc_info()[1]
self.logger.error("Failed to upload configuration statistics: "
"%s" % err)
diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py
index 637a916d6..baf8a14f2 100644
--- a/src/lib/Bcfg2/Client/Frame.py
+++ b/src/lib/Bcfg2/Client/Frame.py
@@ -310,10 +310,10 @@ class Frame(object):
for bundle in self.setup['bundle']:
if bundle not in all_bundle_names:
self.logger.info("Warning: Bundle %s not found" % bundle)
- bundles = filter(lambda b: b.get('name') in self.setup['bundle'],
- bundles)
+ bundles = [b for b in bundles
+ if b.get('name') in self.setup['bundle']]
elif self.setup['indep']:
- bundles = filter(lambda b: b.tag != 'Bundle', bundles)
+ bundles = [b for b in bundles if b.tag != 'Bundle']
if self.setup['skipbundle']:
# warn if non-existent bundle given
if not self.setup['bundle_quick']:
@@ -321,39 +321,41 @@ class Frame(object):
if bundle not in all_bundle_names:
self.logger.info("Warning: Bundle %s not found" %
bundle)
- bundles = filter(lambda b:
- b.get('name') not in self.setup['skipbundle'],
- bundles)
+ bundles = [b for b in bundles
+ if b.get('name') not in self.setup['skipbundle']]
if self.setup['skipindep']:
- bundles = filter(lambda b: b.tag == 'Bundle', bundles)
+ bundles = [b for b in bundles if b.tag == 'Bundle']
self.whitelist = [e for e in self.whitelist
- if True in [e in b for b in bundles]]
+ if any(e in b for b in bundles)]
# first process prereq actions
for bundle in bundles[:]:
- if bundle.tag != 'Bundle':
- continue
- bmodified = len([item for item in bundle
- if item in self.whitelist])
+ if bundle.tag == 'Bundle':
+ bmodified = any(item in self.whitelist for item in bundle)
+ else:
+ bmodified = False
actions = [a for a in bundle.findall('./Action')
- if (a.get('timing') != 'post' and
+ if (a.get('timing') in ['pre', 'both'] and
(bmodified or a.get('when') == 'always'))]
# now we process all "always actions"
if self.setup['interactive']:
self.promptFilter(iprompt, actions)
self.DispatchInstallCalls(actions)
+ if bundle.tag != 'Bundle':
+ continue
+
# need to test to fail entries in whitelist
- if False in [self.states[a] for a in actions]:
+ if not all(self.states[a] for a in actions):
# then display bundles forced off with entries
- self.logger.info("Bundle %s failed prerequisite action" %
- (bundle.get('name')))
+ self.logger.info("%s %s failed prerequisite action" %
+ (bundle.tag, bundle.get('name')))
bundles.remove(bundle)
b_to_remv = [ent for ent in self.whitelist if ent in bundle]
if b_to_remv:
- self.logger.info("Not installing entries from Bundle %s" %
- (bundle.get('name')))
+ self.logger.info("Not installing entries from %s %s" %
+ (bundle.tag, bundle.get('name')))
self.logger.info(["%s:%s" % (e.tag, e.get('name'))
for e in b_to_remv])
for ent in b_to_remv:
@@ -419,14 +421,26 @@ class Frame(object):
# prune out unspecified bundles when running with -b
continue
for tool in self.tools:
+ if bundle in mbundles:
+ func = tool.BundleUpdated
+ else:
+ func = tool.BundleNotUpdated
try:
- if bundle in mbundles:
- tool.BundleUpdated(bundle, self.states)
- else:
- tool.BundleNotUpdated(bundle, self.states)
+ func(bundle, self.states)
+ except:
+ self.logger.error("%s.%s(%s:%s) call failed:" %
+ (tool.name, func.im_func.func_name,
+ bundle.tag, bundle.get("name")),
+ exc_info=1)
+
+ for indep in self.config.findall('.//Independent'):
+ for tool in self.tools:
+ try:
+ tool.BundleNotUpdated(indep, self.states)
except:
- self.logger.error("%s.BundleNotUpdated() call failed:" %
- tool.name, exc_info=1)
+ self.logger.error("%s.BundleNotUpdated(%s:%s) call failed:"
+ % (tool.name, indep.tag,
+ indep.get("name")), exc_info=1)
def Remove(self):
"""Remove extra entries."""
diff --git a/src/lib/Bcfg2/Client/Proxy.py b/src/lib/Bcfg2/Client/Proxy.py
new file mode 100644
index 000000000..1276c3ce9
--- /dev/null
+++ b/src/lib/Bcfg2/Client/Proxy.py
@@ -0,0 +1,358 @@
+import logging
+import re
+import socket
+
+# The ssl module is provided by either Python 2.6 or a separate ssl
+# package that works on older versions of Python (see
+# http://pypi.python.org/pypi/ssl). If neither can be found, look for
+# M2Crypto instead.
+try:
+ import ssl
+ SSL_LIB = 'py26_ssl'
+ SSL_ERROR = ssl.SSLError
+except ImportError:
+ from M2Crypto import SSL
+ import M2Crypto.SSL.Checker
+ SSL_LIB = 'm2crypto'
+ SSL_ERROR = SSL.SSLError
+
+import sys
+import time
+
+# Compatibility imports
+from Bcfg2.Compat import httplib, xmlrpclib, urlparse
+
+version = sys.version_info[:2]
+has_py26 = version >= (2, 6)
+
+__all__ = ["ComponentProxy",
+ "RetryMethod",
+ "SSLHTTPConnection",
+ "XMLRPCTransport"]
+
+
+class ProxyError(Exception):
+ """ ProxyError provides a consistent reporting interface to
+ the various xmlrpclib errors that might arise (mainly
+ ProtocolError and Fault) """
+ def __init__(self, err):
+ msg = None
+ if isinstance(err, xmlrpclib.ProtocolError):
+ # cut out the password in the URL
+ url = re.sub(r'([^:]+):(.*?)@([^@]+:\d+/)', r'\1:******@\3',
+ err.url)
+ msg = "XML-RPC Protocol Error for %s: %s (%s)" % (url,
+ err.errmsg,
+ err.errcode)
+ elif isinstance(err, xmlrpclib.Fault):
+ msg = "XML-RPC Fault: %s (%s)" % (err.faultString,
+ err.faultCode)
+ else:
+ msg = str(err)
+ Exception.__init__(self, msg)
+
+class CertificateError(Exception):
+ def __init__(self, commonName):
+ self.commonName = commonName
+ def __str__(self):
+ return ("Got unallowed commonName %s from server"
+ % self.commonName)
+
+_orig_Method = xmlrpclib._Method
+
+class RetryMethod(xmlrpclib._Method):
+ """Method with error handling and retries built in."""
+ log = logging.getLogger('xmlrpc')
+ max_retries = 3
+ retry_delay = 1
+
+ def __call__(self, *args):
+ for retry in range(self.max_retries):
+ if retry >= self.max_retries - 1:
+ final = True
+ else:
+ final = False
+ msg = None
+ try:
+ return _orig_Method.__call__(self, *args)
+ except xmlrpclib.ProtocolError:
+ err = sys.exc_info()[1]
+ msg = "Server failure: Protocol Error: %s %s" % \
+ (err.errcode, err.errmsg)
+ except xmlrpclib.Fault:
+ msg = sys.exc_info()[1]
+ except socket.error:
+ err = sys.exc_info()[1]
+ if hasattr(err, 'errno') and err.errno == 336265218:
+ msg = "SSL Key error: %s" % err
+ elif hasattr(err, 'errno') and err.errno == 185090050:
+ msg = "SSL CA error: %s" % err
+ elif final:
+ msg = "Server failure: %s" % err
+ except CertificateError:
+ err = sys.exc_info()[1]
+ msg = "Got unallowed commonName %s from server" % \
+ err.commonName
+ except KeyError:
+ err = sys.exc_info()[1]
+ msg = "Server disallowed connection: %s" % err
+ except ProxyError:
+ err = sys.exc_info()[1]
+ msg = err
+ except:
+ raise
+ etype, err = sys.exc_info()[:2]
+ msg = "Unknown failure: %s (%s)" % (err, etype.__name__)
+ if msg:
+ if final:
+ self.log.error(msg)
+ raise ProxyError(msg)
+ else:
+ self.log.info(msg)
+ time.sleep(self.retry_delay)
+
+xmlrpclib._Method = RetryMethod
+
+
+class SSLHTTPConnection(httplib.HTTPConnection):
+ """Extension of HTTPConnection that
+ implements SSL and related behaviors.
+ """
+
+ def __init__(self, host, port=None, strict=None, timeout=90, key=None,
+ cert=None, ca=None, scns=None, protocol='xmlrpc/ssl'):
+ """Initializes the `httplib.HTTPConnection` object and stores security
+ parameters
+
+ Parameters
+ ----------
+ host : string
+ Name of host to contact
+ port : int, optional
+ Port on which to contact the host. If none is specified,
+ the default port of 80 will be used unless the `host`
+ string has a port embedded in the form host:port.
+ strict : Boolean, optional
+ Passed to the `httplib.HTTPConnection` constructor and if
+ True, causes the `BadStatusLine` exception to be raised if
+ the status line cannot be parsed as a valid HTTP 1.0 or
+ 1.1 status.
+ timeout : int, optional
+ Causes blocking operations to timeout after `timeout`
+ seconds.
+ key : string, optional
+ The file system path to the local endpoint's SSL key. May
+ specify the same file as `cert` if using a file that
+ contains both. See
+ http://docs.python.org/library/ssl.html#ssl-certificates
+ for details. Required if using xmlrpc/ssl with client
+ certificate authentication.
+ cert : string, optional
+ The file system path to the local endpoint's SSL
+ certificate. May specify the same file as `cert` if using
+ a file that contains both. See
+ http://docs.python.org/library/ssl.html#ssl-certificates
+ for details. Required if using xmlrpc/ssl with client
+ certificate authentication.
+ ca : string, optional
+ The file system path to a set of concatenated certificate
+ authority certs, which are used to validate certificates
+ passed from the other end of the connection.
+ scns : array-like, optional
+ List of acceptable server commonNames. The peer cert's
+ common name must appear in this list, otherwise the
+ connect() call will throw a `CertificateError`.
+ protocol : {'xmlrpc/ssl', 'xmlrpc/tlsv1'}, optional
+ Communication protocol to use.
+
+ """
+ if not has_py26:
+ httplib.HTTPConnection.__init__(self, host, port, strict)
+ else:
+ httplib.HTTPConnection.__init__(self, host, port, strict, timeout)
+ self.logger = logging.getLogger("%s.%s" % (self.__class__.__module__,
+ self.__class__.__name__))
+
+ self.key = key
+ self.cert = cert
+ self.ca = ca
+ self.scns = scns
+ self.protocol = protocol
+ self.timeout = timeout
+
+ def connect(self):
+ """Initiates a connection using previously set attributes."""
+ if SSL_LIB == 'py26_ssl':
+ self._connect_py26ssl()
+ elif SSL_LIB == 'm2crypto':
+ self._connect_m2crypto()
+ else:
+ raise Exception("No SSL module support")
+
+ def _connect_py26ssl(self):
+ """Initiates a connection using the ssl module."""
+ # check for IPv6
+ hostip = socket.getaddrinfo(self.host,
+ self.port,
+ socket.AF_UNSPEC,
+ socket.SOCK_STREAM)[0][4][0]
+ if ':' in hostip:
+ rawsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+ else:
+ rawsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ if self.protocol == 'xmlrpc/ssl':
+ ssl_protocol_ver = ssl.PROTOCOL_SSLv23
+ elif self.protocol == 'xmlrpc/tlsv1':
+ ssl_protocol_ver = ssl.PROTOCOL_TLSv1
+ else:
+ self.logger.error("Unknown protocol %s" % (self.protocol))
+ raise Exception("unknown protocol %s" % self.protocol)
+ if self.ca:
+ other_side_required = ssl.CERT_REQUIRED
+ else:
+ other_side_required = ssl.CERT_NONE
+ self.logger.warning("No ca is specified. Cannot authenticate the server with SSL.")
+ if self.cert and not self.key:
+ self.logger.warning("SSL cert specfied, but no key. Cannot authenticate this client with SSL.")
+ self.cert = None
+ if self.key and not self.cert:
+ self.logger.warning("SSL key specfied, but no cert. Cannot authenticate this client with SSL.")
+ self.key = None
+
+ rawsock.settimeout(self.timeout)
+ self.sock = ssl.SSLSocket(rawsock, cert_reqs=other_side_required,
+ ca_certs=self.ca, suppress_ragged_eofs=True,
+ keyfile=self.key, certfile=self.cert,
+ ssl_version=ssl_protocol_ver)
+ self.sock.connect((self.host, self.port))
+ peer_cert = self.sock.getpeercert()
+ if peer_cert and self.scns:
+ scn = [x[0][1] for x in peer_cert['subject'] if x[0][0] == 'commonName'][0]
+ if scn not in self.scns:
+ raise CertificateError(scn)
+ self.sock.closeSocket = True
+
+ def _connect_m2crypto(self):
+ """Initiates a connection using the M2Crypto module."""
+
+ if self.protocol == 'xmlrpc/ssl':
+ ctx = SSL.Context('sslv23')
+ elif self.protocol == 'xmlrpc/tlsv1':
+ ctx = SSL.Context('tlsv1')
+ else:
+ self.logger.error("Unknown protocol %s" % (self.protocol))
+ raise Exception("unknown protocol %s" % self.protocol)
+
+ if self.ca:
+ # Use the certificate authority to validate the cert
+ # presented by the server
+ ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9)
+ if ctx.load_verify_locations(self.ca) != 1:
+ raise Exception('No CA certs')
+ else:
+ self.logger.warning("No ca is specified. Cannot authenticate the server with SSL.")
+
+ if self.cert and self.key:
+ # A cert/key is defined, use them to support client
+ # authentication to the server
+ ctx.load_cert(self.cert, self.key)
+ elif self.cert:
+ self.logger.warning("SSL cert specfied, but no key. Cannot authenticate this client with SSL.")
+ elif self.key:
+ self.logger.warning("SSL key specfied, but no cert. Cannot authenticate this client with SSL.")
+
+ self.sock = SSL.Connection(ctx)
+ if re.match('\\d+\\.\\d+\\.\\d+\\.\\d+', self.host):
+ # host is ip address
+ try:
+ hostname = socket.gethostbyaddr(self.host)[0]
+ except:
+ # fall back to ip address
+ hostname = self.host
+ else:
+ hostname = self.host
+ try:
+ self.sock.connect((hostname, self.port))
+ # automatically checks cert matches host
+ except M2Crypto.SSL.Checker.WrongHost:
+ wr = sys.exc_info()[1]
+ raise CertificateError(wr)
+
+
+class XMLRPCTransport(xmlrpclib.Transport):
+ def __init__(self, key=None, cert=None, ca=None,
+ scns=None, use_datetime=0, timeout=90):
+ if hasattr(xmlrpclib.Transport, '__init__'):
+ xmlrpclib.Transport.__init__(self, use_datetime)
+ self.key = key
+ self.cert = cert
+ self.ca = ca
+ self.scns = scns
+ self.timeout = timeout
+
+ def make_connection(self, host):
+ host, self._extra_headers = self.get_host_info(host)[0:2]
+ return SSLHTTPConnection(host,
+ key=self.key,
+ cert=self.cert,
+ ca=self.ca,
+ scns=self.scns,
+ timeout=self.timeout)
+
+ def request(self, host, handler, request_body, verbose=0):
+ """Send request to server and return response."""
+ try:
+ conn = self.send_request(host, handler, request_body, False)
+ response = conn.getresponse()
+ errcode = response.status
+ errmsg = response.reason
+ headers = response.msg
+ except (socket.error, SSL_ERROR):
+ err = sys.exc_info()[1]
+ raise ProxyError(xmlrpclib.ProtocolError(host + handler,
+ 408,
+ str(err),
+ self._extra_headers))
+
+ if errcode != 200:
+ raise ProxyError(xmlrpclib.ProtocolError(host + handler,
+ errcode,
+ errmsg,
+ headers))
+
+ self.verbose = verbose
+ return self.parse_response(response)
+
+ if sys.hexversion < 0x03000000:
+ def send_request(self, host, handler, request_body, debug):
+ """ send_request() changed significantly in py3k."""
+ conn = self.make_connection(host)
+ xmlrpclib.Transport.send_request(self, conn, handler, request_body)
+ self.send_host(conn, host)
+ self.send_user_agent(conn)
+ self.send_content(conn, request_body)
+ return conn
+
+
+def ComponentProxy(url, user=None, password=None, key=None, cert=None, ca=None,
+ allowedServerCNs=None, timeout=90, retries=3, delay=1):
+
+ """Constructs proxies to components.
+
+ Arguments:
+ component_name -- name of the component to connect to
+
+ Additional arguments are passed to the ServerProxy constructor.
+
+ """
+ xmlrpclib._Method.max_retries = retries
+ xmlrpclib._Method.retry_delay = delay
+
+ if user and password:
+ method, path = urlparse(url)[:2]
+ newurl = "%s://%s:%s@%s" % (method, user, password, path)
+ else:
+ newurl = url
+ ssl_trans = XMLRPCTransport(key, cert, ca,
+ allowedServerCNs, timeout=float(timeout))
+ return xmlrpclib.ServerProxy(newurl, allow_none=True, transport=ssl_trans)
diff --git a/src/lib/Bcfg2/Client/Tools/APK.py b/src/lib/Bcfg2/Client/Tools/APK.py
index f23fbb119..8a02b7d6d 100644
--- a/src/lib/Bcfg2/Client/Tools/APK.py
+++ b/src/lib/Bcfg2/Client/Tools/APK.py
@@ -19,8 +19,8 @@ class APK(Bcfg2.Client.Tools.PkgTool):
def RefreshPackages(self):
"""Refresh memory hashes of packages."""
- names = self.cmd.run("/sbin/apk info")[1]
- nameversions = self.cmd.run("/sbin/apk info -v")[1]
+ names = self.cmd.run("/sbin/apk info").stdout.splitlines()
+ nameversions = self.cmd.run("/sbin/apk info -v").stdout.splitlines()
for pkg in zip(names, nameversions):
pkgname = pkg[0]
version = pkg[1][len(pkgname) + 1:]
@@ -56,7 +56,6 @@ class APK(Bcfg2.Client.Tools.PkgTool):
"""Remove extra packages."""
names = [pkg.get('name') for pkg in packages]
self.logger.info("Removing packages: %s" % " ".join(names))
- self.cmd.run("/sbin/apk del %s" % \
- " ".join(names))
+ self.cmd.run("/sbin/apk del %s" % " ".join(names))
self.RefreshPackages()
self.extra = self.FindExtra()
diff --git a/src/lib/Bcfg2/Client/Tools/APT.py b/src/lib/Bcfg2/Client/Tools/APT.py
index 879d2720a..0cdefa613 100644
--- a/src/lib/Bcfg2/Client/Tools/APT.py
+++ b/src/lib/Bcfg2/Client/Tools/APT.py
@@ -59,7 +59,8 @@ class APT(Bcfg2.Client.Tools.Tool):
os.environ["DEBIAN_FRONTEND"] = 'noninteractive'
self.actions = {}
if self.setup['kevlar'] and not self.setup['dryrun']:
- self.cmd.run("%s --force-confold --configure --pending" % self.dpkg)
+ self.cmd.run("%s --force-confold --configure --pending" %
+ self.dpkg)
self.cmd.run("%s clean" % self.aptget)
try:
self.pkg_cache = apt.cache.Cache()
@@ -88,13 +89,15 @@ class APT(Bcfg2.Client.Tools.Tool):
for (name, version) in extras]
def VerifyDebsums(self, entry, modlist):
- output = self.cmd.run("%s -as %s" % (self.debsums,
- entry.get('name')))[1]
+ output = \
+ self.cmd.run("%s -as %s" %
+ (self.debsums, entry.get('name'))).stdout.splitlines()
if len(output) == 1 and "no md5sums for" in output[0]:
self.logger.info("Package %s has no md5sums. Cannot verify" % \
entry.get('name'))
- entry.set('qtext', "Reinstall Package %s-%s to setup md5sums? (y/N) " \
- % (entry.get('name'), entry.get('version')))
+ entry.set('qtext',
+ "Reinstall Package %s-%s to setup md5sums? (y/N) " %
+ (entry.get('name'), entry.get('version')))
return False
files = []
for item in output:
@@ -250,8 +253,7 @@ class APT(Bcfg2.Client.Tools.Tool):
self.logger.error(bad_pkgs)
if not ipkgs:
return
- rc = self.cmd.run(self.pkgcmd % (" ".join(ipkgs)))[0]
- if rc:
+ if not self.cmd.run(self.pkgcmd % (" ".join(ipkgs))):
self.logger.error("APT command failed")
self.pkg_cache = apt.cache.Cache()
self.extra = self.FindExtra()
diff --git a/src/lib/Bcfg2/Client/Tools/Action.py b/src/lib/Bcfg2/Client/Tools/Action.py
index b1a897c81..7e8366928 100644
--- a/src/lib/Bcfg2/Client/Tools/Action.py
+++ b/src/lib/Bcfg2/Client/Tools/Action.py
@@ -11,9 +11,8 @@ from Bcfg2.Compat import input # pylint: disable=W0622
class Action(Bcfg2.Client.Tools.Tool):
"""Implement Actions"""
name = 'Action'
- __handles__ = [('PostInstall', None), ('Action', None)]
- __req__ = {'PostInstall': ['name'],
- 'Action': ['name', 'timing', 'when', 'command', 'status']}
+ __handles__ = [('Action', None)]
+ __req__ = {'Action': ['name', 'timing', 'when', 'command', 'status']}
def _action_allowed(self, action):
""" Return true if the given action is allowed to be run by
@@ -49,14 +48,11 @@ class Action(Bcfg2.Client.Tools.Tool):
"to build mode" % entry.get('command'))
return False
self.logger.debug("Running Action %s" % (entry.get('name')))
- rv = self.cmd.run(entry.get('command'))[0]
+ rv = self.cmd.run(entry.get('command'))
self.logger.debug("Action: %s got return code %s" %
- (entry.get('command'), rv))
- entry.set('rc', str(rv))
- if entry.get('status', 'check') == 'ignore':
- return True
- else:
- return rv == 0
+ (entry.get('command'), rv.retval))
+ entry.set('rc', str(rv.retval))
+ return entry.get('status', 'check') == 'ignore' or rv.success
else:
self.logger.debug("In dryrun mode: not running action: %s" %
(entry.get('name')))
@@ -66,28 +62,14 @@ class Action(Bcfg2.Client.Tools.Tool):
"""Actions always verify true."""
return True
- def VerifyPostInstall(self, dummy, _):
- """Actions always verify true."""
- return True
-
def InstallAction(self, entry):
"""Run actions as pre-checks for bundle installation."""
if entry.get('timing') != 'post':
return self.RunAction(entry)
return True
- def InstallPostInstall(self, entry):
- """ Install a deprecated PostInstall entry """
- self.logger.warning("Installing deprecated PostInstall entry %s" %
- entry.get("name"))
- return self.InstallAction(entry)
-
def BundleUpdated(self, bundle, states):
"""Run postinstalls when bundles have been updated."""
- for postinst in bundle.findall("PostInstall"):
- if not self._action_allowed(postinst):
- continue
- self.cmd.run(postinst.get('name'))
for action in bundle.findall("Action"):
if action.get('timing') in ['post', 'both']:
if not self._action_allowed(action):
@@ -97,8 +79,8 @@ class Action(Bcfg2.Client.Tools.Tool):
def BundleNotUpdated(self, bundle, states):
"""Run Actions when bundles have not been updated."""
for action in bundle.findall("Action"):
- if action.get('timing') in ['post', 'both'] and \
- action.get('when') != 'modified':
+ if (action.get('timing') in ['post', 'both'] and
+ action.get('when') != 'modified'):
if not self._action_allowed(action):
continue
states[action] = self.RunAction(action)
diff --git a/src/lib/Bcfg2/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
index e1ad35861..ec7f462b3 100644
--- a/src/lib/Bcfg2/Client/Tools/Chkconfig.py
+++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
@@ -24,16 +24,14 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
if entry.get('status') == 'ignore':
return True
- try:
- cmd = "/sbin/chkconfig --list %s " % (entry.get('name'))
- raw = self.cmd.run(cmd)[1]
- patterns = ["error reading information", "unknown service"]
- srvdata = [line.split() for line in raw for pattern in patterns \
- if pattern not in line][0]
- except IndexError:
- # Ocurrs when no lines are returned (service not installed)
+ rv = self.cmd.run("/sbin/chkconfig --list %s " % entry.get('name'))
+ if rv.success:
+ srvdata = rv.stdout.splitlines()[0].split()
+ else:
+ # service not installed
entry.set('current_status', 'off')
return False
+
if len(srvdata) == 2:
# This is an xinetd service
if entry.get('status') == srvdata[1]:
@@ -43,7 +41,7 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
return False
try:
- onlevels = [level.split(':')[0] for level in srvdata[1:] \
+ onlevels = [level.split(':')[0] for level in srvdata[1:]
if level.split(':')[1] == 'on']
except IndexError:
onlevels = []
@@ -70,25 +68,25 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
if entry.get('status') == 'off':
rv &= self.cmd.run((rcmd + " --level 0123456") %
(entry.get('name'),
- entry.get('status')))[0] == 0
+ entry.get('status'))).success
if entry.get("current_status") == "on":
- rv &= self.stop_service(entry)
+ rv &= self.stop_service(entry).success
else:
rv &= self.cmd.run(rcmd % (entry.get('name'),
- entry.get('status')))[0] == 0
+ entry.get('status'))).success
if entry.get("current_status") == "off":
- rv &= (self.start_service(entry) == 0)
+ rv &= self.start_service(entry).success
return rv
def FindExtra(self):
"""Locate extra chkconfig Services."""
allsrv = [line.split()[0]
- for line in self.cmd.run("/sbin/chkconfig "
- "--list 2>/dev/null|grep :on")[1]]
+ for line in self.cmd.run("/sbin/chkconfig",
+ "--list").stdout.splitlines()
+ if ":on" in line]
self.logger.debug('Found active services:')
self.logger.debug(allsrv)
specified = [srv.get('name') for srv in self.getSupportedEntries()]
- return [Bcfg2.Client.XML.Element('Service',
- type='chkconfig',
- name=name) \
+ return [Bcfg2.Client.XML.Element('Service', type='chkconfig',
+ name=name)
for name in allsrv if name not in specified]
diff --git a/src/lib/Bcfg2/Client/Tools/DebInit.py b/src/lib/Bcfg2/Client/Tools/DebInit.py
index 7d5af1127..ca556e98b 100644
--- a/src/lib/Bcfg2/Client/Tools/DebInit.py
+++ b/src/lib/Bcfg2/Client/Tools/DebInit.py
@@ -15,7 +15,8 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
__execs__ = ['/usr/sbin/update-rc.d', '/usr/sbin/invoke-rc.d']
__handles__ = [('Service', 'deb')]
__req__ = {'Service': ['name', 'status']}
- svcre = re.compile("/etc/.*/(?P<action>[SK])(?P<sequence>\d+)(?P<name>\S+)")
+ svcre = \
+ re.compile("/etc/.*/(?P<action>[SK])(?P<sequence>\d+)(?P<name>\S+)")
# implement entry (Verify|Install) ops
def VerifyService(self, entry, _):
@@ -28,7 +29,7 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
files = []
try:
- deb_version = open('/etc/debian_version', 'r').read().split('/', 1)[0]
+ deb_version = open('/etc/debian_version').read().split('/', 1)[0]
except IOError:
deb_version = 'unknown'
@@ -59,20 +60,20 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
return False
else:
return True
+ elif files:
+ if start_sequence:
+ for filename in files:
+ match = self.svcre.match(filename)
+ file_sequence = int(match.group('sequence'))
+ if ((match.group('action') == 'S' and
+ file_sequence != start_sequence) or
+ (match.group('action') == 'K' and
+ file_sequence != kill_sequence)):
+ return False
+ return True
else:
- if files:
- if start_sequence:
- for filename in files:
- match = self.svcre.match(filename)
- file_sequence = int(match.group('sequence'))
- if match.group('action') == 'S' and file_sequence != start_sequence:
- return False
- if match.group('action') == 'K' and file_sequence != kill_sequence:
- return False
- return True
- else:
- entry.set('current_status', 'off')
- return False
+ entry.set('current_status', 'off')
+ return False
def InstallService(self, entry):
"""Install Service for entry."""
@@ -80,35 +81,35 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
try:
os.stat('/etc/init.d/%s' % entry.get('name'))
except OSError:
- self.logger.debug("Init script for service %s does not exist" % entry.get('name'))
+ self.logger.debug("Init script for service %s does not exist" %
+ entry.get('name'))
return False
if entry.get('status') == 'off':
self.cmd.run("/usr/sbin/invoke-rc.d %s stop" % (entry.get('name')))
- cmdrc = self.cmd.run("/usr/sbin/update-rc.d -f %s remove" % entry.get('name'))[0]
+ return self.cmd.run("/usr/sbin/update-rc.d -f %s remove" %
+ entry.get('name')).success
else:
command = "/usr/sbin/update-rc.d %s defaults" % (entry.get('name'))
if entry.get('sequence'):
- cmdrc = self.cmd.run("/usr/sbin/update-rc.d -f %s remove" % entry.get('name'))[0]
- if cmdrc != 0:
+ if not self.cmd.run("/usr/sbin/update-rc.d -f %s remove" %
+ entry.get('name')).success:
return False
start_sequence = int(entry.get('sequence'))
kill_sequence = 100 - start_sequence
command = "%s %d %d" % (command, start_sequence, kill_sequence)
- cmdrc = self.cmd.run(command)[0]
- return cmdrc == 0
+ return self.cmd.run(command).success
def FindExtra(self):
"""Find Extra Debian Service entries."""
specified = [entry.get('name') for entry in self.getSupportedEntries()]
- extra = []
- for name in [self.svcre.match(fname).group('name') for fname in
- glob.glob("/etc/rc[12345].d/S*") \
- if self.svcre.match(fname).group('name') not in specified]:
- if name not in extra:
- extra.append(name)
- return [Bcfg2.Client.XML.Element('Service', name=name, type='deb') for name \
- in extra]
+ extra = set()
+ for fname in glob.glob("/etc/rc[12345].d/S*"):
+ name = self.svcre.match(fname).group('name')
+ if name not in specified:
+ extra.add(name)
+ return [Bcfg2.Client.XML.Element('Service', name=name, type='deb')
+ for name in list(extra)]
def Remove(self, _):
"""Remove extra service entries."""
diff --git a/src/lib/Bcfg2/Client/Tools/Encap.py b/src/lib/Bcfg2/Client/Tools/Encap.py
index ca6fc7653..678e0f00c 100644
--- a/src/lib/Bcfg2/Client/Tools/Encap.py
+++ b/src/lib/Bcfg2/Client/Tools/Encap.py
@@ -33,14 +33,13 @@ class Encap(Bcfg2.Client.Tools.PkgTool):
self.logger.info("Insufficient information of Package %s; "
"cannot Verify" % entry.get('name'))
return False
- cmdrc = self.cmd.run("/usr/local/bin/epkg -q -S -k %s-%s >/dev/null" %
- (entry.get('name'), entry.get('version')))[0]
- if cmdrc != 0:
+ success = self.cmd.run("/usr/local/bin/epkg -q -S -k %s-%s" %
+ (entry.get('name'),
+ entry.get('version'))).success
+ if not success:
self.logger.debug("Package %s version incorrect" %
entry.get('name'))
- else:
- return True
- return False
+ return success
def Remove(self, packages):
"""Deal with extra configuration detected."""
diff --git a/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py b/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py
index ded84bef4..635318805 100644
--- a/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py
+++ b/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py
@@ -20,7 +20,7 @@ class FreeBSDPackage(Bcfg2.Client.Tools.PkgTool):
def RefreshPackages(self):
self.installed = {}
- packages = self.cmd.run("/usr/sbin/pkg_info -a -E")[1]
+ packages = self.cmd.run("/usr/sbin/pkg_info -a -E").stdout.splitlines()
pattern = re.compile('(.*)-(\d.*)')
for pkg in packages:
if pattern.match(pkg):
diff --git a/src/lib/Bcfg2/Client/Tools/MacPorts.py b/src/lib/Bcfg2/Client/Tools/MacPorts.py
index be441135e..bc3765ec6 100644
--- a/src/lib/Bcfg2/Client/Tools/MacPorts.py
+++ b/src/lib/Bcfg2/Client/Tools/MacPorts.py
@@ -19,7 +19,8 @@ class MacPorts(Bcfg2.Client.Tools.PkgTool):
def RefreshPackages(self):
"""Refresh memory hashes of packages."""
- pkgcache = self.cmd.run("/opt/local/bin/port installed")[1]
+ pkgcache = self.cmd.run(["/opt/local/bin/port",
+ "installed"]).stdout.splitlines()
self.installed = {}
for pkg in pkgcache:
if pkg.startswith("Warning:"):
@@ -65,7 +66,7 @@ class MacPorts(Bcfg2.Client.Tools.PkgTool):
"""Remove extra packages."""
names = [pkg.get('name') for pkg in packages]
self.logger.info("Removing packages: %s" % " ".join(names))
- self.cmd.run("/opt/local/bin/port uninstall %s" % \
+ self.cmd.run("/opt/local/bin/port uninstall %s" %
" ".join(names))
self.RefreshPackages()
self.extra = self.FindExtra()
diff --git a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py
index 7c8a4d578..e9db33d16 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py
@@ -1,95 +1,11 @@
""" A tool to handle creating users and groups with useradd/mod/del
and groupadd/mod/del """
-import sys
import pwd
import grp
-import subprocess
import Bcfg2.Client.XML
import Bcfg2.Client.Tools
-from Bcfg2.Compat import any # pylint: disable=W0622
-
-
-class IDRangeSet(object):
- """ Representation of a set of integer ranges. Used to describe
- which UID/GID ranges are managed or unmanaged. """
-
- def __init__(self, *ranges):
- self.ranges = []
- self.ints = []
- self.str = ",".join(str(r) for r in ranges)
- for item in ranges:
- item = str(item).strip()
- if item.endswith("-"):
- self.ranges.append((int(item[:-1]), None))
- elif '-' in str(item):
- self.ranges.append(tuple(int(x) for x in item.split('-')))
- else:
- self.ints.append(int(item))
-
- def __contains__(self, other):
- other = int(other)
- if other in self.ints:
- return True
- return any((end is None and other >= start) or
- (end is not None and other >= start and other <= end)
- for start, end in self.ranges)
-
- def __repr__(self):
- return "%s:%s" % (self.__class__.__name__, str(self))
-
- def __str__(self):
- return "[%s]" % self.str
-
- def __len__(self):
- return len(self.ranges) + len(self.ints)
-
-
-class ExecutionError(Exception):
- """ Raised when running an external command fails """
-
- def __init__(self, msg, retval=None):
- Exception.__init__(self, msg)
- self.retval = retval
-
- def __str__(self):
- return "%s (rv: %s)" % (Exception.__str__(self),
- self.retval)
-
-
-class Executor(object):
- """ A better version of Bcfg2.Client.Tool.Executor, which captures
- stderr, raises exceptions on error, and doesn't use the shell to
- execute by default """
-
- def __init__(self, logger):
- self.logger = logger
- self.stdout = None
- self.stderr = None
- self.retval = None
-
- def run(self, command, inputdata=None, shell=False):
- """ Run a command, given as a list, optionally giving it the
- specified input data """
- self.logger.debug("Running: %s" % " ".join(command))
- proc = subprocess.Popen(command, shell=shell, bufsize=16384,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, close_fds=True)
- if inputdata:
- for line in inputdata.splitlines():
- self.logger.debug('> %s' % line)
- (self.stdout, self.stderr) = proc.communicate(inputdata)
- else:
- (self.stdout, self.stderr) = proc.communicate()
- for line in self.stdout.splitlines(): # pylint: disable=E1103
- self.logger.debug('< %s' % line)
- self.retval = proc.wait()
- if self.retval == 0:
- for line in self.stderr.splitlines(): # pylint: disable=E1103
- self.logger.warning(line)
- return True
- else:
- raise ExecutionError(self.stderr, self.retval)
+from Bcfg2.Utils import PackedDigitRange
class POSIXUsers(Bcfg2.Client.Tools.Tool):
@@ -102,7 +18,6 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool):
('POSIXGroup', None)]
__req__ = dict(POSIXUser=['name'],
POSIXGroup=['name'])
- experimental = True
#: A mapping of XML entry attributes to the indexes of
#: corresponding values in the get{pw|gr}all data structures
@@ -118,22 +33,21 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool):
Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config)
self.set_defaults = dict(POSIXUser=self.populate_user_entry,
POSIXGroup=lambda g: g)
- self.cmd = Executor(logger)
self._existing = None
self._whitelist = dict(POSIXUser=None, POSIXGroup=None)
self._blacklist = dict(POSIXUser=None, POSIXGroup=None)
if self.setup['posix_uid_whitelist']:
self._whitelist['POSIXUser'] = \
- IDRangeSet(*self.setup['posix_uid_whitelist'])
+ PackedDigitRange(*self.setup['posix_uid_whitelist'])
else:
self._blacklist['POSIXUser'] = \
- IDRangeSet(*self.setup['posix_uid_blacklist'])
+ PackedDigitRange(*self.setup['posix_uid_blacklist'])
if self.setup['posix_gid_whitelist']:
self._whitelist['POSIXGroup'] = \
- IDRangeSet(*self.setup['posix_gid_whitelist'])
+ PackedDigitRange(*self.setup['posix_gid_whitelist'])
else:
self._blacklist['POSIXGroup'] = \
- IDRangeSet(*self.setup['posix_gid_blacklist'])
+ PackedDigitRange(*self.setup['posix_gid_blacklist'])
@property
def existing(self):
@@ -309,16 +223,14 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool):
action = "add"
else:
action = "mod"
- try:
- self.cmd.run(self._get_cmd(action,
- self.set_defaults[entry.tag](entry)))
+ rv = self.cmd.run(self._get_cmd(action,
+ self.set_defaults[entry.tag](entry)))
+ if rv.success:
self.modified.append(entry)
- return True
- except ExecutionError:
+ else:
self.logger.error("POSIXUsers: Error creating %s %s: %s" %
- (entry.tag, entry.get("name"),
- sys.exc_info()[1]))
- return False
+ (entry.tag, entry.get("name"), rv.error))
+ return rv.success
def _get_cmd(self, action, entry):
""" Get a command to perform the appropriate action (add, mod,
@@ -373,11 +285,8 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool):
def _remove(self, entry):
""" Remove an entry """
- try:
- self.cmd.run(self._get_cmd("del", entry))
- return True
- except ExecutionError:
+ rv = self.cmd.run(self._get_cmd("del", entry))
+ if not rv.success:
self.logger.error("POSIXUsers: Error deleting %s %s: %s" %
- (entry.tag, entry.get("name"),
- sys.exc_info()[1]))
- return False
+ (entry.tag, entry.get("name"), rv.error))
+ return rv.success
diff --git a/src/lib/Bcfg2/Client/Tools/Pacman.py b/src/lib/Bcfg2/Client/Tools/Pacman.py
index 9c14a3de6..12785afee 100644
--- a/src/lib/Bcfg2/Client/Tools/Pacman.py
+++ b/src/lib/Bcfg2/Client/Tools/Pacman.py
@@ -20,9 +20,8 @@ class Pacman(Bcfg2.Client.Tools.PkgTool):
def RefreshPackages(self):
'''Refresh memory hashes of packages'''
- pkgcache = self.cmd.run("/usr/bin/pacman -Q")[1]
self.installed = {}
- for pkg in pkgcache:
+ for pkg in self.cmd.run("/usr/bin/pacman -Q").stdout.splitlines():
pkgname = pkg.split(' ')[0].strip()
version = pkg.split(' ')[1].strip()
#self.logger.info(" pkgname: %s, version: %s" % (pkgname, version))
@@ -62,7 +61,7 @@ class Pacman(Bcfg2.Client.Tools.PkgTool):
'''Remove extra packages'''
names = [pkg.get('name') for pkg in packages]
self.logger.info("Removing packages: %s" % " ".join(names))
- self.cmd.run("%s --noconfirm --noprogressbar -R %s" % \
+ self.cmd.run("%s --noconfirm --noprogressbar -R %s" %
(self.pkgtool, " ".join(names)))
self.RefreshPackages()
self.extra = self.FindExtra()
diff --git a/src/lib/Bcfg2/Client/Tools/Portage.py b/src/lib/Bcfg2/Client/Tools/Portage.py
index 9381f44e9..6cbcff2e0 100644
--- a/src/lib/Bcfg2/Client/Tools/Portage.py
+++ b/src/lib/Bcfg2/Client/Tools/Portage.py
@@ -3,6 +3,7 @@
import re
import Bcfg2.Client.Tools
+
class Portage(Bcfg2.Client.Tools.PkgTool):
"""The Gentoo toolset implements package and service operations and
inherits the rest from Toolset.Toolset."""
@@ -35,9 +36,8 @@ class Portage(Bcfg2.Client.Tools.PkgTool):
if not self._initialised:
return
self.logger.info('Getting list of installed packages')
- cache = self.cmd.run("equery -q list '*'")[1]
self.installed = {}
- for pkg in cache:
+ 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)
@@ -73,12 +73,12 @@ class Portage(Bcfg2.Client.Tools.PkgTool):
self.logger.debug('Running equery check on %s' %
entry.get('name'))
- output = self.cmd.run("/usr/bin/equery -N check '=%s-%s' "
- "2>&1 | grep '!!!' | awk '{print $2}'"
- % ((entry.get('name'), version)))[1]
- if [filename for filename in output \
- if filename not in modlist]:
- return False
+ for line in self.cmd.run(["/usr/bin/equery", "-N", "check",
+ '=%s-%s' %
+ (entry.get('name'),
+ version)]).stdout.splitlines():
+ if '!!!' in line and line.split()[1] not in modlist:
+ return False
# By now the package must be in one of the following states:
# - Not require checking
diff --git a/src/lib/Bcfg2/Client/Tools/RPM.py b/src/lib/Bcfg2/Client/Tools/RPM.py
index 3d93149ff..e9dff3db5 100644
--- a/src/lib/Bcfg2/Client/Tools/RPM.py
+++ b/src/lib/Bcfg2/Client/Tools/RPM.py
@@ -26,8 +26,6 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
__new_gpg_ireq__ = {'Package': ['name'],
'Instance': ['version', 'release']}
- conflicts = ['RPMng']
-
pkgtype = 'rpm'
pkgtool = ("rpm --oldpackage --replacepkgs --quiet -U %s", ("%s", ["url"]))
@@ -80,13 +78,12 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
# Many, if not most package verifies can be caused by out of
# date prelinking.
if os.path.isfile('/usr/sbin/prelink') and not self.setup['dryrun']:
- cmdrc, output = self.cmd.run('/usr/sbin/prelink -a -mR')
- if cmdrc == 0:
+ rv = self.cmd.run('/usr/sbin/prelink -a -mR')
+ if rv.success:
self.logger.debug('Pre-emptive prelink succeeded')
else:
# FIXME : this is dumb - what if the output is huge?
- self.logger.error('Pre-emptive prelink failed: %s' % output)
-
+ self.logger.error('Pre-emptive prelink failed: %s' % rv.error)
def RefreshPackages(self):
"""
@@ -593,29 +590,26 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
# Fix installOnlyPackages
if len(install_only_pkgs) > 0:
self.logger.info("Attempting to install 'install only packages'")
- install_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
- inst.get('simplefile')) \
- for inst in install_only_pkgs])
- self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args)
- cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \
- install_args)
- if cmdrc == 0:
+ install_args = \
+ " ".join(os.path.join(self.instance_status[inst].get('pkg').get('uri'),
+ inst.get('simplefile'))
+ for inst in install_only_pkgs)
+ if self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs "
+ "%s" % install_args):
# The rpm command succeeded. All packages installed.
self.logger.info("Single Pass for InstallOnlyPkgs Succeded")
self.RefreshPackages()
-
else:
# The rpm command failed. No packages installed.
# Try installing instances individually.
self.logger.error("Single Pass for InstallOnlyPackages Failed")
installed_instances = []
for inst in install_only_pkgs:
- install_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
- inst.get('simplefile'))
- self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args)
- cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \
- install_args)
- if cmdrc == 0:
+ install_args = \
+ os.path.join(self.instance_status[inst].get('pkg').get('uri'),
+ inst.get('simplefile'))
+ if self.cmd.run("rpm --install --quiet --oldpackage "
+ "--replacepkgs %s" % install_args):
installed_instances.append(inst)
else:
self.logger.debug("InstallOnlyPackage %s %s would not install." % \
@@ -632,15 +626,15 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
self.logger.info("Installing GPG keys.")
key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
inst.get('simplefile'))
- cmdrc, output = self.cmd.run("rpm --import %s" % key_arg)
- if cmdrc != 0:
- self.logger.debug("Unable to install %s-%s" % \
- (self.instance_status[inst].get('pkg').get('name'), \
- self.str_evra(inst)))
+ if not self.cmd.run("rpm --import %s" % key_arg):
+ self.logger.debug("Unable to install %s-%s" %
+ (self.instance_status[inst].get('pkg').get('name'),
+ self.str_evra(inst)))
else:
- self.logger.debug("Installed %s-%s-%s" % \
- (self.instance_status[inst].get('pkg').get('name'), \
- inst.get('version'), inst.get('release')))
+ self.logger.debug("Installed %s-%s-%s" %
+ (self.instance_status[inst].get('pkg').get('name'),
+ inst.get('version'),
+ inst.get('release')))
self.RefreshPackages()
self.gpg_keyids = self.getinstalledgpg()
pkg = self.instance_status[gpg_keys[0]].get('pkg')
@@ -652,13 +646,12 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
upgrade_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
inst.get('simplefile')) \
for inst in upgrade_pkgs])
- cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \
- upgrade_args)
- if cmdrc == 0:
+ if self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs "
+ "%s" % upgrade_args):
# The rpm command succeeded. All packages upgraded.
self.logger.info("Single Pass for Upgraded Packages Succeded")
- upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \
- for inst in upgrade_pkgs])
+ upgrade_pkg_set = set([self.instance_status[inst].get('pkg')
+ for inst in upgrade_pkgs])
self.RefreshPackages()
else:
# The rpm command failed. No packages upgraded.
@@ -670,13 +663,13 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
inst.get('simplefile'))
#self.logger.debug("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \
# upgrade_args)
- cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % upgrade_args)
- if cmdrc == 0:
+ if self.cmd.run("rpm --upgrade --quiet --oldpackage "
+ "--replacepkgs %s" % upgrade_args):
upgraded_instances.append(inst)
else:
- self.logger.debug("Package %s %s would not upgrade." % \
- (self.instance_status[inst].get('pkg').get('name'), \
- self.str_evra(inst)))
+ self.logger.debug("Package %s %s would not upgrade." %
+ (self.instance_status[inst].get('pkg').get('name'),
+ self.str_evra(inst)))
upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \
for inst in upgrade_pkgs])
diff --git a/src/lib/Bcfg2/Client/Tools/RPMng.py b/src/lib/Bcfg2/Client/Tools/RPMng.py
deleted file mode 100644
index 0f0e4c700..000000000
--- a/src/lib/Bcfg2/Client/Tools/RPMng.py
+++ /dev/null
@@ -1,9 +0,0 @@
-""" RPM driver called 'RPMng' for backwards compat """
-
-from Bcfg2.Client.Tools.RPM import RPM
-
-
-class RPMng(RPM):
- """ RPM driver called 'RPMng' for backwards compat """
- deprecated = True
- name = "RPM"
diff --git a/src/lib/Bcfg2/Client/Tools/RcUpdate.py b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
index d5cef6e34..2e58f2564 100644
--- a/src/lib/Bcfg2/Client/Tools/RcUpdate.py
+++ b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
@@ -23,8 +23,7 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
# check if service is enabled
cmd = '/sbin/rc-update show default | grep %s'
- rv = self.cmd.run(cmd % entry.get('name'))[0]
- is_enabled = (rv == 0)
+ is_enabled = self.cmd.run(cmd % entry.get('name')).success
# check if init script exists
try:
@@ -36,8 +35,7 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
# check if service is enabled
cmd = '/etc/init.d/%s status | grep started'
- rv = self.cmd.run(cmd % entry.attrib['name'])[0]
- is_running = (rv == 0)
+ is_running = self.cmd.run(cmd % entry.attrib['name']).success
if entry.get('status') == 'on' and not (is_enabled and is_running):
entry.set('current_status', 'off')
@@ -60,27 +58,25 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
self.start_service(entry)
# make sure it's enabled
cmd = '/sbin/rc-update add %s default'
- rv = self.cmd.run(cmd % entry.get('name'))[0]
- return (rv == 0)
-
+ return self.cmd.run(cmd % entry.get('name')).success
elif entry.get('status') == 'off':
if entry.get('current_status') == 'on':
self.stop_service(entry)
# make sure it's disabled
cmd = '/sbin/rc-update del %s default'
- rv = self.cmd.run(cmd % entry.get('name'))[0]
- return (rv == 0)
+ return self.cmd.run(cmd % entry.get('name')).success
return False
def FindExtra(self):
"""Locate extra rc-update services."""
- cmd = '/bin/rc-status -s | grep started'
- allsrv = [line.split()[0] for line in self.cmd.run(cmd)[1]]
+ cmd = '/bin/rc-status -s'
+ allsrv = [line.split()[0]
+ for line in self.cmd.run(cmd).stdout.splitlines()
+ if 'started' in line]
self.logger.debug('Found active services:')
self.logger.debug(allsrv)
specified = [srv.get('name') for srv in self.getSupportedEntries()]
- return [Bcfg2.Client.XML.Element('Service',
- type='rc-update',
- name=name) \
+ return [Bcfg2.Client.XML.Element('Service', type='rc-update',
+ name=name)
for name in allsrv if name not in specified]
diff --git a/src/lib/Bcfg2/Client/Tools/SELinux.py b/src/lib/Bcfg2/Client/Tools/SELinux.py
index f2b868316..451495be2 100644
--- a/src/lib/Bcfg2/Client/Tools/SELinux.py
+++ b/src/lib/Bcfg2/Client/Tools/SELinux.py
@@ -12,7 +12,6 @@ import seobject
import Bcfg2.Client.XML
import Bcfg2.Client.Tools
from Bcfg2.Client.Tools.POSIX.File import POSIXFile
-from subprocess import Popen, PIPE
def pack128(int_val):
@@ -734,9 +733,7 @@ class SELinuxSemoduleHandler(SELinuxEntryHandler):
self._all = dict()
self.logger.debug("SELinux: Getting modules from semodule")
try:
- proc = Popen(['semodule', '-l'], stdout=PIPE, stderr=PIPE)
- out = proc.communicate()[0]
- rv = proc.wait()
+ rv = self.tool.cmd.run(['semodule', '-l'])
except OSError:
# semanage failed; probably not in $PATH. try to
# get the list of modules from the filesystem
@@ -745,13 +742,9 @@ class SELinuxSemoduleHandler(SELinuxEntryHandler):
err)
self._all.update(self._all_records_from_filesystem())
else:
- if rv:
- self.logger.error("SELinux: Failed to run semodule: %s"
- % err)
- self._all.update(self._all_records_from_filesystem())
- else:
+ if rv.success:
# ran semodule successfully
- for line in out.splitlines():
+ for line in rv.stdout.splitlines():
mod, version = line.split()
self._all[mod] = (version, 1)
@@ -759,6 +752,10 @@ class SELinuxSemoduleHandler(SELinuxEntryHandler):
for mod in self._all_records_from_filesystem().keys():
if mod not in self._all:
self._all[mod] = ('', 0)
+ else:
+ self.logger.error("SELinux: Failed to run semodule: %s"
+ % rv.error)
+ self._all.update(self._all_records_from_filesystem())
return self._all
def _all_records_from_filesystem(self):
@@ -870,26 +867,23 @@ class SELinuxSemoduleHandler(SELinuxEntryHandler):
self.logger.debug("Install SELinux module %s with semodule -i %s" %
(entry.get('name'), self._filepath(entry)))
try:
- proc = Popen(['semodule', '-i', self._filepath(entry)],
- stdout=PIPE, stderr=PIPE)
- err = proc.communicate()[1]
- rv = proc.wait()
+ rv = self.tool.cmd.run(['semodule', '-i', self._filepath(entry)])
except OSError:
err = sys.exc_info()[1]
self.logger.error("Failed to install SELinux module %s with "
"semodule: %s" % (entry.get("name"), err))
return False
- if rv:
- self.logger.error("Failed to install SELinux module %s with "
- "semodule: %s" % (entry.get("name"), err))
- return False
- else:
+ if rv.success:
if entry.get("disabled", "false").lower() == "true":
self.logger.warning("SELinux: Cannot disable modules with "
"semodule")
return False
else:
return True
+ else:
+ self.logger.error("Failed to install SELinux module %s with "
+ "semodule: %s" % (entry.get("name"), rv.error))
+ return False
def _addargs(self, entry):
""" argument list for adding entries """
diff --git a/src/lib/Bcfg2/Client/Tools/SMF.py b/src/lib/Bcfg2/Client/Tools/SMF.py
index 4409b40f3..68d8b2965 100644
--- a/src/lib/Bcfg2/Client/Tools/SMF.py
+++ b/src/lib/Bcfg2/Client/Tools/SMF.py
@@ -26,21 +26,20 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
def GetFMRI(self, entry):
"""Perform FMRI resolution for service."""
if not 'FMRI' in entry.attrib:
- name = self.cmd.run("/usr/bin/svcs -H -o FMRI %s 2>/dev/null" % \
- entry.get('name'))[1]
- if name:
- entry.set('FMRI', name[0])
- return True
+ rv = self.cmd.run(["/usr/bin/svcs", "-H", "-o", "FMRI",
+ entry.get('name')])
+ if rv.success:
+ entry.set('FMRI', rv.stdout.splitlines()[0])
else:
- self.logger.info('Failed to locate FMRI for service %s' % \
+ self.logger.info('Failed to locate FMRI for service %s' %
entry.get('name'))
- return False
+ return rv.success
return True
def VerifyService(self, entry, _):
"""Verify SMF Service entry."""
if not self.GetFMRI(entry):
- self.logger.error("smf service %s doesn't have FMRI set" % \
+ self.logger.error("smf service %s doesn't have FMRI set" %
entry.get('name'))
return False
if entry.get('FMRI').startswith('lrc'):
@@ -57,8 +56,9 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
(entry.get("FMRI")))
return entry.get('status') == 'off'
try:
- srvdata = self.cmd.run("/usr/bin/svcs -H -o STA %s" % \
- entry.get('FMRI'))[1][0].split()
+ srvdata = \
+ self.cmd.run("/usr/bin/svcs -H -o STA %s" %
+ entry.get('FMRI')).stdout.splitlines()[0].split()
except IndexError:
# Occurs when no lines are returned (service not installed)
return False
@@ -85,31 +85,30 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
(loc))
return False
else:
- cmdrc = self.cmd.run("/usr/sbin/svcadm disable %s" % \
- (entry.get('FMRI')))[0]
+ return self.cmd.run("/usr/sbin/svcadm disable %s" %
+ entry.get('FMRI')).success
+ elif entry.get('FMRI').startswith('lrc'):
+ loc = entry.get("FMRI")[4:].replace('_', '.')
+ try:
+ os.stat(loc.replace('/S', '/Disabled.'))
+ self.logger.debug("Renaming file %s to %s" %
+ (loc.replace('/S', '/DISABLED.S'), loc))
+ os.rename(loc.replace('/S', '/DISABLED.S'), loc)
+ return True
+ except OSError:
+ self.logger.debug("Failed to rename %s to %s" %
+ (loc.replace('/S', '/DISABLED.S'), loc))
+ return False
else:
- if entry.get('FMRI').startswith('lrc'):
- loc = entry.get("FMRI")[4:].replace('_', '.')
- try:
- os.stat(loc.replace('/S', '/Disabled.'))
- self.logger.debug("Renaming file %s to %s" % \
- (loc.replace('/S', '/DISABLED.S'), loc))
- os.rename(loc.replace('/S', '/DISABLED.S'), loc)
- cmdrc = 0
- except OSError:
- self.logger.debug("Failed to rename %s to %s" % \
- (loc.replace('/S', '/DISABLED.S'), loc))
- cmdrc = 1
+ srvdata = \
+ self.cmd.run("/usr/bin/svcs -H -o STA %s" %
+ entry.get('FMRI'))[1].splitlines()[0].split()
+ if srvdata[0] == 'MNT':
+ cmdarg = 'clear'
else:
- srvdata = self.cmd.run("/usr/bin/svcs -H -o STA %s" %
- entry.get('FMRI'))[1][0].split()
- if srvdata[0] == 'MNT':
- cmdarg = 'clear'
- else:
- cmdarg = 'enable'
- cmdrc = self.cmd.run("/usr/sbin/svcadm %s -r %s" % \
- (cmdarg, entry.get('FMRI')))[0]
- return cmdrc == 0
+ cmdarg = 'enable'
+ return self.cmd.run("/usr/sbin/svcadm %s -r %s" %
+ (cmdarg, entry.get('FMRI'))).success
def Remove(self, svcs):
"""Remove Extra SMF entries."""
@@ -120,12 +119,14 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
def FindExtra(self):
"""Find Extra SMF Services."""
allsrv = [name for name, version in \
- [srvc.split() for srvc in
- self.cmd.run("/usr/bin/svcs -a -H -o FMRI,STATE")[1]]
+ [srvc.split()
+ for srvc in self.cmd.run([
+ "/usr/bin/svcs", "-a", "-H",
+ "-o", "FMRI,STATE"]).stdout.splitlines()]
if version != 'disabled']
for svc in self.getSupportedEntries():
if svc.get("FMRI") in allsrv:
allsrv.remove(svc.get('FMRI'))
- return [Bcfg2.Client.XML.Element("Service", type='smf', name=name) \
+ return [Bcfg2.Client.XML.Element("Service", type='smf', name=name)
for name in allsrv]
diff --git a/src/lib/Bcfg2/Client/Tools/SYSV.py b/src/lib/Bcfg2/Client/Tools/SYSV.py
index 9b84a14cc..38072c52e 100644
--- a/src/lib/Bcfg2/Client/Tools/SYSV.py
+++ b/src/lib/Bcfg2/Client/Tools/SYSV.py
@@ -1,11 +1,11 @@
"""This provides bcfg2 support for Solaris SYSV packages."""
import tempfile
-
+from Bcfg2.Compat import any # pylint: disable=W0622
import Bcfg2.Client.Tools
import Bcfg2.Client.XML
-
+# pylint: disable=C0103
noask = '''
mail=
instance=overwrite
@@ -19,6 +19,7 @@ conflict=nocheck
action=nocheck
basedir=default
'''
+# pylint: enable=C0103
class SYSV(Bcfg2.Client.Tools.PkgTool):
@@ -42,14 +43,14 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
self.noaskfile.flush()
self.pkgtool = (self.pkgtool[0] % ("-a %s" % (self.noaskname)), \
self.pkgtool[1])
- except:
- self.pkgtool = (self.pkgtool[0] % (""), self.pkgtool[1])
+ except: # pylint: disable=W0702
+ self.pkgtool = (self.pkgtool[0] % "", self.pkgtool[1])
def RefreshPackages(self):
"""Refresh memory hashes of packages."""
self.installed = {}
# Build list of packages
- lines = self.cmd.run("/usr/bin/pkginfo -x")[1]
+ lines = self.cmd.run("/usr/bin/pkginfo -x").stdout.splitlines()
while lines:
# Splitting on whitespace means that packages with spaces in
# their version numbers don't work right. Found this with
@@ -62,35 +63,36 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
def VerifyPackage(self, entry, modlist):
"""Verify Package status for entry."""
- if not entry.get('version'):
- self.logger.info("Insufficient information of Package %s; cannot Verify" % entry.get('name'))
- return False
-
- desiredVersion = entry.get('version')
- if desiredVersion == 'any':
- desiredVersion = self.installed.get(entry.get('name'), desiredVersion)
-
- cmdrc = self.cmd.run("/usr/bin/pkginfo -q -v \"%s\" %s" % \
- (desiredVersion, entry.get('name')))[0]
+ desired_version = entry.get('version')
+ if desired_version == 'any':
+ desired_version = self.installed.get(entry.get('name'),
+ desired_version)
- if cmdrc != 0:
+ if not self.cmd.run(["/usr/bin/pkginfo", "-q", "-v",
+ desired_version, entry.get('name')]):
if entry.get('name') in self.installed:
- self.logger.debug("Package %s version incorrect: have %s want %s" \
- % (entry.get('name'), self.installed[entry.get('name')],
- desiredVersion))
+ self.logger.debug("Package %s version incorrect: "
+ "have %s want %s" %
+ (entry.get('name'),
+ self.installed[entry.get('name')],
+ desired_version))
else:
- self.logger.debug("Package %s not installed" % (entry.get("name")))
+ self.logger.debug("Package %s not installed" %
+ entry.get("name"))
else:
- if self.setup['quick'] or entry.attrib.get('verify', 'true') == 'false':
+ if (self.setup['quick'] or
+ entry.attrib.get('verify', 'true') == 'false'):
return True
- (vstat, odata) = self.cmd.run("/usr/sbin/pkgchk -n %s" % (entry.get('name')))
- if vstat == 0:
+ rv = self.cmd.run("/usr/sbin/pkgchk -n %s" % entry.get('name'))
+ if rv.success:
return True
else:
- output = [line for line in odata if line[:5] == 'ERROR']
- if len([name for name in output if name.split()[-1] not in modlist]):
- self.logger.debug("Package %s content verification failed" % \
- (entry.get('name')))
+ output = [line for line in rv.stdout.splitlines()
+ if line[:5] == 'ERROR']
+ if any(name for name in output
+ if name.split()[-1] not in modlist):
+ self.logger.debug("Package %s content verification failed"
+ % entry.get('name'))
else:
return True
return False
@@ -99,7 +101,7 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
"""Remove specified Sysv packages."""
names = [pkg.get('name') for pkg in packages]
self.logger.info("Removing packages: %s" % (names))
- self.cmd.run("/usr/sbin/pkgrm -a %s -n %s" % \
+ self.cmd.run("/usr/sbin/pkgrm -a %s -n %s" %
(self.noaskname, names))
self.RefreshPackages()
self.extra = self.FindExtra()
diff --git a/src/lib/Bcfg2/Client/Tools/Systemd.py b/src/lib/Bcfg2/Client/Tools/Systemd.py
index 43eca2eac..027d91c71 100644
--- a/src/lib/Bcfg2/Client/Tools/Systemd.py
+++ b/src/lib/Bcfg2/Client/Tools/Systemd.py
@@ -5,6 +5,7 @@
import Bcfg2.Client.Tools
import Bcfg2.Client.XML
+
class Systemd(Bcfg2.Client.Tools.SvcTool):
"""Systemd support for Bcfg2."""
name = 'Systemd'
@@ -21,35 +22,25 @@ class Systemd(Bcfg2.Client.Tools.SvcTool):
return True
cmd = "/bin/systemctl status %s.service " % (entry.get('name'))
- raw = ''.join(self.cmd.run(cmd)[1])
+ rv = self.cmd.run(cmd)
- if raw.find('Loaded: error') >= 0:
+ if 'Loaded: error' in rv.stdout:
entry.set('current_status', 'off')
- status = False
-
- elif raw.find('Active: active') >= 0:
+ return False
+ elif 'Active: active' in rv.stdout:
entry.set('current_status', 'on')
- if entry.get('status') == 'off':
- status = False
- else:
- status = True
-
+ return entry.get('status') == 'on'
else:
entry.set('current_status', 'off')
- if entry.get('status') == 'on':
- status = False
- else:
- status = True
-
- return status
+ return entry.get('status') == 'off'
def InstallService(self, entry):
"""Install Service entry."""
if entry.get('status') == 'on':
- rv = self.cmd.run(self.get_svc_command(entry, 'enable'))[0] == 0
- rv &= self.cmd.run(self.get_svc_command(entry, 'start'))[0] == 0
+ rv = self.cmd.run(self.get_svc_command(entry, 'enable')).success
+ rv &= self.cmd.run(self.get_svc_command(entry, 'start')).success
else:
- rv = self.cmd.run(self.get_svc_command(entry, 'stop'))[0] == 0
- rv &= self.cmd.run(self.get_svc_command(entry, 'disable'))[0] == 0
+ rv = self.cmd.run(self.get_svc_command(entry, 'stop')).success
+ rv &= self.cmd.run(self.get_svc_command(entry, 'disable')).success
return rv
diff --git a/src/lib/Bcfg2/Client/Tools/Upstart.py b/src/lib/Bcfg2/Client/Tools/Upstart.py
index 02ed52544..cd1c4a2bc 100644
--- a/src/lib/Bcfg2/Client/Tools/Upstart.py
+++ b/src/lib/Bcfg2/Client/Tools/Upstart.py
@@ -39,7 +39,8 @@ class Upstart(Bcfg2.Client.Tools.SvcTool):
try:
output = self.cmd.run('/usr/sbin/service %s status %s' %
- (entry.get('name'), params))[1][0]
+ (entry.get('name'),
+ params)).stdout.splitlines()[0]
except IndexError:
self.logger.error("Service %s not an Upstart service" %
entry.get('name'))
@@ -71,11 +72,10 @@ class Upstart(Bcfg2.Client.Tools.SvcTool):
def InstallService(self, entry):
"""Install Service for entry."""
if entry.get('status') == 'on':
- pstatus = self.cmd.run(self.get_svc_command(entry, 'start'))[0]
+ cmd = "start"
elif entry.get('status') == 'off':
- pstatus = self.cmd.run(self.get_svc_command(entry, 'stop'))[0]
- # pstatus is true if command failed
- return not pstatus
+ cmd = "stop"
+ return self.cmd.run(self.get_svc_command(entry, cmd)).success
def FindExtra(self):
"""Locate extra Upstart services."""
diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py
index 1fe275c2c..4539a6a36 100644
--- a/src/lib/Bcfg2/Client/Tools/YUM.py
+++ b/src/lib/Bcfg2/Client/Tools/YUM.py
@@ -126,7 +126,7 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
__req__ = {'Package': ['name'],
'Path': ['type']}
- conflicts = ['YUM24', 'RPM', 'RPMng', 'YUMng']
+ conflicts = ['RPM']
def __init__(self, logger, setup, config):
self.yumbase = self._loadYumBase(setup=setup, logger=logger)
diff --git a/src/lib/Bcfg2/Client/Tools/YUM24.py b/src/lib/Bcfg2/Client/Tools/YUM24.py
deleted file mode 100644
index cd25ecf37..000000000
--- a/src/lib/Bcfg2/Client/Tools/YUM24.py
+++ /dev/null
@@ -1,404 +0,0 @@
-"""This provides bcfg2 support for yum."""
-
-import copy
-import os.path
-import sys
-import yum
-import Bcfg2.Client.XML
-from Bcfg2.Client.Tools.RPM import RPM
-
-
-def build_yname(pkgname, inst):
- """Build yum appropriate package name."""
- ypname = pkgname
- if inst.get('version') != 'any':
- ypname += '-'
- if inst.get('epoch', False):
- ypname += "%s:" % inst.get('epoch')
- if inst.get('version', False) and inst.get('version') != 'any':
- ypname += "%s" % (inst.get('version'))
- if inst.get('release', False) and inst.get('release') != 'any':
- ypname += "-%s" % (inst.get('release'))
- if inst.get('arch', False) and inst.get('arch') != 'any':
- ypname += ".%s" % (inst.get('arch'))
- return ypname
-
-
-class YUM24(RPM):
- """Support for Yum packages."""
- pkgtype = 'yum'
- deprecated = True
- __execs__ = ['/usr/bin/yum', '/var/lib/rpm']
- __handles__ = [('Package', 'yum'),
- ('Package', 'rpm'),
- ('Path', 'ignore')]
-
- __req__ = {'Package': ['name', 'version']}
- __ireq__ = {'Package': ['name']}
- #__ireq__ = {'Package': ['name', 'version']}
-
- __new_req__ = {'Package': ['name'],
- 'Instance': ['version', 'release', 'arch']}
- __new_ireq__ = {'Package': ['name'], \
- 'Instance': []}
- #__new_ireq__ = {'Package': ['name', 'uri'], \
- # 'Instance': ['simplefile', 'version', 'release', 'arch']}
-
- __gpg_req__ = {'Package': ['name', 'version']}
- __gpg_ireq__ = {'Package': ['name', 'version']}
-
- __new_gpg_req__ = {'Package': ['name'],
- 'Instance': ['version', 'release']}
- __new_gpg_ireq__ = {'Package': ['name'],
- 'Instance': ['version', 'release']}
-
- def __init__(self, logger, setup, config):
- RPM.__init__(self, logger, setup, config)
- self.__important__ = self.__important__ + \
- [entry.get('name') for struct in config \
- for entry in struct \
- if entry.tag in ['Path', 'ConfigFile'] and \
- (entry.get('name').startswith('/etc/yum.d') \
- or entry.get('name').startswith('/etc/yum.repos.d')) \
- or entry.get('name') == '/etc/yum.conf']
- self.autodep = setup.get("yum24_autodep")
- self.yum_avail = dict()
- self.yum_installed = dict()
- self.yb = yum.YumBase()
- self.yb.doConfigSetup()
- self.yb.doTsSetup()
- self.yb.doRpmDBSetup()
- yup = self.yb.doPackageLists(pkgnarrow='updates')
- if hasattr(self.yb.rpmdb, 'pkglist'):
- yinst = self.yb.rpmdb.pkglist
- else:
- yinst = self.yb.rpmdb.getPkgList()
- for dest, source in [(self.yum_avail, yup.updates),
- (self.yum_installed, yinst)]:
- for pkg in source:
- if dest is self.yum_avail:
- pname = pkg.name
- data = {pkg.arch: (pkg.epoch, pkg.version, pkg.release)}
- else:
- pname = pkg[0]
- if pkg[1] is None:
- a = 'noarch'
- else:
- a = pkg[1]
- if pkg[2] is None:
- e = '0'
- else:
- e = pkg[2]
- data = {a: (e, pkg[3], pkg[4])}
- if pname in dest:
- dest[pname].update(data)
- else:
- dest[pname] = dict(data)
-
- def VerifyPackage(self, entry, modlist):
- pinned_version = None
- if entry.get('version', False) == 'auto':
- # old style entry; synthesize Instances from current installed
- if entry.get('name') not in self.yum_installed and \
- entry.get('name') not in self.yum_avail:
- # new entry; fall back to default
- entry.set('version', 'any')
- else:
- data = copy.copy(self.yum_installed[entry.get('name')])
- if entry.get('name') in self.yum_avail:
- # installed but out of date
- data.update(self.yum_avail[entry.get('name')])
- for (arch, (epoch, vers, rel)) in list(data.items()):
- x = Bcfg2.Client.XML.SubElement(entry, "Instance",
- name=entry.get('name'),
- version=vers, arch=arch,
- release=rel, epoch=epoch)
- if 'verify_flags' in entry.attrib:
- x.set('verify_flags', entry.get('verify_flags'))
- if 'verify' in entry.attrib:
- x.set('verify', entry.get('verify'))
-
- if entry.get('type', False) == 'yum':
- # Check for virtual provides or packages. If we don't have
- # this package use Yum to resolve it to a real package name
- knownPkgs = list(self.yum_installed.keys()) + list(self.yum_avail.keys())
- if entry.get('name') not in knownPkgs:
- # If the package name matches something installed
- # or available the that's the correct package.
- try:
- pkgDict = dict([(i.name, i) for i in \
- self.yb.returnPackagesByDep(entry.get('name'))])
- except yum.Errors.YumBaseError:
- e = sys.exc_info()[1]
- self.logger.error('Yum Error Depsolving for %s: %s' % \
- (entry.get('name'), str(e)))
- pkgDict = {}
-
- if len(pkgDict) > 1:
- # What do we do with multiple packages?
- s = "YUM24: returnPackagesByDep(%s) returned many packages"
- self.logger.info(s % entry.get('name'))
- s = "YUM24: matching packages: %s"
- self.logger.info(s % str(list(pkgDict.keys())))
- pkgs = set(pkgDict.keys()) & set(self.yum_installed.keys())
- if len(pkgs) > 0:
- # Virtual packages matches an installed real package
- pkg = pkgDict[pkgs.pop()]
- s = "YUM24: chosing: %s" % pkg.name
- self.logger.info(s)
- else:
- # What's the right package? This will fail verify
- # and Yum should Do The Right Thing on package install
- pkg = None
- elif len(pkgDict) == 1:
- pkg = list(pkgDict.values())[0]
- else: # len(pkgDict) == 0
- s = "YUM24: returnPackagesByDep(%s) returned no results"
- self.logger.info(s % entry.get('name'))
- pkg = None
-
- if pkg is not None:
- s = "YUM24: remapping virtual package %s to %s"
- self.logger.info(s % (entry.get('name'), pkg.name))
- entry.set('name', pkg.name)
-
- return RPM.VerifyPackage(self, entry, modlist)
-
- def Install(self, packages, states):
- """
- Try and fix everything that YUM24.VerifyPackages() found wrong for
- each Package Entry. This can result in individual RPMs being
- installed (for the first time), deleted, downgraded
- or upgraded.
-
- NOTE: YUM can not reinstall a package that it thinks is already
- installed.
-
- packages is a list of Package Elements that has
- states[<Package Element>] == False
-
- The following effects occur:
- - states{} is conditionally updated for each package.
- - self.installed{} is rebuilt, possibly multiple times.
- - self.instance_status{} is conditionally updated for each instance
- of a package.
- - Each package will be added to self.modified[] if its states{}
- entry is set to True.
-
- """
- self.logger.info('Running YUM24.Install()')
-
- install_pkgs = []
- gpg_keys = []
- upgrade_pkgs = []
-
- # Remove extra instances.
- # Can not reverify because we don't have a package entry.
- if len(self.extra_instances) > 0:
- if (self.setup.get('remove') == 'all' or \
- self.setup.get('remove') == 'packages'):
- self.Remove(self.extra_instances)
- else:
- self.logger.info("The following extra package instances will be removed by the '-r' option:")
- for pkg in self.extra_instances:
- for inst in pkg:
- self.logger.info(" %s %s" % \
- ((pkg.get('name'), self.str_evra(inst))))
-
- # Figure out which instances of the packages actually need something
- # doing to them and place in the appropriate work 'queue'.
- for pkg in packages:
- insts = [pinst for pinst in pkg \
- if pinst.tag in ['Instance', 'Package']]
- if insts:
- for inst in insts:
- if self.FixInstance(inst, self.instance_status[inst]):
- if self.instance_status[inst].get('installed', False) \
- == False:
- if pkg.get('name') == 'gpg-pubkey':
- gpg_keys.append(inst)
- else:
- install_pkgs.append(inst)
- elif self.instance_status[inst].get('version_fail', \
- False) == True:
- upgrade_pkgs.append(inst)
- else:
- install_pkgs.append(pkg)
-
- # Install GPG keys.
- # Alternatively specify the required keys using 'gpgkey' in the
- # repository definition in yum.conf. YUM will install the keys
- # automatically.
- if len(gpg_keys) > 0:
- for inst in gpg_keys:
- self.logger.info("Installing GPG keys.")
- if inst.get('simplefile') is None:
- self.logger.error("GPG key has no simplefile attribute")
- continue
- key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
- inst.get('simplefile'))
- cmdrc, output = self.cmd.run("rpm --import %s" % key_arg)
- if cmdrc != 0:
- self.logger.debug("Unable to install %s-%s" % \
- (self.instance_status[inst].get('pkg').get('name'), \
- self.str_evra(inst)))
- else:
- self.logger.debug("Installed %s-%s-%s" % \
- (self.instance_status[inst].get('pkg').get('name'), \
- inst.get('version'), inst.get('release')))
- self.RefreshPackages()
- self.gpg_keyids = self.getinstalledgpg()
- pkg = self.instance_status[gpg_keys[0]].get('pkg')
- states[pkg] = self.VerifyPackage(pkg, [])
-
- # Install packages.
- if len(install_pkgs) > 0:
- self.logger.info("Attempting to install packages")
-
- if self.autodep:
- pkgtool = "/usr/bin/yum -d0 -y install %s"
- else:
- pkgtool = "/usr/bin/yum -d0 install %s"
-
- install_args = []
- for inst in install_pkgs:
- pkg_arg = self.instance_status[inst].get('pkg').get('name')
- install_args.append(build_yname(pkg_arg, inst))
-
- cmdrc, output = self.cmd.run(pkgtool % " ".join(install_args))
- if cmdrc == 0:
- # The yum command succeeded. All packages installed.
- self.logger.info("Single Pass for Install Succeeded")
- self.RefreshPackages()
- else:
- # The yum command failed. No packages installed.
- # Try installing instances individually.
- self.logger.error("Single Pass Install of Packages Failed")
- installed_instances = []
- for inst in install_pkgs:
- pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst)
-
- cmdrc, output = self.cmd.run(pkgtool % pkg_arg)
- if cmdrc == 0:
- installed_instances.append(inst)
- else:
- self.logger.debug("%s %s would not install." % \
- (self.instance_status[inst].get('pkg').get('name'), \
- self.str_evra(inst)))
- self.RefreshPackages()
-
- # Fix upgradeable packages.
- if len(upgrade_pkgs) > 0:
- self.logger.info("Attempting to upgrade packages")
-
- if self.autodep:
- pkgtool = "/usr/bin/yum -d0 -y update %s"
- else:
- pkgtool = "/usr/bin/yum -d0 update %s"
-
- upgrade_args = []
- for inst in upgrade_pkgs:
- pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst)
- upgrade_args.append(pkg_arg)
-
- cmdrc, output = self.cmd.run(pkgtool % " ".join(upgrade_args))
- if cmdrc == 0:
- # The yum command succeeded. All packages installed.
- self.logger.info("Single Pass for Install Succeeded")
- self.RefreshPackages()
- else:
- # The yum command failed. No packages installed.
- # Try installing instances individually.
- self.logger.error("Single Pass Install of Packages Failed")
- installed_instances = []
- for inst in upgrade_pkgs:
- pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst)
- cmdrc, output = self.cmd.run(pkgtool % pkg_arg)
- if cmdrc == 0:
- installed_instances.append(inst)
- else:
- self.logger.debug("%s %s would not install." % \
- (self.instance_status[inst].get('pkg').get('name'), \
- self.str_evra(inst)))
-
- self.RefreshPackages()
-
- if not self.setup['kevlar']:
- for pkg_entry in [p for p in packages if self.canVerify(p)]:
- self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name')))
- states[pkg_entry] = self.VerifyPackage(pkg_entry, \
- self.modlists.get(pkg_entry, []))
-
- for entry in [ent for ent in packages if states[ent]]:
- self.modified.append(entry)
-
- def Remove(self, packages):
- """
- Remove specified entries.
-
- packages is a list of Package Entries with Instances generated
- by FindExtra().
- """
- self.logger.debug('Running YUM24.Remove()')
-
- if self.autodep:
- pkgtool = "/usr/bin/yum -d0 -y erase %s"
- else:
- pkgtool = "/usr/bin/yum -d0 erase %s"
-
- erase_args = []
- for pkg in packages:
- for inst in pkg:
- if pkg.get('name') != 'gpg-pubkey':
- pkg_arg = pkg.get('name') + '-'
- if inst.get('epoch', False):
- pkg_arg = pkg_arg + inst.get('epoch') + ':'
- pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release')
- if inst.get('arch', False):
- pkg_arg = pkg_arg + '.' + inst.get('arch')
- erase_args.append(pkg_arg)
- else:
- pkgspec = {'name': pkg.get('name'),
- 'version': inst.get('version'),
- 'release': inst.get('release')}
- self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
- % (pkgspec.get('name'), self.str_evra(pkgspec)))
- self.logger.info(" This package will be deleted in a future version of the YUM24 driver.")
-
- cmdrc, output = self.cmd.run(pkgtool % " ".join(erase_args))
- if cmdrc == 0:
- self.modified += packages
- for pkg in erase_args:
- self.logger.info("Deleted %s" % (pkg))
- else:
- self.logger.info("Bulk erase failed with errors:")
- self.logger.debug("Erase results = %s" % output)
- self.logger.info("Attempting individual erase for each package.")
- for pkg in packages:
- pkg_modified = False
- for inst in pkg:
- if pkg.get('name') != 'gpg-pubkey':
- pkg_arg = pkg.get('name') + '-'
- if 'epoch' in inst.attrib:
- pkg_arg = pkg_arg + inst.get('epoch') + ':'
- pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release')
- if 'arch' in inst.attrib:
- pkg_arg = pkg_arg + '.' + inst.get('arch')
- else:
- self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
- % (pkg.get('name'), self.str_evra(pkg)))
- self.logger.info(" This package will be deleted in a future version of the YUM24 driver.")
- continue
-
- cmdrc, output = self.cmd.run(self.pkgtool % pkg_arg)
- if cmdrc == 0:
- pkg_modified = True
- self.logger.info("Deleted %s" % pkg_arg)
- else:
- self.logger.error("unable to delete %s" % pkg_arg)
- self.logger.debug("Failure = %s" % output)
- if pkg_modified == True:
- self.modified.append(pkg)
-
- self.RefreshPackages()
- self.extra = self.FindExtra()
diff --git a/src/lib/Bcfg2/Client/Tools/YUMng.py b/src/lib/Bcfg2/Client/Tools/YUMng.py
deleted file mode 100644
index 22fbba537..000000000
--- a/src/lib/Bcfg2/Client/Tools/YUMng.py
+++ /dev/null
@@ -1,9 +0,0 @@
-""" YUM driver called 'YUMng' for backwards compat """
-
-from Bcfg2.Client.Tools.YUM import YUM
-
-
-class YUMng(YUM):
- """ YUM driver called 'YUMng' for backwards compat """
- deprecated = True
- conflicts = ['YUM24', 'RPM', 'RPMng']
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index 08dc09294..41759655d 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -3,9 +3,9 @@
import os
import sys
import stat
-from subprocess import Popen, PIPE
import Bcfg2.Client
import Bcfg2.Client.XML
+from Bcfg2.Utils import Executor, ClassName
from Bcfg2.Compat import walk_packages # pylint: disable=W0622
__all__ = [m[1] for m in walk_packages(path=__path__)]
@@ -25,48 +25,6 @@ class ToolInstantiationError(Exception):
pass
-class Executor:
- """ This class runs shell commands. """
-
- def __init__(self, logger):
- """
- :param logger: The logger to use to produce debug logging
- :type logger: logging.Logger
- """
- self.logger = logger
-
- def run(self, command):
- """ Run a command inside a shell.
-
- :param command: The command to run, given as a list as to
- :class:`subprocess.Popen`. Since the command
- will be run within a shell it is particularly
- important to pass it as a list.
- :type command: list
- :returns: tuple of return value (integer) and output (list of
- lines)
- """
- self.logger.debug("Running: %s" % command)
- proc = Popen(command, shell=True, bufsize=16384,
- stdin=PIPE, stdout=PIPE, close_fds=True)
- output = proc.communicate()[0].splitlines()
- for line in output:
- self.logger.debug('< %s' % line)
- return (proc.wait(), output)
-
-
-class ClassName(object):
- """ This very simple descriptor class exists only to get the name
- of the owner class. This is used because, for historical reasons,
- we expect every tool to have a ``name`` attribute that is in
- almost all cases the same as the ``__class__.__name__`` attribute
- of the plugin object. This makes that more dynamic so that each
- plugin isn't repeating its own name."""
-
- def __get__(self, inst, owner):
- return owner.__name__
-
-
class Tool(object):
""" The base tool class. All tools subclass this.
@@ -141,9 +99,9 @@ class Tool(object):
#: The XML configuration for this client
self.config = config
- #: An :class:`Bcfg2.Client.Tools.Executor` object for
+ #: An :class:`Bcfg2.Utils.Executor` object for
#: running external commands.
- self.cmd = Executor(logger)
+ self.cmd = Executor(timeout=self.setup['command_timeout'])
#: A list of entries that have been modified by this tool
self.modified = []
@@ -490,11 +448,7 @@ class PkgTool(Tool):
self.logger.info("Trying single pass package install for pkgtype %s" %
self.pkgtype)
- pkgcmd = self._get_package_command(packages)
- self.logger.debug("Running command: %s" % pkgcmd)
-
- cmdrc = self.cmd.run(pkgcmd)[0]
- if cmdrc == 0:
+ if self.cmd.run(self.pkgtool[0] % pkgargs):
self.logger.info("Single Pass Succeded")
# set all package states to true and flush workqueues
pkgnames = [pkg.get('name') for pkg in packages]
@@ -519,7 +473,7 @@ class PkgTool(Tool):
else:
self.logger.info("Installing pkg %s version %s" %
(pkg.get('name'), pkg.get('version')))
- if self.cmd.run(self._get_package_command([pkg]))[0] == 0:
+ if self.cmd.run(self._get_package_command([pkg])):
states[pkg] = True
else:
self.logger.error("Failed to install package %s" %
@@ -573,7 +527,7 @@ class SvcTool(Tool):
:class:`Bcfg2.Client.Tools.Executor.run`
"""
self.logger.debug('Starting service %s' % service.get('name'))
- return self.cmd.run(self.get_svc_command(service, 'start'))[0]
+ return self.cmd.run(self.get_svc_command(service, 'start'))
def stop_service(self, service):
""" Stop a service.
@@ -584,7 +538,7 @@ class SvcTool(Tool):
:class:`Bcfg2.Client.Tools.Executor.run`
"""
self.logger.debug('Stopping service %s' % service.get('name'))
- return self.cmd.run(self.get_svc_command(service, 'stop'))[0]
+ return self.cmd.run(self.get_svc_command(service, 'stop'))
def restart_service(self, service):
""" Restart a service.
@@ -596,7 +550,7 @@ class SvcTool(Tool):
"""
self.logger.debug('Restarting service %s' % service.get('name'))
restart_target = service.get('target', 'restart')
- return self.cmd.run(self.get_svc_command(service, restart_target))[0]
+ return self.cmd.run(self.get_svc_command(service, restart_target))
def check_service(self, service):
""" Check the status a service.
@@ -606,7 +560,7 @@ class SvcTool(Tool):
:returns: bool - True if the status command returned 0, False
otherwise
"""
- return self.cmd.run(self.get_svc_command(service, 'status'))[0] == 0
+ return self.cmd.run(self.get_svc_command(service, 'status'))
def Remove(self, services):
if self.setup['servicemode'] != 'disabled':
@@ -628,21 +582,21 @@ class SvcTool(Tool):
(restart == "interactive" and not self.setup['interactive'])):
continue
- rv = None
+ success = False
if entry.get('status') == 'on':
if self.setup['servicemode'] == 'build':
- rv = self.stop_service(entry)
+ success = self.stop_service(entry)
elif entry.get('name') not in self.restarted:
if self.setup['interactive']:
if not Bcfg2.Client.prompt('Restart service %s? (y/N) '
% entry.get('name')):
continue
- rv = self.restart_service(entry)
- if not rv:
+ success = self.restart_service(entry)
+ if success:
self.restarted.append(entry.get('name'))
else:
- rv = self.stop_service(entry)
- if rv:
+ success = self.stop_service(entry)
+ if not success:
self.logger.error("Failed to manipulate service %s" %
(entry.get('name')))
BundleUpdated.__doc__ = Tool.BundleUpdated.__doc__
diff --git a/src/lib/Bcfg2/Client/Tools/launchd.py b/src/lib/Bcfg2/Client/Tools/launchd.py
index 0a587da2e..b0661b26b 100644
--- a/src/lib/Bcfg2/Client/Tools/launchd.py
+++ b/src/lib/Bcfg2/Client/Tools/launchd.py
@@ -1,61 +1,58 @@
"""launchd support for Bcfg2."""
import os
-
import Bcfg2.Client.Tools
-class launchd(Bcfg2.Client.Tools.Tool):
- """Support for Mac OS X launchd services."""
+class launchd(Bcfg2.Client.Tools.Tool): # pylint: disable=C0103
+ """Support for Mac OS X launchd services. Currently requires the
+ path to the plist to load/unload, and Name is acually a
+ reverse-fqdn (or the label)."""
__handles__ = [('Service', 'launchd')]
__execs__ = ['/bin/launchctl', '/usr/bin/defaults']
- name = 'launchd'
__req__ = {'Service': ['name', 'status']}
- '''
- Currently requires the path to the plist to load/unload,
- and Name is acually a reverse-fqdn (or the label).
- '''
-
def __init__(self, logger, setup, config):
Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config)
- '''Locate plist file that provides given reverse-fqdn name
- /Library/LaunchAgents Per-user agents provided by the administrator.
- /Library/LaunchDaemons System wide daemons provided by the administrator.
- /System/Library/LaunchAgents Mac OS X Per-user agents.
- /System/Library/LaunchDaemons Mac OS X System wide daemons.'''
- plistLocations = ["/Library/LaunchDaemons",
- "/System/Library/LaunchDaemons"]
- self.plistMapping = {}
- for directory in plistLocations:
+ # Locate plist file that provides given reverse-fqdn name:
+ #
+ # * ``/Library/LaunchAgents``: Per-user agents provided by the
+ # administrator.
+ # * ``/Library/LaunchDaemons``: System-wide daemons provided
+ # by the administrator.
+ # * ``/System/Library/LaunchAgents``: Mac OS X per-user
+ # agents.
+ # * ``/System/Library/LaunchDaemons``: Mac OS X system-wide
+ # daemons.
+ plist_locations = ["/Library/LaunchDaemons",
+ "/System/Library/LaunchDaemons"]
+ self.plist_mapping = {}
+ for directory in plist_locations:
for daemon in os.listdir(directory):
- try:
- if daemon.endswith(".plist"):
- d = daemon[:-6]
- else:
- d = daemon
- label = self.cmd.run('defaults read %s/%s Label' %
- (directory, d))[1][0]
- self.plistMapping[label] = "%s/%s" % (directory, daemon)
- except KeyError:
- self.logger.warning("Could not get label from %s/%s" %
- (directory, daemon))
+ if daemon.endswith(".plist"):
+ daemon = daemon[:-6]
+ dpath = os.path.join(directory, daemon)
+ rv = self.cmd.run(['defaults', 'read', dpath, 'Label'])
+ if rv.success:
+ label = rv.stdout.splitlines()[0]
+ self.plist_mapping[label] = dpath
+ else:
+ self.logger.warning("Could not get label from %s" % dpath)
def FindPlist(self, entry):
- return self.plistMapping.get(entry.get('name'), None)
+ """ Find the location of the plist file for the given entry """
+ return self.plist_mapping.get(entry.get('name'), None)
def os_version(self):
- version = ""
- try:
- vers = self.cmd.run('sw_vers')[1]
- except:
- return version
-
- for line in vers:
- if line.startswith("ProductVersion"):
- version = line.split()[-1]
- return version
+ """ Determine the OS version """
+ rv = self.cmd.run('sw_vers')
+ if rv:
+ for line in rv.stdout.splitlines():
+ if line.startswith("ProductVersion"):
+ return line.split()[-1]
+ else:
+ return ''
def VerifyService(self, entry, _):
"""Verify launchd service entry."""
@@ -63,7 +60,7 @@ class launchd(Bcfg2.Client.Tools.Tool):
return True
try:
- services = self.cmd.run("/bin/launchctl list")[1]
+ services = self.cmd.run("/bin/launchctl list").stdout.splitlines()
except IndexError:
# happens when no services are running (should be never)
services = []
@@ -93,15 +90,13 @@ class launchd(Bcfg2.Client.Tools.Tool):
name = entry.get('name')
if entry.get('status') == 'on':
self.logger.error("Installing service %s" % name)
- cmdrc = self.cmd.run("/bin/launchctl load -w %s" %
- self.FindPlist(entry))
- cmdrc = self.cmd.run("/bin/launchctl start %s" % name)
+ self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry))
+ return self.cmd.run("/bin/launchctl start %s" % name).success
else:
self.logger.error("Uninstalling service %s" % name)
- cmdrc = self.cmd.run("/bin/launchctl stop %s" % name)
- cmdrc = self.cmd.run("/bin/launchctl unload -w %s" %
- self.FindPlist(entry))
- return cmdrc[0] == 0
+ self.cmd.run("/bin/launchctl stop %s" % name)
+ return self.cmd.run("/bin/launchctl unload -w %s" %
+ self.FindPlist(entry)).success
def Remove(self, svcs):
"""Remove Extra launchd entries."""
@@ -110,23 +105,24 @@ class launchd(Bcfg2.Client.Tools.Tool):
def FindExtra(self):
"""Find Extra launchd services."""
try:
- allsrv = self.cmd.run("/bin/launchctl list")[1]
+ allsrv = self.cmd.run("/bin/launchctl list").stdout.splitlines()
except IndexError:
allsrv = []
- [allsrv.remove(svc) for svc in [entry.get("name") for entry
- in self.getSupportedEntries()] if svc in allsrv]
- return [Bcfg2.Client.XML.Element("Service",
- type='launchd',
- name=name,
- status='on') for name in allsrv]
+ for entry in self.getSupportedEntries():
+ svc = entry.get("name")
+ if svc in allsrv:
+ allsrv.remove(svc)
+ return [Bcfg2.Client.XML.Element("Service", type='launchd', name=name,
+ status='on')
+ for name in allsrv]
def BundleUpdated(self, bundle, states):
"""Reload launchd plist."""
for entry in [entry for entry in bundle if self.handlesEntry(entry)]:
if not self.canInstall(entry):
- self.logger.error("Insufficient information to restart service %s" %
- (entry.get('name')))
+ self.logger.error("Insufficient information to restart "
+ "service %s" % entry.get('name'))
else:
name = entry.get('name')
if entry.get('status') == 'on' and self.FindPlist(entry):