diff options
Diffstat (limited to 'src/lib/Bcfg2/Client/Tools')
24 files changed, 306 insertions, 911 deletions
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): |