From 8a70dbabd08308403ad8296979f50c379c707df9 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Mon, 18 Oct 2010 11:43:37 -0500 Subject: POSIX: Remove client-side support for old POSIX types Signed-off-by: Sol Jerome --- schemas/rules.xsd | 26 -- src/lib/Client/Frame.py | 14 - src/lib/Client/Tools/APT.py | 2 +- src/lib/Client/Tools/POSIX.py | 636 ++++++++++++++++++---------------- src/lib/Client/Tools/YUMng.py | 2 +- src/lib/Client/Tools/__init__.py | 3 +- src/lib/Server/Admin/Bundle.py | 2 +- src/lib/Server/Plugins/POSIXCompat.py | 38 -- src/lib/Server/Plugins/__init__.py | 1 - src/sbin/bcfg2-repo-validate | 41 ++- 10 files changed, 355 insertions(+), 410 deletions(-) delete mode 100644 src/lib/Server/Plugins/POSIXCompat.py diff --git a/schemas/rules.xsd b/schemas/rules.xsd index 207eb65e5..80036834a 100644 --- a/schemas/rules.xsd +++ b/schemas/rules.xsd @@ -54,14 +54,6 @@ - - - - - - - - @@ -70,11 +62,6 @@ - - - - - @@ -89,21 +76,11 @@ - - - - - - - - - - @@ -117,11 +94,8 @@ - - - diff --git a/src/lib/Client/Frame.py b/src/lib/Client/Frame.py index 6cfb19732..545d4b584 100644 --- a/src/lib/Client/Frame.py +++ b/src/lib/Client/Frame.py @@ -110,20 +110,6 @@ class Frame: self.logger.info("Loaded tool drivers:") self.logger.info([tool.name for tool in self.tools]) if not self.dryrun and not self.setup['bundle']: - for cfile in [cfl for cfl in config.findall(".//ConfigFile") \ - if cfl.get('name') in self.__important__]: - tl = [t for t in self.tools if t.handlesEntry(cfile) \ - and t.canVerify(cfile)] - if tl: - if not tl[0].VerifyConfigFile(cfile, []): - if self.setup['interactive'] and not \ - promptFilter("Install %s: %s? (y/N):", [cfile]): - continue - try: - self.states[cfile] = tl[0].InstallConfigFile(cfile) - except: - self.logger.error("Unexpected tool failure", - exc_info=1) for cfile in [cfl for cfl in config.findall(".//Path") \ if cfl.get('name') in self.__important__ and \ cfl.get('type') == 'file']: diff --git a/src/lib/Client/Tools/APT.py b/src/lib/Client/Tools/APT.py index 9dc2c5bba..2afe2eab7 100644 --- a/src/lib/Client/Tools/APT.py +++ b/src/lib/Client/Tools/APT.py @@ -55,7 +55,7 @@ class APT(Bcfg2.Client.Tools.Tool): '%s/apt/apt.conf' % etc_path, '%s/dpkg/dpkg.cfg' % etc_path] + \ [entry.get('name') for struct in config for entry in struct \ - if entry.tag in ['Path', 'ConfigFile'] and \ + if entry.tag == 'Path' and \ entry.get('name').startswith('%s/apt/sources.list' % etc_path)] self.nonexistent = [entry.get('name') for struct in config for entry in struct \ if entry.tag == 'Path' and entry.get('type') == 'nonexistent'] diff --git a/src/lib/Client/Tools/POSIX.py b/src/lib/Client/Tools/POSIX.py index a4417297a..d2611130c 100644 --- a/src/lib/Client/Tools/POSIX.py +++ b/src/lib/Client/Tools/POSIX.py @@ -1,11 +1,11 @@ """All POSIX Type client support for Bcfg2.""" __revision__ = '$Revision$' -from datetime import datetime from stat import S_ISVTX, S_ISGID, S_ISUID, S_IXUSR, S_IWUSR, S_IRUSR, S_IXGRP 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, ST_MTIME import binascii +from datetime import datetime import difflib import errno import grp @@ -14,7 +14,6 @@ import os import pwd import shutil import stat -import string import time import Bcfg2.Client.Tools import Bcfg2.Options @@ -45,21 +44,6 @@ def calcPerms(initial, perms): return tempperms -def normUid(entry): - """ - This takes a user name or uid and - returns the corresponding uid or False. - """ - try: - try: - return int(entry.get('owner')) - except: - return int(pwd.getpwnam(entry.get('owner'))[2]) - except (OSError, KeyError): - log.error('UID normalization failed for %s' % (entry.get('name'))) - return False - - def normGid(entry): """ This takes a group name or gid and @@ -74,40 +58,33 @@ def normGid(entry): log.error('GID normalization failed for %s' % (entry.get('name'))) return False -text_chars = "".join([chr(y) for y in range(32, 127)] + list("\n\r\t\b")) -notrans = string.maketrans("", "") - -def isString(strng): - """Returns true if a string contains no binary chars.""" - if "\0" in strng: +def normUid(entry): + """ + This takes a user name or uid and + returns the corresponding uid or False. + """ + try: + try: + return int(entry.get('owner')) + except: + return int(pwd.getpwnam(entry.get('owner'))[2]) + except (OSError, KeyError): + log.error('UID normalization failed for %s' % (entry.get('name'))) return False - if not strng: - return True - - return len(strng.translate(notrans, text_chars)) == 0 - class POSIX(Bcfg2.Client.Tools.Tool): """POSIX File support code.""" name = 'POSIX' - __handles__ = [('ConfigFile', None), - ('Directory', None), - ('Path', 'device'), + __handles__ = [('Path', 'device'), ('Path', 'directory'), ('Path', 'file'), ('Path', 'hardlink'), ('Path', 'nonexistent'), ('Path', 'permissions'), - ('Path', 'symlink'), - ('Permissions', None), - ('SymLink', None)] - __req__ = {'ConfigFile': ['name', 'owner', 'group', 'perms'], - 'Directory': ['name', 'owner', 'group', 'perms'], - 'Path': ['name', 'type'], - 'Permissions': ['name', 'owner', 'group', 'perms'], - 'SymLink': ['name', 'to']} + ('Path', 'symlink')] + __req__ = {'Path': ['name', 'type']} # grab paranoid options from /etc/bcfg2.conf opts = {'ppath': Bcfg2.Options.PARANOID_PATH, @@ -120,64 +97,150 @@ class POSIX(Bcfg2.Client.Tools.Tool): def canInstall(self, entry): """Check if entry is complete for installation.""" if Bcfg2.Client.Tools.Tool.canInstall(self, entry): - if (entry.tag, entry.text, entry.get('empty', 'false')) == \ - ('ConfigFile', None, 'false'): + if (entry.tag, + entry.get('type'), + entry.text, + entry.get('empty', 'false')) == ('Path', + 'file', + None, + 'false'): return False return True else: return False - def VerifySymLink(self, entry, _): - """Verify SymLink Entry.""" - try: - sloc = os.readlink(entry.get('name')) - if sloc == entry.get('to'): - return True - self.logger.debug("Symlink %s points to %s, should be %s" % \ - (entry.get('name'), sloc, entry.get('to'))) - entry.set('current_to', sloc) - entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'), - entry.get('to'))) + def gatherCurrentData(self, entry): + if entry.tag == 'Path' and entry.get('type') == 'file': + try: + ondisk = os.stat(entry.get('name')) + except OSError: + entry.set('current_exists', 'false') + self.logger.debug("%s %s does not exist" % + (entry.tag, entry.get('name'))) + return False + try: + entry.set('current_owner', str(ondisk[ST_UID])) + entry.set('current_group', str(ondisk[ST_GID])) + except (OSError, KeyError): + pass + entry.set('perms', str(oct(ondisk[ST_MODE])[-4:])) + try: + content = open(entry.get('name')).read() + entry.set('current_bfile', binascii.b2a_base64(content)) + except IOError, error: + self.logger.error("Failed to read %s: %s" % (error.filename, + error.strerror)) + + def Verifydevice(self, entry, _): + """Verify device entry.""" + if entry.get('dev_type') == None or \ + entry.get('owner') == None or \ + entry.get('group') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % (entry.get('name'))) return False + if entry.get('dev_type') in ['block', 'char']: + # check if major/minor are properly specified + if entry.get('major') == None or \ + entry.get('minor') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % (entry.get('name'))) + return False + try: + # check for file existence + filestat = os.stat(entry.get('name')) except OSError: entry.set('current_exists', 'false') - entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'), - entry.get('to'))) + self.logger.debug("%s %s does not exist" % + (entry.tag, entry.get('name'))) return False - def InstallSymLink(self, entry): - """Install SymLink entry.""" - self.logger.info("Installing Symlink %s" % (entry.get('name'))) - if os.path.lexists(entry.get('name')): - try: - fmode = os.lstat(entry.get('name'))[ST_MODE] - if S_ISREG(fmode) or S_ISLNK(fmode): - self.logger.debug("Non-directory entry already exists at " - "%s. Unlinking entry." % \ - (entry.get('name'))) - os.unlink(entry.get('name')) - elif S_ISDIR(fmode): - self.logger.debug("Directory entry already exists at %s" %\ - (entry.get('name'))) - self.cmd.run("mv %s/ %s.bak" % \ - (entry.get('name'), - entry.get('name'))) - else: - os.unlink(entry.get('name')) - except OSError: - self.logger.info("Symlink %s cleanup failed" %\ - (entry.get('name'))) try: - os.symlink(entry.get('to'), entry.get('name')) - return True + # attempt to verify device properties as specified in config + dev_type = entry.get('dev_type') + mode = calcPerms(device_map[dev_type], + entry.get('mode', '0600')) + owner = normUid(entry) + group = normGid(entry) + if dev_type in ['block', 'char']: + # check for incompletely specified entries + if entry.get('major') == None or \ + entry.get('minor') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % (entry.get('name'))) + return False + major = int(entry.get('major')) + minor = int(entry.get('minor')) + if major == os.major(filestat.st_rdev) and \ + minor == os.minor(filestat.st_rdev) and \ + mode == filestat.st_mode and \ + owner == filestat.st_uid and \ + group == filestat.st_gid: + return True + else: + return False + elif dev_type == 'fifo' and \ + mode == filestat.st_mode and \ + owner == filestat.st_uid and \ + group == filestat.st_gid: + return True + else: + self.logger.info('Device properties for %s incorrect' % \ + entry.get('name')) + return False except OSError: + self.logger.debug("%s %s failed to verify" % + (entry.tag, entry.get('name'))) return False - def VerifyDirectory(self, entry, modlist): - """Verify Directory entry.""" + def Installdevice(self, entry): + """Install device entries.""" + try: + # check for existing paths and remove them + os.lstat(entry.get('name')) + try: + os.unlink(entry.get('name')) + exists = False + except OSError: + self.logger.info('Failed to unlink %s' % \ + entry.get('name')) + return False + except OSError: + exists = False + + if not exists: + try: + dev_type = entry.get('dev_type') + mode = calcPerms(device_map[dev_type], + entry.get('mode', '0600')) + if dev_type in ['block', 'char']: + # check if major/minor are properly specified + if entry.get('major') == None or \ + entry.get('minor') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % (entry.get('name'))) + return False + major = int(entry.get('major')) + minor = int(entry.get('minor')) + device = os.makedev(major, minor) + os.mknod(entry.get('name'), mode, device) + else: + os.mknod(entry.get('name'), mode) + os.chown(entry.get('name'), normUid(entry), normGid(entry)) + return True + except KeyError: + self.logger.error('Failed to install %s' % entry.get('name')) + except OSError: + self.logger.error('Failed to install %s' % entry.get('name')) + return False + + def Verifydirectory(self, entry, modlist): + """Verify Path type='directory' entry.""" if entry.get('perms') == None or \ entry.get('owner') == None or \ entry.get('group') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % (entry.get('name'))) return False while len(entry.get('perms', '')) < 4: entry.set('perms', '0' + entry.get('perms', '')) @@ -210,9 +273,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): pruneTrue = True ex_ents = [] if entry.get('prune', 'false') == 'true' \ - and (entry.tag == 'Directory' or - entry.get('type') == 'directory'): - # FIXME: need to verify both old and new POSIX types + and (entry.tag == 'Path' and entry.get('type') == 'directory'): + # check for any extra entries when prune='true' attribute is set try: entries = ['/'.join([entry.get('name'), ent]) \ for ent in os.listdir(entry.get('name'))] @@ -227,7 +289,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): entry.get('name') nqtext += ":".join(ex_ents) entry.set('qtest', nqtext) - [entry.append(XML.Element('Prune', path=x)) for x in ex_ents] + [entry.append(XML.Element('Prune', path=x)) \ + for x in ex_ents] except OSError: ex_ents = [] pruneTrue = True @@ -243,7 +306,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): entry.set('qtext', nqtext) if group != str(normGid(entry)): entry.set('current_group', group) - self.logger.debug("%s %s group wrong" % (entry.tag, entry.get('name'))) + self.logger.debug("%s %s group wrong" % \ + (entry.tag, entry.get('name'))) nqtext = entry.get('qtext', '') + '\n' nqtext += "%s group is %s should be %s" % \ (entry.get('name'), group, entry.get('group')) @@ -251,10 +315,16 @@ class POSIX(Bcfg2.Client.Tools.Tool): if perms != entry.get('perms'): entry.set('current_perms', perms) self.logger.debug("%s %s permissions are %s should be %s" % - (entry.tag, entry.get('name'), perms, entry.get('perms'))) + (entry.tag, + entry.get('name'), + perms, + entry.get('perms'))) nqtext = entry.get('qtext', '') + '\n' - nqtext += "%s perms are %s should be %s" % \ - (entry.get('name'), perms, entry.get('perms')) + nqtext += "%s %s perms are %s should be %s" % \ + (entry.tag, + entry.get('name'), + perms, + entry.get('perms')) entry.set('qtext', nqtext) if mtime != entry.get('mtime', '-1'): entry.set('current_mtime', mtime) @@ -265,21 +335,23 @@ class POSIX(Bcfg2.Client.Tools.Tool): nqtext += "%s mtime is %s should be %s" % \ (entry.get('name'), mtime, entry.get('mtime')) entry.set('qtext', nqtext) - if entry.tag != 'ConfigFile': + if entry.get('type') != 'file': nnqtext = entry.get('qtext') - nnqtext += '\nInstall %s %s: (y/N) ' % (entry.tag, entry.get('name')) + nnqtext += '\nInstall %s %s: (y/N) ' % (entry.get('type'), + entry.get('name')) entry.set('qtext', nnqtext) return pTrue and pruneTrue - def InstallDirectory(self, entry): - """Install Directory entry.""" + def Installdirectory(self, entry): + """Install Path type='directory' entry.""" if entry.get('perms') == None or \ entry.get('owner') == None or \ entry.get('group') == None: self.logger.error('Entry %s not completely specified. ' - 'Try running bcfg2-repo-validate.' % (entry.get('name'))) + 'Try running bcfg2-repo-validate.' % \ + (entry.get('name'))) return False - self.logger.info("Installing Directory %s" % (entry.get('name'))) + self.logger.info("Installing directory %s" % (entry.get('name'))) try: fmode = os.lstat(entry.get('name')) if not S_ISDIR(fmode[ST_MODE]): @@ -289,7 +361,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): os.unlink(entry.get('name')) exists = False except OSError: - self.logger.info("Failed to unlink %s" % (entry.get('name'))) + self.logger.info("Failed to unlink %s" % \ + (entry.get('name'))) return False else: self.logger.debug("Found a pre-existing directory at %s" % \ @@ -334,7 +407,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): pname = pent.get('path') ulfailed = False if os.path.isdir(pname): - self.logger.info("Not removing extra directory %s, please check and remove manually" % pname) + self.logger.info("Not removing extra directory %s, " + "please check and remove manually" % pname) continue try: self.logger.debug("Unlinking file %s" % pname) @@ -344,188 +418,12 @@ class POSIX(Bcfg2.Client.Tools.Tool): ulfailed = True if ulfailed: return False - return self.InstallPermissions(entry) - - def VerifyhardLink(self, entry, _): - """Verify HardLink entry.""" - try: - if os.path.samefile(entry.get('name'), entry.get('to')): - return True - self.logger.debug("Hardlink %s is incorrect" % \ - entry.get('name')) - entry.set('qtext', "Link %s to %s? [y/N] " % \ - (entry.get('name'), - entry.get('to'))) - return False - except OSError: - entry.set('current_exists', 'false') - entry.set('qtext', "Link %s to %s? [y/N] " % \ - (entry.get('name'), - entry.get('to'))) - return False - - def InstallhardLink(self, entry): - """Install HardLink entry.""" - self.logger.info("Installing Hardlink %s" % (entry.get('name'))) - if os.path.lexists(entry.get('name')): - try: - fmode = os.lstat(entry.get('name'))[ST_MODE] - if S_ISREG(fmode) or S_ISLNK(fmode): - self.logger.debug("Non-directory entry already exists at " - "%s. Unlinking entry." % (entry.get('name'))) - os.unlink(entry.get('name')) - elif S_ISDIR(fmode): - self.logger.debug("Directory entry already exists at %s" % \ - (entry.get('name'))) - self.cmd.run("mv %s/ %s.bak" % \ - (entry.get('name'), - entry.get('name'))) - else: - os.unlink(entry.get('name')) - except OSError: - self.logger.info("Hardlink %s cleanup failed" % (entry.get('name'))) - try: - os.link(entry.get('to'), entry.get('name')) - return True - except OSError: - return False - - def VerifyPermissions(self, entry, _): - """Verify Permissions entry""" - return self.VerifyDirectory(entry, _) - - def InstallPermissions(self, entry): - """Install POSIX permissions""" - if entry.get('perms') == None or \ - entry.get('owner') == None or \ - entry.get('group') == None: - self.logger.error('Entry %s not completely specified. ' - 'Try running bcfg2-repo-validate.' % (entry.get('name'))) - return False - try: - os.chown(entry.get('name'), normUid(entry), normGid(entry)) - os.chmod(entry.get('name'), calcPerms(S_IFDIR, entry.get('perms'))) - return True - except (OSError, KeyError): - self.logger.error('Permission fixup failed for %s' % \ - (entry.get('name'))) - return False - - def Verifydevice(self, entry, _): - """Verify device entry.""" - try: - # check for file existence - filestat = os.stat(entry.get('name')) - except OSError: - entry.set('current_exists', 'false') - self.logger.debug("%s %s does not exist" % - (entry.tag, entry.get('name'))) - return False - - try: - # attempt to verify device properties as specified in config - dev_type = entry.get('dev_type') - mode = calcPerms(device_map[dev_type], - entry.get('mode', '0600')) - owner = entry.get('owner') - group = entry.get('group') - if dev_type in ['block', 'char']: - major = int(entry.get('major')) - minor = int(entry.get('minor')) - if major == os.major(filestat.st_rdev) and \ - minor == os.minor(filestat.st_rdev) and \ - mode == filestat.st_mode and \ - owner == filestat.st_uid and \ - group == filestat.st_gid: - return True - else: - return False - elif dev_type == 'fifo' and \ - mode == filestat.st_mode and \ - owner == filestat.st_uid and \ - group == filestat.st_gid: - return True - else: - self.logger.info('Device properties for %s incorrect' % \ - entry.get('name')) - return False - except OSError: - self.logger.debug("%s %s failed to verify" % - (entry.tag, entry.get('name'))) - return False - - def Installdevice(self, entry): - """Install device entries.""" - try: - # check for existing paths and remove them - filestat = os.lstat(entry.get('name')) - try: - os.unlink(entry.get('name')) - exists = False - except OSError: - self.logger.info('Failed to unlink %s' % \ - entry.get('name')) - return False - except OSError: - exists = False - - if not exists: - try: - dev_type = entry.get('dev_type') - mode = calcPerms(device_map[dev_type], - entry.get('mode', '0600')) - if dev_type in ['block', 'char']: - major = int(entry.get('major')) - minor = int(entry.get('minor')) - device = os.makedev(major, minor) - os.mknod(entry.get('name'), mode, device) - else: - os.mknod(entry.get('name'), mode) - os.chown(entry.get('name'), normUid(entry), normGid(entry)) - return True - except OSError: - self.logger.error('Failed to install %s' % entry.get('name')) - return False + return self.Installpermissions(entry) - def Verifynonexistent(self, entry, _): - """Verify nonexistent entry.""" - # return true if path does _not_ exist - return not os.path.lexists(entry.get('name')) - - def Installnonexistent(self, entry): - '''Remove nonexistent entries''' - try: - os.remove(entry.get('name')) - return True - except OSError: - self.logger.error('Failed to remove %s' % entry.get('name')) - return False - - def gatherCurrentData(self, entry): - if entry.tag == 'ConfigFile': - try: - ondisk = os.stat(entry.get('name')) - except OSError: - entry.set('current_exists', 'false') - self.logger.debug("%s %s does not exist" % - (entry.tag, entry.get('name'))) - return False - try: - entry.set('current_owner', str(ondisk[ST_UID])) - entry.set('current_group', str(ondisk[ST_GID])) - except (OSError, KeyError): - pass - entry.set('perms', str(oct(ondisk[ST_MODE])[-4:])) - try: - content = open(entry.get('name')).read() - entry.set('current_bfile', binascii.b2a_base64(content)) - except IOError, error: - self.logger.error("Failed to read %s: %s" % (error.filename, error.strerror)) - - def VerifyConfigFile(self, entry, _): - """Install ConfigFile entry.""" - # configfile verify is permissions check + content check - permissionStatus = self.VerifyDirectory(entry, _) + def Verifyfile(self, entry, _): + """Verify Path type='file' entry.""" + # permissions check + content check + permissionStatus = self.Verifydirectory(entry, _) tbin = False if entry.get('encoding', 'ascii') == 'base64': tempdata = binascii.a2b_base64(entry.text) @@ -534,8 +432,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): tempdata = '' else: if entry.text == None: - self.logger.error("Cannot verify incomplete ConfigFile %s" % \ - (entry.get('name'))) + self.logger.error("Cannot verify incomplete Path type='%s' %s" % \ + (entry.get('type'), entry.get('name'))) return False tempdata = entry.text if type(tempdata) == unicode: @@ -558,7 +456,12 @@ class POSIX(Bcfg2.Client.Tools.Tool): # md5sum so it would be faster for big binary files contentStatus = content == tempdata if not contentStatus: - if tbin or not isString(content): + try: + content.decode('ascii') + isstring = True + except: + isstring = False + if tbin or not isstring: entry.set('current_bfile', binascii.b2a_base64(content)) nqtext = entry.get('qtext', '') nqtext += '\nBinary file, no printable diff' @@ -567,7 +470,8 @@ class POSIX(Bcfg2.Client.Tools.Tool): rawdiff = [] start = time.time() longtime = False - for x in difflib.ndiff(content.split('\n'), tempdata.split('\n')): + for x in difflib.ndiff(content.split('\n'), + tempdata.split('\n')): now = time.time() rawdiff.append(x) if now - start > 5 and not longtime: @@ -606,9 +510,9 @@ class POSIX(Bcfg2.Client.Tools.Tool): entry.set('qtext', qtxt) return contentStatus and permissionStatus - def InstallConfigFile(self, entry): - """Install ConfigFile entry.""" - self.logger.info("Installing ConfigFile %s" % (entry.get('name'))) + def Installfile(self, entry): + """Install Path type='file' entry.""" + self.logger.info("Installing file %s" % (entry.get('name'))) parent = "/".join(entry.get('name').split('/')[:-1]) if parent: @@ -642,9 +546,9 @@ class POSIX(Bcfg2.Client.Tools.Tool): self.setup.get("paranoid", False) and not \ (entry.get('current_exists', 'true') == 'false'): bkupnam = entry.get('name').replace('/', '_') - # current list of backups for this ConfigFile + # current list of backups for this file bkuplist = [f for f in os.listdir(self.ppath) if - f.startswith(bkupnam)] + f.startswith(bkupnam)] bkuplist.sort() if len(bkuplist) == int(self.max_copies): # remove the oldest backup available @@ -663,7 +567,7 @@ class POSIX(Bcfg2.Client.Tools.Tool): self.logger.info("Backup of %s saved to %s" % (entry.get('name'), self.ppath)) except IOError, e: - self.logger.error("Failed to create backup file for ConfigFile %s" % \ + self.logger.error("Failed to create backup file for %s" % \ (entry.get('name'))) self.logger.error(e) return False @@ -694,7 +598,7 @@ class POSIX(Bcfg2.Client.Tools.Tool): os.utime(entry.get('name'), (int(entry.get('mtime')), int(entry.get('mtime')))) except: - self.logger.error("ConfigFile %s mtime fix failed" \ + self.logger.error("File %s mtime fix failed" \ % (entry.get('name'))) return False return True @@ -705,42 +609,158 @@ class POSIX(Bcfg2.Client.Tools.Tool): print(err) return False - def Verifydirectory(self, entry, _): - ret = getattr(self, 'VerifyDirectory') - return ret(entry, _) + def Verifyhardlink(self, entry, _): + """Verify HardLink entry.""" + if entry.get('to') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % \ + (entry.get('name'))) + return False + try: + if os.path.samefile(entry.get('name'), entry.get('to')): + return True + self.logger.debug("Hardlink %s is incorrect" % \ + entry.get('name')) + entry.set('qtext', "Link %s to %s? [y/N] " % \ + (entry.get('name'), + entry.get('to'))) + return False + except OSError: + entry.set('current_exists', 'false') + entry.set('qtext', "Link %s to %s? [y/N] " % \ + (entry.get('name'), + entry.get('to'))) + return False - def Installdirectory(self, entry): - ret = getattr(self, 'InstallDirectory') - return ret(entry) + def Installhardlink(self, entry): + """Install HardLink entry.""" + if entry.get('to') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % \ + (entry.get('name'))) + return False + self.logger.info("Installing Hardlink %s" % (entry.get('name'))) + if os.path.lexists(entry.get('name')): + try: + fmode = os.lstat(entry.get('name'))[ST_MODE] + if S_ISREG(fmode) or S_ISLNK(fmode): + self.logger.debug("Non-directory entry already exists at " + "%s. Unlinking entry." % (entry.get('name'))) + os.unlink(entry.get('name')) + elif S_ISDIR(fmode): + self.logger.debug("Directory already exists at %s" % \ + (entry.get('name'))) + self.cmd.run("mv %s/ %s.bak" % \ + (entry.get('name'), + entry.get('name'))) + else: + os.unlink(entry.get('name')) + except OSError: + self.logger.info("Hardlink %s cleanup failed" % \ + (entry.get('name'))) + try: + os.link(entry.get('to'), entry.get('name')) + return True + except OSError: + return False - def Verifyfile(self, entry, _): - ret = getattr(self, 'VerifyConfigFile') - return ret(entry, _) + def Verifynonexistent(self, entry, _): + """Verify nonexistent entry.""" + # return true if path does _not_ exist + return not os.path.lexists(entry.get('name')) - def Installfile(self, entry): - ret = getattr(self, 'InstallConfigFile') - return ret(entry) + def Installnonexistent(self, entry): + '''Remove nonexistent entries''' + try: + os.remove(entry.get('name')) + return True + except OSError: + self.logger.error('Failed to remove %s' % entry.get('name')) + return False def Verifypermissions(self, entry, _): - ret = getattr(self, 'VerifyPermissions') - return ret(entry, _) + """Verify Path type='permissions' entry""" + return self.Verifydirectory(entry, _) def Installpermissions(self, entry): - ret = getattr(self, 'InstallPermissions') - return ret(entry) + """Install POSIX permissions""" + if entry.get('perms') == None or \ + entry.get('owner') == None or \ + entry.get('group') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % (entry.get('name'))) + return False + try: + os.chown(entry.get('name'), normUid(entry), normGid(entry)) + os.chmod(entry.get('name'), calcPerms(S_IFDIR, entry.get('perms'))) + return True + except (OSError, KeyError): + self.logger.error('Permission fixup failed for %s' % \ + (entry.get('name'))) + return False def Verifysymlink(self, entry, _): - ret = getattr(self, 'VerifySymLink') - return ret(entry, _) + """Verify Path type='symlink' entry.""" + if entry.get('to') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % \ + (entry.get('name'))) + return False + try: + sloc = os.readlink(entry.get('name')) + if sloc == entry.get('to'): + return True + self.logger.debug("Symlink %s points to %s, should be %s" % \ + (entry.get('name'), sloc, entry.get('to'))) + entry.set('current_to', sloc) + entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'), + entry.get('to'))) + return False + except OSError: + entry.set('current_exists', 'false') + entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'), + entry.get('to'))) + return False def Installsymlink(self, entry): - ret = getattr(self, 'InstallSymLink') - return ret(entry) + """Install Path type='symlink' entry.""" + if entry.get('to') == None: + self.logger.error('Entry %s not completely specified. ' + 'Try running bcfg2-repo-validate.' % \ + (entry.get('name'))) + return False + self.logger.info("Installing symlink %s" % (entry.get('name'))) + if os.path.lexists(entry.get('name')): + try: + fmode = os.lstat(entry.get('name'))[ST_MODE] + if S_ISREG(fmode) or S_ISLNK(fmode): + self.logger.debug("Non-directory entry already exists at " + "%s. Unlinking entry." % \ + (entry.get('name'))) + os.unlink(entry.get('name')) + elif S_ISDIR(fmode): + self.logger.debug("Directory already exists at %s" %\ + (entry.get('name'))) + self.cmd.run("mv %s/ %s.bak" % \ + (entry.get('name'), + entry.get('name'))) + else: + os.unlink(entry.get('name')) + except OSError: + self.logger.info("Symlink %s cleanup failed" %\ + (entry.get('name'))) + try: + os.symlink(entry.get('to'), entry.get('name')) + return True + except OSError: + return False def InstallPath(self, entry): + """Dispatch install to the proper method according to type""" ret = getattr(self, 'Install%s' % entry.get('type')) return ret(entry) def VerifyPath(self, entry, _): + """Dispatch verify to the proper method according to type""" ret = getattr(self, 'Verify%s' % entry.get('type')) return ret(entry, _) diff --git a/src/lib/Client/Tools/YUMng.py b/src/lib/Client/Tools/YUMng.py index 9fc776471..ce8832395 100644 --- a/src/lib/Client/Tools/YUMng.py +++ b/src/lib/Client/Tools/YUMng.py @@ -157,7 +157,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool): self.__important__ = self.__important__ + \ [entry.get('name') for struct in config \ for entry in struct \ - if entry.tag in ['Path', 'ConfigFile'] and \ + if entry.tag in == 'Path' and \ (entry.get('name').startswith('/etc/yum.d') \ or entry.get('name').startswith('/etc/yum.repos.d')) \ or entry.get('name') == '/etc/yum.conf'] diff --git a/src/lib/Client/Tools/__init__.py b/src/lib/Client/Tools/__init__.py index 1d89e6d02..8a90e130c 100644 --- a/src/lib/Client/Tools/__init__.py +++ b/src/lib/Client/Tools/__init__.py @@ -172,8 +172,7 @@ class Tool: '''Build a list of potentially modified POSIX paths for this entry''' return [entry.get('name') for struct in self.config.getchildren() \ for entry in struct.getchildren() \ - if entry.tag in ['ConfigFile', 'SymLink', 'Directory', - 'Permissions', 'Ignore', 'Path']] + if entry.tag in ['Ignore', 'Path']] def gatherCurrentData(self, entry): """Default implementation of the information gathering routines.""" diff --git a/src/lib/Server/Admin/Bundle.py b/src/lib/Server/Admin/Bundle.py index e293c6a4f..41cd5727e 100644 --- a/src/lib/Server/Admin/Bundle.py +++ b/src/lib/Server/Admin/Bundle.py @@ -91,7 +91,7 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore): tree = lxml.etree.parse(bundle_list[int(lineno)]) #Prints bundle content #print lxml.etree.tostring(tree) - names = ['Action', 'ConfigFile', 'Directory', 'Package', 'Permission', 'Service', 'SymLink'] + names = ['Action', 'Package', 'Path', 'Service'] for name in names: for node in tree.findall("//" + name): print "%s:\t%s" % (name, node.attrib["name"]) diff --git a/src/lib/Server/Plugins/POSIXCompat.py b/src/lib/Server/Plugins/POSIXCompat.py deleted file mode 100644 index fc16e4b48..000000000 --- a/src/lib/Server/Plugins/POSIXCompat.py +++ /dev/null @@ -1,38 +0,0 @@ -"""This plugin provides a compatibility layer which turns new-style -POSIX entries into old-style entries. -""" - -__revision__ = '$Revision$' - -import Bcfg2.Server.Plugin - -COMPAT_DICT = {'file': 'ConfigFile', - 'directory': 'Directory', - 'permissions': 'Permissions', - 'symlink': 'SymLink'} - - -class POSIXCompat(Bcfg2.Server.Plugin.Plugin, - Bcfg2.Server.Plugin.GoalValidator): - """POSIXCompat is a goal validator plugin for POSIX entries.""" - name = 'POSIXCompat' - __version__ = '$Id$' - __author__ = 'bcfg-dev@mcs.anl.gov' - - def __init__(self, core, datastore): - Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) - Bcfg2.Server.Plugin.GoalValidator.__init__(self) - - def validate_goals(self, metadata, goals): - """Verify that we are generating correct old - Cfg/Directory/Symlink entries. - """ - for goal in goals: - for entry in goal.getchildren(): - if entry.tag == 'Path' and \ - entry.get('type') not in ['nonexistent', 'device']: - # Use new entry 'type' attribute to map old entry tags - oldentry = entry - entry.tag = COMPAT_DICT[entry.get('type')] - del entry.attrib['type'] - goal.replace(oldentry, entry) diff --git a/src/lib/Server/Plugins/__init__.py b/src/lib/Server/Plugins/__init__.py index 758f1219d..c69c37452 100644 --- a/src/lib/Server/Plugins/__init__.py +++ b/src/lib/Server/Plugins/__init__.py @@ -22,7 +22,6 @@ __all__ = [ 'Properties', 'Probes', 'Pkgmgr', - 'POSIXCompat', 'Rules', 'SSHbase', 'Snapshots', diff --git a/src/sbin/bcfg2-repo-validate b/src/sbin/bcfg2-repo-validate index 1889c9892..3d5efb093 100755 --- a/src/sbin/bcfg2-repo-validate +++ b/src/sbin/bcfg2-repo-validate @@ -88,14 +88,14 @@ if __name__ == '__main__': # (as defined in doc/server/configurationentries) # TODO: See if it is possible to do this in the schema instead required_configuration_attrs = { - 'device':['name', 'owner', 'group', 'dev_type'], - 'directory':['name', 'owner', 'group', 'perms'], - 'file':['name', 'owner', 'group', 'perms'], - 'hardlink':['name', 'to'], - 'symlink':['name', 'to'], - 'ignore':['name'], - 'nonexist':['name'], - 'permissions':['name', 'owner', 'group', 'perms']} + 'device': ['name', 'owner', 'group', 'dev_type'], + 'directory': ['name', 'owner', 'group', 'perms'], + 'file': ['name', 'owner', 'group', 'perms'], + 'hardlink': ['name', 'to'], + 'symlink': ['name', 'to'], + 'ignore': ['name'], + 'nonexist': ['name'], + 'permissions': ['name', 'owner', 'group', 'perms']} for rfile in rules_list: try: xdata = lxml.etree.parse(rfile) @@ -110,6 +110,11 @@ if __name__ == '__main__': + ['type']) except KeyError: continue + if 'dev_type' in required_attrs: + dev_type = posixpath.get('dev_type') + if dev_type in ['block', 'char']: + # check if major/minor are specified + required_attrs |= set(['major', 'minor']) if pathset.issuperset(required_attrs): continue else: @@ -146,16 +151,16 @@ if __name__ == '__main__': else: pset.add(ptuple) - filesets = {'metadata':(metadata_list, "%s/metadata.xsd"), - 'clients':(clients_list, "%s/clients.xsd"), - 'info':(info_list, "%s/info.xsd"), - 'bundle':(bundle_list, "%s/bundle.xsd"), - 'pkglist':(pkg_list, "%s/pkglist.xsd"), - 'base':(base_list, "%s/base.xsd"), - 'rules':(rules_list, "%s/rules.xsd"), - 'imageinfo':(imageinfo_list, "%s/report-configuration.xsd"), - 'services':(services_list, "%s/services.xsd"), - 'deps':(deps_list, "%s/deps.xsd"), + filesets = {'metadata': (metadata_list, "%s/metadata.xsd"), + 'clients': (clients_list, "%s/clients.xsd"), + 'info': (info_list, "%s/info.xsd"), + 'bundle': (bundle_list, "%s/bundle.xsd"), + 'pkglist': (pkg_list, "%s/pkglist.xsd"), + 'base': (base_list, "%s/base.xsd"), + 'rules': (rules_list, "%s/rules.xsd"), + 'imageinfo': (imageinfo_list, "%s/report-configuration.xsd"), + 'services': (services_list, "%s/services.xsd"), + 'deps': (deps_list, "%s/deps.xsd"), 'decisions': (dec_list, "%s/decisions.xsd"), 'packages': (pkgcfg_list, "%s/packages.xsd"), 'grouppatterns': (gp_list, "%s/grouppatterns.xsd"), -- cgit v1.2.3-1-g7c22