summaryrefslogtreecommitdiffstats
path: root/src/lib/Client
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Client')
-rw-r--r--src/lib/Client/Debian.py49
-rw-r--r--src/lib/Client/Gentoo.py2
-rw-r--r--src/lib/Client/Redhat.py11
-rw-r--r--src/lib/Client/Solaris.py1
-rw-r--r--src/lib/Client/Toolset.py143
5 files changed, 148 insertions, 58 deletions
diff --git a/src/lib/Client/Debian.py b/src/lib/Client/Debian.py
index 35b398ffd..9260a2afe 100644
--- a/src/lib/Client/Debian.py
+++ b/src/lib/Client/Debian.py
@@ -7,32 +7,35 @@ from re import compile as regcompile
import apt_pkg
-from Bcfg2.Client.Toolset import Toolset, saferun
+from Bcfg2.Client.Toolset import Toolset
class ToolsetImpl(Toolset):
'''The Debian toolset implements package and service operations and inherits
the rest from Toolset.Toolset'''
+ __name__ = 'Debian'
__important__ = ["/etc/apt/sources.list", "/var/cache/debconf/config.dat", \
"/var/cache/debconf/templates.dat", '/etc/passwd', '/etc/group', \
'/etc/apt/apt.conf']
- pkgtool = {'deb':('DEBIAN_FRONTEND=noninteractive apt-get --reinstall -q=2 --force-yes -y install %s >/dev/null 2>&1',
+ pkgtool = {'deb':('DEBIAN_FRONTEND=noninteractive apt-get --reinstall -q=2 --force-yes -y install %s',
('%s=%s', ['name', 'version']))}
svcre = regcompile("/etc/.*/[SK]\d\d(?P<name>\S+)")
def __init__(self, cfg, setup):
Toolset.__init__(self, cfg, setup)
self.cfg = cfg
+ self.CondPrint('debug', 'Configuring Debian toolset')
environ["DEBIAN_FRONTEND"] = 'noninteractive'
- system("dpkg --force-confold --configure -a > /dev/null 2>&1")
+ self.saferun("dpkg --force-confold --configure -a")
if not self.setup['build']:
- system("dpkg-reconfigure -f noninteractive debconf < /dev/null > /dev/null 2>&1")
- system("apt-get clean > /dev/null 2>&1")
- system("apt-get -q=2 -y update > /dev/null 2>&1")
+ self.saferun("/usr/sbin/dpkg-reconfigure -f noninteractive debconf < /dev/null")
+ self.saferun("apt-get clean")
+ self.saferun("apt-get -q=2 -y update")
self.installed = {}
self.pkgwork = {'add':[], 'update':[], 'remove':[]}
for pkg in [cpkg for cpkg in self.cfg.findall(".//Package") if not cpkg.attrib.has_key('type')]:
pkg.set('type', 'deb')
self.Refresh()
+ self.CondPrint('debug', 'Done configuring Debian toolset')
def Refresh(self):
'''Refresh memory hashes of packages'''
@@ -44,7 +47,6 @@ class ToolsetImpl(Toolset):
self.installed[pkg.Name] = pkg.CurrentVer.VerStr
# implement entry (Verify|Install) ops
-
def VerifyService(self, entry):
'''Verify Service status for entry'''
rawfiles = glob("/etc/rc*.d/*%s" % (entry.get('name')))
@@ -74,24 +76,27 @@ class ToolsetImpl(Toolset):
if self.setup['dryrun']:
print "Disabling service %s" % (entry.get('name'))
else:
- system("/etc/init.d/%s stop > /dev/null 2>&1" % (entry.get('name')))
- cmdrc = system("/usr/sbin/update-rc.d -f %s remove > /dev/null 2>&1" %
- entry.get('name'))
+ self.saferun("/etc/init.d/%s stop" % (entry.get('name')))
+ cmdrc = self.saferun("/usr/sbin/update-rc.d -f %s remove" % entry.get('name'))[0]
else:
if self.setup['dryrun']:
print "Enabling service %s" % (entry.attrib['name'])
else:
- cmdrc = system("/usr/sbin/update-rc.d %s defaults > /dev/null 2>&1" % (entry.attrib['name']))
+ cmdrc = self.saferun("/usr/sbin/update-rc.d %s defaults" % (entry.attrib['name']))[0]
if cmdrc:
return False
return True
def VerifyPackage(self, entry, modlist):
- '''Verify Package for entry'''
+ '''Verify package for entry'''
+ if not entry.attrib.has_key('version'):
+ self.CondPrint('verbose', "Cannot verify unversioned package %s" %
+ (entry.attrib['name']))
+ return False
if self.installed.has_key(entry.attrib['name']):
if self.installed[entry.attrib['name']] == entry.attrib['version']:
if not self.setup['quick']:
- output = saferun("debsums -s %s" % entry.get('name'))[1]
+ output = self.saferun("/usr/bin/debsums -s %s" % entry.get('name'))[1]
if [filename for filename in output if filename not in modlist]:
return False
return True
@@ -103,7 +108,7 @@ class ToolsetImpl(Toolset):
allsrv = []
[allsrv.append(self.svcre.match(fname).group('name')) for fname in
glob("/etc/rc[12345].d/S*") if self.svcre.match(fname).group('name') not in allsrv]
- self.CondPrint('debug', "Found active services: %s" % allsrv)
+ self.CondDisplayList('debug', "Found active services:", allsrv)
csrv = self.cfg.findall(".//Service")
[allsrv.remove(svc.get('name')) for svc in csrv if
svc.get('status') == 'on' and svc.get('name') in allsrv]
@@ -111,18 +116,22 @@ class ToolsetImpl(Toolset):
def HandleExtra(self):
'''Deal with extra configuration detected'''
+ if self.setup['dryrun']:
+ return
+
if len(self.pkgwork) > 0:
if self.setup['remove'] in ['all', 'packages']:
- self.CondPrint('verbose', "Removing packages: %s" % self.pkgwork['remove'])
- if not system("apt-get remove %s" % " ".join(self.pkgwork['remove'])):
+ self.CondDisplayList('verbose', "Removing packages", self.pkgwork['remove'])
+ if not self.saferun("apt-get remove %s" % " ".join(self.pkgwork['remove']))[0]:
self.pkgwork['remove'] = []
else:
- self.CondPrint('verbose', "Need to remove packages: %s" % self.pkgwork['remove'])
+ self.CondDisplayList('verbose', "Need to remove packages:", self.pkgwork['remove'])
+
if len(self.extra_services) > 0:
if self.setup['remove'] in ['all', 'services']:
- self.CondPrint('verbose', "Removing services: %s" % self.extra_services)
+ self.CondDisplayList('verbose', "Removing services:", self.extra_services)
[self.extra_services.remove(serv) for serv in self.extra_services if
- not system("rm -f /etc/rc*.d/S??%s" % serv)]
+ not self.saferun("rm -f /etc/rc*.d/S??%s" % serv)[0]]
else:
- self.CondPrint('verbose', "Need to remove services: %s" % self.extra_services)
+ self.CondDisplayList('verbose', "Need to remove services:", self.extra_services)
diff --git a/src/lib/Client/Gentoo.py b/src/lib/Client/Gentoo.py
index 1bd698a4a..84ca313c5 100644
--- a/src/lib/Client/Gentoo.py
+++ b/src/lib/Client/Gentoo.py
@@ -1,5 +1,5 @@
'''This provides bcfg2 support for Gentoo'''
-__revision__ = '$Revision: $'
+__revision__ = '$Revision$'
from os import popen, system, stat
from popen2 import Popen4
diff --git a/src/lib/Client/Redhat.py b/src/lib/Client/Redhat.py
index 57f1a17ba..6c9d0d7c9 100644
--- a/src/lib/Client/Redhat.py
+++ b/src/lib/Client/Redhat.py
@@ -1,5 +1,5 @@
# This is the bcfg2 support for redhat
-# $Id: $
+# $Id$
'''This is redhat client support'''
__revision__ = '$Revision$'
@@ -10,6 +10,7 @@ from Bcfg2.Client.Toolset import Toolset, saferun
class ToolsetImpl(Toolset):
'''This class implelements support for rpm packages and standard chkconfig services'''
+ __name__ = 'Redhat'
pkgtool = {'rpm':("rpm --oldpackage --replacepkgs --quiet -U %s", ("%s", ["url"]))}
def __init__(self, cfg, setup):
@@ -111,21 +112,21 @@ class ToolsetImpl(Toolset):
self.pkgwork['remove'] = []
self.Inventory()
else:
- self.CondPrint('verbose', "Need to remove packages: %s" % self.pkgwork['remove'])
+ self.CondDisplayList('verbose', "Need to remove packages", self.pkgwork['remove'])
if len(self.extra_services) > 0:
if self.setup['remove'] in ['all', 'services']:
- self.CondPrint('verbose', "Removing services: %s" % self.extra_services)
+ self.CondDisplayList('verbose', 'Removing services:', self.extra_services)
for service in self.extra_services:
if not system("/sbin/chkconfig %s off" % service):
self.extra_services.remove(service)
else:
- self.CondPrint('verbose', "Need to remove services: %s" % self.extra_services)
+ self.CondDisplayList('verbose', 'Need to remove services:', self.extra_services)
def Inventory(self):
'''Do standard inventory plus debian extra service check'''
Toolset.Inventory(self)
allsrv = [line.split()[0] for line in popen("/sbin/chkconfig --list|grep :on").readlines()]
- self.CondPrint('debug', "Found active services: %s" % allsrv)
+ self.CondDisplayList('debug', 'Found active services:', allsrv)
csrv = self.cfg.findall(".//Service")
[allsrv.remove(svc.get('name')) for svc in csrv if
svc.get('status') == 'on' and svc.get('name') in allsrv]
diff --git a/src/lib/Client/Solaris.py b/src/lib/Client/Solaris.py
index a02df4cce..8ab506aa9 100644
--- a/src/lib/Client/Solaris.py
+++ b/src/lib/Client/Solaris.py
@@ -31,6 +31,7 @@ class ToolsetImpl(Toolset):
'encap':("/local/sbin/epkg -l -q %s", ("%s", ["url"]))}
splitter = regcompile('.*/(?P<name>[\w-]+)\-(?P<version>[\w\.-]+)')
ptypes = {}
+ __name__ = 'Solaris'
def __init__(self, cfg, setup):
Toolset.__init__(self, cfg, setup)
diff --git a/src/lib/Client/Toolset.py b/src/lib/Client/Toolset.py
index 32deea99b..7c2da5f14 100644
--- a/src/lib/Client/Toolset.py
+++ b/src/lib/Client/Toolset.py
@@ -11,10 +11,10 @@ from stat import S_IWGRP, S_IRGRP, S_IXOTH, S_IWOTH, S_IROTH, ST_MODE, S_ISDIR
from stat import S_IFREG, ST_UID, ST_GID, S_ISREG, S_IFDIR, S_ISLNK
from sys import exc_info
import stat as statmod
+from math import floor, ceil
#from time import asctime, localtime
from traceback import extract_tb
from popen2 import Popen4
-
from lxml.etree import Element, SubElement, tostring
def calc_perms(initial, perms):
@@ -31,19 +31,10 @@ def calc_perms(initial, perms):
tempperms |= perm
return tempperms
-def saferun(command):
- '''Run a command in a pipe dealing with stdout buffer overloads'''
- runpipe = Popen4(command, bufsize=16384)
- output = runpipe.fromchild.read()
- cmdstat = runpipe.poll()
- while cmdstat == -1:
- output += runpipe.fromchild.read()
- cmdstat = runpipe.poll()
- return (cmdstat, [line for line in output.split('\n') if line])
-
class Toolset(object):
'''The toolset class contains underlying command support and all states'''
__important__ = []
+ __name__ = 'Toolset'
pkgtool = ('echo', ('%s', ['name']))
def __init__(self, cfg, setup):
@@ -57,20 +48,97 @@ class Toolset(object):
self.installed = {}
self.pkgwork = {'add':[], 'update':[], 'remove':[]}
self.extra_services = []
+ (self.height, self.width) = self.get_height_width()
if self.__important__:
for cfile in [cfl for cfl in cfg.findall(".//ConfigFile") if cfl.get('name') in self.__important__]:
self.VerifyEntry(cfile)
if not self.states[cfile]:
self.InstallConfigFile(cfile)
+ def saferun(self, command):
+ '''Run a command in a pipe dealing with stdout buffer overloads'''
+ self.CondPrint('debug', '> %s' % command)
+
+ runpipe = Popen4(command, bufsize=16384)
+ output = runpipe.fromchild.read()
+ if len(output) > 0:
+ self.CondPrint('debug', '< %s' % output)
+ cmdstat = runpipe.poll()
+ while cmdstat == -1:
+ moreOutput = runpipe.fromchild.read()
+ if len(moreOutput) > 0:
+ self.CondPrint('debug', '< %s' % moreOutput)
+ output += moreOutput
+ cmdstat = runpipe.poll()
+
+ return (cmdstat, [line for line in output.split('\n') if line])
+
def CondPrint(self, state, msg):
'''Conditionally print message'''
if self.setup[state]:
try:
- print msg
+ prefix = "%s[%s]: " % (self.__name__, state)
+ line_len = self.width-len(prefix)
+ for line in msg.split('\n'):
+ inner_lines = int(floor(float(len(line)) / line_len))+1
+ for i in xrange(inner_lines):
+ print "%s%s" % (prefix, line[i*line_len:(i+1)*line_len])
except IOError:
pass
+ def get_height_width(self):
+ try:
+ import termios, struct, fcntl
+ height, width = struct.unpack('hhhh',
+ fcntl.ioctl(0, termios.TIOCGWINSZ,
+ "\000"*8))[0:2]
+ return height, width
+ except:
+ return 25, 80
+
+ def FormattedCondPrint(self, state, items):
+ items.sort()
+ screenWidth = self.width - len("%s[%s]:" % (self.__name__, state))
+ columnWidth = 1
+ for item in items:
+ if len(item) > columnWidth:
+ columnWidth = len(item)
+ columnWidth += 1
+
+ columns = int(floor(float(screenWidth) / columnWidth))
+ lines = int(ceil(float(len(items)) / columns))
+
+ for lineNumber in xrange(lines):
+ lineItems = []
+ for columnNumber in xrange(columns):
+ itemNumber = int(columnNumber*lines + lineNumber)
+ if itemNumber < len(items):
+ lineItems.append(items[itemNumber])
+ format = "%%-%ss" % columnWidth
+ lineText = "".join([format % item for item in lineItems])
+ self.CondPrint(state, lineText.rstrip())
+
+ def CondDisplayList(self, state, title, items):
+ self.CondPrint(state, title)
+ self.FormattedCondPrint(state, items)
+ self.CondPrint(state, '')
+
+ def CondDisplayState(self, state, phase):
+ self.CondPrint(state, 'Phase: %s' % phase)
+ self.CondPrint(state, 'Correct entries:\t%d'
+ % self.states.values().count(True))
+ self.CondPrint(state, 'Incorrect entries:\t%d' %
+ self.states.values().count(False))
+ self.CondPrint(state, 'Total managed entries:\t%d' %
+ len(self.states.values()))
+ self.CondPrint(state, 'Unmanaged entries:\t%d' %
+ len(self.pkgwork['remove']))
+ self.CondPrint(state, '')
+
+ if ((self.states.values().count(False) > 0) and
+ not self.pkgwork['remove']):
+ self.CondPrint('All entries correct.')
+
def LogFailure(self, area, entry):
'''Print tracebacks in unexpected cases'''
print "Failure in %s for entry: %s" % (area, tostring(entry))
@@ -229,7 +297,7 @@ class Toolset(object):
unlink(entry.get('name'))
elif S_ISDIR(fmode):
self.CondPrint('debug', "Directory entry already exists at %s" % (entry.get('name')))
- system("mv %s/ %s.bak" % (entry.get('name'), entry.get('name')))
+ self.saferun("mv %s/ %s.bak" % (entry.get('name'), entry.get('name')))
else:
unlink(entry.get('name'))
except OSError:
@@ -386,7 +454,7 @@ class Toolset(object):
chown(newfile.name, 0, 0)
chmod(newfile.name, calc_perms(S_IFREG, entry.get('perms')))
if entry.get("paranoid", False) and self.setup.get("paranoid", False):
- system("cp %s /var/cache/bcfg2/%s" % (entry.get('name')))
+ self.saferun("cp %s /var/cache/bcfg2/%s" % (entry.get('name')))
rename(newfile.name, entry.get('name'))
return True
except (OSError, IOError), err:
@@ -413,9 +481,10 @@ class Toolset(object):
perms = oct(sinfo[ST_MODE])[-4:]
if perms == entry.get('perms'):
return True
- else:
- self.CondPrint('debug', "Entry %s permissions incorrect" % entry.get('name'))
+ self.CondPrint('debug', "Entry %s permissions incorrect" % entry.get('name'))
+ return False
+
def InstallPermissions(self, entry):
'''Install method for abstract permission'''
try:
@@ -455,15 +524,15 @@ class Toolset(object):
self.CondPrint('debug', "Re-checked entry %s %s: %s" %
(child.tag, child.get('name'), self.states[child]))
for postinst in [entry for entry in bchildren if entry.tag == 'PostInstall']:
- system(postinst.get('name'))
+ self.saferun(postinst.get('name'))
for svc in [svc for svc in bchildren if svc.tag == 'Service' and
svc.get('status', 'off') == 'on']:
if self.setup['build']:
# stop services in miniroot
- system('/etc/init.d/%s stop' % svc.get('name'))
+ self.saferun('/etc/init.d/%s stop' % svc.get('name'))
else:
self.CondPrint('debug', 'Restarting service %s' % svc.get('name'))
- system('/etc/init.d/%s %s' % (svc.get('name'), svc.get('reload', 'reload')))
+ self.saferun('/etc/init.d/%s %s' % (svc.get('name'), svc.get('reload', 'reload')))
for entry in self.structures:
if [strent for strent in entry.getchildren() if not self.states.get(strent, False)]:
@@ -477,18 +546,27 @@ class Toolset(object):
def Install(self):
'''Correct detected misconfigurations'''
- self.CondPrint("verbose", "Installing needed configuration changes")
+ if self.setup['dryrun']:
+ self.CondPrint("verbose", "Dry-run mode: no changes will be made")
+ else:
+ self.CondPrint("verbose", "Updating the system")
+ self.CondPrint("verbose", "")
self.HandleExtra()
# use quick package ops from here on
self.setup['quick'] = True
- self.CondPrint('dryrun', "Packages to update: %s" %
- (" ".join([pkg.get('name') for pkg in self.pkgwork['update']])))
- self.CondPrint('dryrun', "Packages to add: %s" %
- (" ".join([pkg.get('name') for pkg in self.pkgwork['add']])))
- self.CondPrint('dryrun', "Packages to remove %s" % (" ".join(self.pkgwork['remove'])))
- for entry in [entry for entry in self.states if (not self.states[entry]
- and (entry.tag != 'Package'))]:
- self.CondPrint('dryrun', "Entry %s %s updated" % (entry.tag, entry.get('name')))
+
+ self.CondDisplayList('dryrun', "Packages to update:",
+ [pkg.get('name') for pkg in self.pkgwork['update']])
+ self.CondDisplayList('dryrun', "Packages to add:",
+ [pkg.get('name') for pkg in self.pkgwork['add']])
+ self.CondDisplayList('dryrun', "Packages to remove:",
+ self.pkgwork['remove'])
+ self.CondDisplayList('dryrun', "Entries to update:",
+ ["%s: %s" % (entry.tag, entry.get('name'))
+ for entry in self.states if not (self.states[entry]
+ or entry.tag == 'Package')])
+ self.CondDisplayList('dryrun', "Services to remove:", self.extra_services)
+
if self.setup['dryrun']:
return
@@ -518,7 +596,7 @@ class Toolset(object):
count = count + 1
old = left
- self.CondPrint("verbose", "Installing Non Package entries")
+ self.CondPrint("verbose", "Installing non-package entries")
[self.InstallEntry(ent) for ent in work if ent.tag != 'Package']
packages = [pkg for pkg in work if pkg.tag == 'Package']
@@ -545,7 +623,7 @@ class Toolset(object):
self.CondPrint("debug", "Installing packages: :%s:" % pkgargs)
self.CondPrint("debug", "Running command ::%s::" % (pkgtool[0] % pkgargs))
- cmdrc = system(pkgtool[0] % pkgargs)
+ (cmdrc, cmdoutput) = self.saferun(pkgtool[0] % pkgargs)
if cmdrc == 0:
self.CondPrint('verbose', "Single Pass Succeded")
@@ -569,8 +647,9 @@ class Toolset(object):
else:
self.CondPrint("verbose", "Installing pkg %s version %s" %
(pkg.get('name'), pkg.get('version')))
- cmdrc = system(pkgtool[0] %
- (pkgtool[1][0]%tuple([pkg.get(field) for field in pkgtool[1][1]])))
+ (cmdrc, cmdoutput) = self.saferun(pkgtool[0] %
+ (pkgtool[1][0] %
+ tuple([pkg.get(field) for field in pkgtool[1][1]])))
if cmdrc == 0:
self.states[pkg] = True
else: