diff options
-rw-r--r-- | debian/changelog | 6 | ||||
-rw-r--r-- | doc/appendix/guides/ubuntu.txt | 2 | ||||
-rw-r--r-- | doc/conf.py | 2 | ||||
-rw-r--r-- | doc/installation/source.txt | 3 | ||||
-rw-r--r-- | doc/server/plugins/grouping/metadata.txt | 29 | ||||
-rw-r--r-- | examples/bcfg2-lint.conf | 2 | ||||
-rw-r--r-- | misc/bcfg2.spec | 10 | ||||
-rw-r--r-- | osx/Makefile | 4 | ||||
-rw-r--r-- | redhat/VERSION | 2 | ||||
-rwxr-xr-x | setup.py | 2 | ||||
-rw-r--r-- | solaris/Makefile | 22 | ||||
-rw-r--r-- | solaris/gen-prototypes.sh | 24 | ||||
-rw-r--r-- | solaris/pkginfo.bcfg2 | 2 | ||||
-rw-r--r-- | solaris/pkginfo.bcfg2-server | 2 | ||||
-rw-r--r-- | src/lib/Options.py | 19 | ||||
-rw-r--r-- | src/lib/Server/Plugin.py | 6 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Cfg.py | 20 | ||||
-rw-r--r-- | src/lib/Server/Plugins/FileProbes.py | 148 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Packages/Source.py | 10 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Packages/Yum.py | 6 | ||||
-rw-r--r-- | src/lib/Server/Plugins/SSHbase.py | 4 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Trigger.py | 13 | ||||
-rw-r--r-- | src/lib/Server/Reports/reports/templates/base.html | 2 |
23 files changed, 209 insertions, 131 deletions
diff --git a/debian/changelog b/debian/changelog index 5722eeb97..2557dd66a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bcfg2 (1.2.2-0.0) unstable; urgency=low + + * New upstream release + + -- Sol Jerome <sol.jerome@gmail.com> Sat, 17 Mar 2012 14:41:17 -0500 + bcfg2 (1.2.1-0.0) unstable; urgency=low * New upstream release diff --git a/doc/appendix/guides/ubuntu.txt b/doc/appendix/guides/ubuntu.txt index d85c34b02..f72247220 100644 --- a/doc/appendix/guides/ubuntu.txt +++ b/doc/appendix/guides/ubuntu.txt @@ -154,7 +154,7 @@ Create Packages layout (as per :ref:`packages-exampleusage`) in [global] root@lucid:~# cat /var/lib/bcfg2/Packages/sources.xml <Sources> - <Group name="lucid"> + <Group name="ubuntu-lucid"> <Source type="apt" url="http://archive.ubuntu.com/ubuntu" version="lucid"> <Component>main</Component> <Component>multiverse</Component> diff --git a/doc/conf.py b/doc/conf.py index 7e9c8b59c..38160715d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -55,7 +55,7 @@ else: # The short X.Y version. version = '1.2' # The full version, including alpha/beta/rc tags. -release = '1.2.1' +release = '1.2.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/installation/source.txt b/doc/installation/source.txt index 3ea0404ad..1406a5ceb 100644 --- a/doc/installation/source.txt +++ b/doc/installation/source.txt @@ -2,6 +2,7 @@ .. _GPG1: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x75BF2C177F7D197E .. _GPG2: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x80B8492FA88FFF4B +.. _Download: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Download .. _source: @@ -14,7 +15,7 @@ Download Tarball ^^^^^^^ -The Bcfg2 source tarball can be grabbed from the Download_ page. +The Bcfg2 source tarball can be grabbed from the `Download`_ page. All tarballs are signed with GPG keys `7F7D197E <GPG1>`_ or `A88FFF4B <GPG2>`_. You can verify your download by importing the keys and running :: diff --git a/doc/server/plugins/grouping/metadata.txt b/doc/server/plugins/grouping/metadata.txt index c52ac7612..305857578 100644 --- a/doc/server/plugins/grouping/metadata.txt +++ b/doc/server/plugins/grouping/metadata.txt @@ -276,22 +276,37 @@ A special client metadata class is available to the MetadataQuery ------------- -This class provides query routines for the servers Metadata. +This class provides query methods for the metadata of all clients +known to the Bcfg2 server. Note that ``*by_groups()`` and +``*by_profiles()`` behave differently; for a client to be included in +the return value of a ``by_groups()`` method, it must be a member of +*all* groups listed in the argument; for a client to be included in +the return value of a ``by_profiles()`` method, it must have any group +listed as its profile group. +------------------------------+------------------------------------------------+-------------------+ | Method | Description | Value | +==============================+================================================+===================+ | by_name(client) | Get ClientMetadata object for 'client' | ClientMetadata | +------------------------------+------------------------------------------------+-------------------+ -| names_by_groups(groups) | All client names in the list of 'groups' | List | +| by_groups(groups) | Get ClientMetadata object for clients in all | List of | +| | listed groups | ClientMetadata | +------------------------------+------------------------------------------------+-------------------+ -| names_by_profiles(profiles) | All client names in the list of 'profiles' | List | +| by_profiles(client) | Get ClientMetadata objects for clients whose | List of | +| | profile matches any listed profile group | ClientMetadata | +------------------------------+------------------------------------------------+-------------------+ -| all_clients() | All known client hostnames | List | +| names_by_groups(groups) | Get the names of all clients in all listed | List of strings | +| | groups | | +------------------------------+------------------------------------------------+-------------------+ -| all_groups() | All known group names | List | +| names_by_profiles(profiles) | Get the names of clients whose profile matches | List of strings | +| | any listed profile group | | +------------------------------+------------------------------------------------+-------------------+ -| all_groups_in_category(cat) | All groups in category 'cat' | List | +| all_clients() | All known client hostnames | List of strings | +------------------------------+------------------------------------------------+-------------------+ -| all() | Get ClientMetadata for all clients | List | +| all_groups() | All known group names | List of strings | ++------------------------------+------------------------------------------------+-------------------+ +| all_groups_in_category(cat) | The names of all groups in category 'cat' | List of strings | ++------------------------------+------------------------------------------------+-------------------+ +| all() | Get ClientMetadata for all clients | List of | +| | | ClientMetadata | +------------------------------+------------------------------------------------+-------------------+ diff --git a/examples/bcfg2-lint.conf b/examples/bcfg2-lint.conf index 9c0d2c72a..6ca3fb61f 100644 --- a/examples/bcfg2-lint.conf +++ b/examples/bcfg2-lint.conf @@ -22,7 +22,7 @@ cfg_keywords = probe_comments = Maintainer,Purpose,Groups,Other Output [Validate] -schema=/usr/share/bcfg2/schema +schema=/usr/share/bcfg2/schemas [MergeFiles] threshold=85 diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec index f19b7ae8c..9fa119edf 100644 --- a/misc/bcfg2.spec +++ b/misc/bcfg2.spec @@ -6,7 +6,7 @@ %{!?_initrddir: %define _initrddir %{_sysconfdir}/rc.d/init.d} Name: bcfg2 -Version: 1.2.1 +Version: 1.2.2 Release: %{release} Summary: Configuration management system @@ -42,11 +42,11 @@ BuildRequires: libsane1 # %{rhel} wasn't set before rhel 6. so this checks for old RHEL # %systems (and potentially very old Fedora systems, too) -%if "%{_vendor}" == "redhat" && 0%{?rhel} <= 6 && 0%{?fedora} == 0 +%if "%{_vendor}" == "redhat" && 0%{?rhel} < 6 && 0%{?fedora} == 0 BuildRequires: python-sphinx10 # the python-sphinx10 package doesn't set sys.path correctly, so we # have to do it for them -%define pythonpath /usr/lib/python%{py_ver}/site-packages/Sphinx-1.0.4-py%{py_ver}.egg +%define pythonpath %(find /usr/lib/python%{py_ver}/site-packages -name %Sphinx*.egg) %else BuildRequires: python-sphinx >= 0.6 %endif @@ -91,7 +91,7 @@ deployment strategies. This package includes the Bcfg2 client software. %package -n bcfg2-server -Version: %{version} +Version: 1.2.2 Summary: Bcfg2 Server %if 0%{?suse_version} Group: System/Management @@ -172,7 +172,7 @@ deployment strategies. This package includes the Bcfg2 documentation. %package -n bcfg2-web -Version: %{version} +Version: 1.2.2 Summary: Bcfg2 Web Reporting Interface %if 0%{?suse_version} Group: System/Management diff --git a/osx/Makefile b/osx/Makefile index 279ad0f6f..72751ff32 100644 --- a/osx/Makefile +++ b/osx/Makefile @@ -29,9 +29,9 @@ SITELIBDIR = /Library/Python/${PYVERSION}/site-packages # an Info.plist file for packagemaker to look at for package creation # and substitute the version strings. Major/Minor versions can only be # integers (e.g. "1" and "00" for bcfg2 version 1.0.0. -BCFGVER = 1.2.1 +BCFGVER = 1.2.2 MAJOR = 1 -MINOR = 21 +MINOR = 22 default: clean client diff --git a/redhat/VERSION b/redhat/VERSION index 6085e9465..23aa83906 100644 --- a/redhat/VERSION +++ b/redhat/VERSION @@ -1 +1 @@ -1.2.1 +1.2.2 @@ -121,7 +121,7 @@ if sys.hexversion < 0x03000000 and os.path.exists(py3lib): setup(cmdclass=cmdclass, name="Bcfg2", - version="1.2.1", + version="1.2.2", description="Bcfg2 Server", author="Narayan Desai", author_email="desai@mcs.anl.gov", diff --git a/solaris/Makefile b/solaris/Makefile index be98f345d..77d9019eb 100644 --- a/solaris/Makefile +++ b/solaris/Makefile @@ -1,26 +1,28 @@ #!/usr/sfw/bin/gmake -PYTHON="/opt/csw/bin/python" -VERS=1.2.1-1 +PYTHON="/usr/local/bin/python" +VERS=1.2.2-1 PYVERSION := $(shell $(PYTHON) -c "import sys; print sys.version[0:3]") default: clean package package: - -mkdir tmp tmp/bcfg2-server tmp/bcfg2 - -cd ../ && $(PYTHON) setup.py install --prefix=$(PWD) + -mkdir tmp tmp/bcfg2-server tmp/bcfg2 + -mkdir -p build/lib/$(PYVERSION)/site-packages + -cd ../ && PYTHONPATH=$(PYTHONPATH):$(PWD)/build/lib/python2.6/site-packages/ $(PYTHON) setup.py install --single-version-externally-managed --record=/dev/null --prefix=$(PWD)/build + #setuptools appears to use a restictive umask + -chmod -R o+r build/ -cat bin/bcfg2 | sed -e 's!/usr/bin/python!$(PYTHON)!' > bin/bcfg2.new && mv bin/bcfg2.new bin/bcfg2 - # Set python version to whichever version is installed - -cat prototype.bcfg2 | sed -e 's!PYVERSION!python$(PYVERSION)!' > prototype.bcfg2.fixed - -cat prototype.bcfg2-server | sed -e 's!PYVERSION!python$(PYVERSION)!' > prototype.bcfg2-server.fixed - -pkgmk -o -a `uname -m` -f prototype.bcfg2.fixed -d $(PWD)/tmp -r $(PWD) - -pkgmk -o -a `uname -m` -f prototype.bcfg2-server.fixed -d $(PWD)/tmp -r $(PWD) + -./gen-prototypes.sh + -pkgmk -o -a `uname -m` -f prototype.bcfg2 -d $(PWD)/tmp -r $(PWD)/build + -pkgmk -o -a `uname -m` -f prototype.bcfg2-server -d $(PWD)/tmp -r $(PWD)/build -pkgtrans -o -s $(PWD)/tmp $(PWD)/bcfg2-$(VERS) SCbcfg2 -pkgtrans -o -s $(PWD)/tmp $(PWD)/bcfg2-server-$(VERS) SCbcfg2-server -gzip -f $(PWD)/bcfg2-$(VERS) -gzip -f $(PWD)/bcfg2-server-$(VERS) clean: - -rm -rf tmp bin lib share + -rm -rf tmp build -rm -rf bcfg2-$(VERS).gz bcfg2-server-$(VERS).gz -rm -rf prototype.bcfg2.fixed prototype.bcfg2-server.fixed + -rm -f prototype.* diff --git a/solaris/gen-prototypes.sh b/solaris/gen-prototypes.sh new file mode 100644 index 000000000..ea0b4bb13 --- /dev/null +++ b/solaris/gen-prototypes.sh @@ -0,0 +1,24 @@ +#!/bin/sh +cd build +PP="./"`ls -1d lib/*`"/site-packages/" + +#bcfg2 +echo "i pkginfo=./pkginfo.bcfg2" > ../prototype.tmp +find . | grep man[15] | pkgproto >> ../prototype.tmp +echo "./bin" | pkgproto >> ../prototype.tmp +echo "./bin/bcfg2" | pkgproto >> ../prototype.tmp +echo "${PP}Bcfg2" | pkgproto >> ../prototype.tmp +ls -1 ${PP}Bcfg2/*.py | pkgproto >> ../prototype.tmp +find ${PP}Bcfg2/Client/ ! -name "*.pyc" | pkgproto >> ../prototype.tmp +sed "s/`id | sed 's/uid=[0-9]*(\(.*\)) gid=[0-9]*(\(.*\))/\1 \2/'`/bin bin/" ../prototype.tmp > ../prototype.bcfg2 + +#bcfg2-server +echo "i pkginfo=./pkginfo.bcfg2-server" > ../prototype.tmp +find . | grep man8 | pkgproto >> ../prototype.tmp +find share/bcfg2 | pkgproto >> ../prototype.tmp +echo "./bin" | pkgproto >> ../prototype.tmp +ls -1 bin/bcfg2-* | pkgproto >> ../prototype.tmp +find ${PP}Bcfg2/Server/ ! -name "*.pyc" | pkgproto >> ../prototype.tmp +sed "s/`id | sed 's/uid=[0-9]*(\(.*\)) gid=[0-9]*(\(.*\))/\1 \2/'`/bin bin/" ../prototype.tmp > ../prototype.bcfg2-server + +rm ../prototype.tmp diff --git a/solaris/pkginfo.bcfg2 b/solaris/pkginfo.bcfg2 index c4633f209..0ff18516d 100644 --- a/solaris/pkginfo.bcfg2 +++ b/solaris/pkginfo.bcfg2 @@ -1,7 +1,7 @@ PKG="SCbcfg2" NAME="bcfg2" ARCH="sparc" -VERSION="1.2.1" +VERSION="1.2.2" CATEGORY="application" VENDOR="Argonne National Labratory" EMAIL="bcfg-dev@mcs.anl.gov" diff --git a/solaris/pkginfo.bcfg2-server b/solaris/pkginfo.bcfg2-server index 762625967..a0958f9e4 100644 --- a/solaris/pkginfo.bcfg2-server +++ b/solaris/pkginfo.bcfg2-server @@ -1,7 +1,7 @@ PKG="SCbcfg2-server" NAME="bcfg2-server" ARCH="sparc" -VERSION="1.2.1" +VERSION="1.2.2" CATEGORY="application" VENDOR="Argonne National Labratory" EMAIL="bcfg-dev@mcs.anl.gov" diff --git a/src/lib/Options.py b/src/lib/Options.py index dc9818c78..2f535397f 100644 --- a/src/lib/Options.py +++ b/src/lib/Options.py @@ -27,12 +27,7 @@ class Option(object): def getCFP(self): if not self.__cfp: self.__cfp = ConfigParser.ConfigParser() - # FIXME: Remove this hack when except: pass below is fixed - try: - self.__cfp.readfp(open(self.cfpath)) - except IOError: - e = sys.exc_info()[1] - print("Unable to read bcfg2.conf: %s" % e) + self.__cfp.readfp(open(self.cfpath)) return self.__cfp cfp = property(getCFP) @@ -116,11 +111,7 @@ class Option(object): self.value = self.get_cooked_value(os.environ[self.env]) return if self.cf: - """ - FIXME: This is masking any sort of error present in getCFP, we - need to remove this catchall exception and figure out something - better - """ + # FIXME: This is potentially masking a lot of errors try: self.value = self.get_cooked_value(self.cfp.get(*self.cf)) return @@ -383,3 +374,9 @@ class OptionParser(OptionSet): Option.cfpath = self.Bootstrap['configfile'] Option.__cfp = False OptionSet.__init__(self, args) + try: + f = open(Option.cfpath, 'r') + f.close() + except IOError: + e = sys.exc_info()[1] + print("Warning! Unable to read specified configuration file: %s" % e) diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index 6a4020b12..1ceed702d 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -992,6 +992,12 @@ class EntrySet: self.entry_init(event) else: if event.filename not in self.entries: + logger.warning("Got %s event for unknown file %s" % + (action, event.filename)) + if action == 'changed': + # received a bogus changed event; warn, but treat + # it like a created event + self.entry_init(event) return if action == 'changed': self.entries[event.filename].handle_event(event) diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Server/Plugins/Cfg.py index d2e6e8a1d..1ea8e4e75 100644 --- a/src/lib/Server/Plugins/Cfg.py +++ b/src/lib/Server/Plugins/Cfg.py @@ -221,32 +221,32 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet): return "%s.H_%s" % (bfname, specific.hostname) def write_update(self, specific, new_entry, log): - # FIXME: need to redo print with python's logging module if 'text' in new_entry: name = self.build_filename(specific) if os.path.exists("%s.genshi" % name): - print("Cfg: Unable to pull data for genshi types") + self.logger.error("Cfg: Unable to pull data for genshi types") raise Bcfg2.Server.Plugin.PluginExecutionError elif os.path.exists("%s.cheetah" % name): - print("Cfg: Unable to pull data for cheetah types") + self.logger.error("Cfg: Unable to pull data for cheetah types") raise Bcfg2.Server.Plugin.PluginExecutionError try: etext = new_entry['text'].encode(self.encoding) except: - print("Cfg: Cannot encode content of %s as %s" % (name, self.encoding)) + self.logger.error("Cfg: Cannot encode content of %s as %s" % (name, self.encoding)) raise Bcfg2.Server.Plugin.PluginExecutionError open(name, 'w').write(etext) - if log: - print("Wrote file %s" % name) + self.debug_log("Wrote file %s" % name, flag=log) badattr = [attr for attr in ['owner', 'group', 'perms'] if attr in new_entry] if badattr: # check for info files and inform user of their removal if os.path.exists(self.path + "/:info"): - print("Removing :info file and replacing with info.xml") + self.logger.info("Removing :info file and replacing with " + "info.xml") os.remove(self.path + "/:info") if os.path.exists(self.path + "/info"): - print("Removing info file and replacing with info.xml") + self.logger.info("Removing info file and replacing with " + "info.xml") os.remove(self.path + "/info") metadata_updates = {} metadata_updates.update(self.metadata) @@ -259,8 +259,8 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet): ofile = open(self.path + "/info.xml", "w") ofile.write(lxml.etree.tostring(infoxml, pretty_print=True)) ofile.close() - if log: - print("Wrote file %s" % (self.path + "/info.xml")) + self.debug_log("Wrote file %s" % (self.path + "/info.xml"), + flag=log) class Cfg(Bcfg2.Server.Plugin.GroupSpool, diff --git a/src/lib/Server/Plugins/FileProbes.py b/src/lib/Server/Plugins/FileProbes.py index 269664ef4..c58040b65 100644 --- a/src/lib/Server/Plugins/FileProbes.py +++ b/src/lib/Server/Plugins/FileProbes.py @@ -6,6 +6,7 @@ the client """ __revision__ = '$Revision: 1465 $' import os +import sys import errno import binascii import lxml.etree @@ -89,33 +90,27 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin, interpreter="/usr/bin/env python") probe.text = probecode % path self.probes[metadata.hostname].append(probe) - self.logger.debug("Adding file probe for %s to %s" % - (path, metadata.hostname)) + self.debug_log("Adding file probe for %s to %s" % + (path, metadata.hostname)) return self.probes[metadata.hostname] def ReceiveData(self, metadata, datalist): """Receive data from probe.""" - self.logger.debug("Receiving file probe data from %s" % - metadata.hostname) + self.debug_log("Receiving file probe data from %s" % metadata.hostname) for data in datalist: if data.text is None: self.logger.error("Got null response to %s file probe from %s" % (data.get('name'), metadata.hostname)) else: - self.logger.debug("%s:fileprobe:%s:%s" % - (metadata.hostname, - data.get("name"), - data.text)) try: - filedata = lxml.etree.XML(data.text) - self.write_file(filedata, metadata) + self.write_data(lxml.etree.XML(data.text), metadata) except lxml.etree.XMLSyntaxError: # if we didn't get XML back from the probe, assume # it's an error message self.logger.error(data.text) - def write_file(self, data, metadata): + def write_data(self, data, metadata): """Write the probed file data to the bcfg2 specification.""" filename = data.get("name") contents = binascii.a2b_base64(data.text) @@ -139,74 +134,80 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin, entrydata = entry.text if create: - self.logger.info("Writing new probed file %s" % fileloc) - try: - os.makedirs(os.path.dirname(fileloc)) - except OSError, err: - if err.errno == errno.EEXIST: - pass - else: - raise - open(fileloc, 'wb').write(contents) - - infoxml = os.path.join("%s%s" % (cfg.data, filename), - "info.xml") - if not os.path.exists(infoxml): - self.write_infoxml(infoxml, entry, data) - - # Service the FAM events queued up by the key generation - # so the data structure entries will be available for - # binding. - # - # NOTE: We wait for up to ten seconds. There is some - # potential for race condition, because if the file - # monitor doesn't get notified about the new key files in - # time, those entries won't be available for binding. In - # practice, this seems "good enough". - tries = 0 - is_bound = False - while not is_bound: - if tries >= 10: - self.logger.error("%s still not registered" % filename) - raise Bcfg2.Server.Plugin.PluginExecutionError - self.core.fam.handle_events_in_interval(1) - try: - cfg.entries[filename].bind_entry(entry, metadata) - is_bound = True - except Bcfg2.Server.Plugin.PluginExecutionError: - pass - tries += 1 + self.logger.info("Writing new probed file %s" % fileloc) + self.write_file(fileloc, contents) + self.verify_file(filename, contents, metadata) + infoxml = os.path.join("%s%s" % (cfg.data, filename), "info.xml") + self.write_infoxml(infoxml, entry, data) elif entrydata == contents: - self.logger.debug("Existing %s contents match probed contents" % - filename) + self.debug_log("Existing %s contents match probed contents" % + filename) return elif (entry.get('update', 'false').lower() == "true"): self.logger.info("Writing updated probed file %s" % fileloc) - open(fileloc, 'wb').write(contents) - - # service FAM events - tries = 0 - updated = False - while not updated: - if tries >= 10: - self.logger.error("%s still not registered" % filename) - raise Bcfg2.Server.Plugin.PluginExecutionError - self.core.fam.handle_events_in_interval(1) - cfg.entries[filename].bind_entry(entry, metadata) - # get current entry data - if entry.get("encoding") == "base64": - entrydata = binascii.a2b_base64(entry.text) - else: - entrydata = entry.text - if entrydata == contents: - updated = True - tries += 1 + self.write_file(fileloc, contents) + self.verify_file(filename, contents, metadata) else: self.logger.info("Skipping updated probed file %s" % fileloc) return + + def write_file(self, fileloc, contents): + try: + os.makedirs(os.path.dirname(fileloc)) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EEXIST: + pass + else: + self.logger.error("Could not create parent directories for %s: " + "%s" % (fileloc, err)) + return + + try: + open(fileloc, 'wb').write(contents) + except IOError: + err = sys.exc_info()[1] + self.logger.error("Could not write %s: %s" % (fileloc, err)) + return + + def verify_file(self, filename, contents, metadata): + # Service the FAM events queued up by the key generation so + # the data structure entries will be available for binding. + # + # NOTE: We wait for up to ten seconds. There is some potential + # for race condition, because if the file monitor doesn't get + # notified about the new key files in time, those entries + # won't be available for binding. In practice, this seems + # "good enough". + entry = self.entries[metadata.hostname][filename] + cfg = self.core.plugins['Cfg'] + tries = 0 + updated = False + while not updated: + if tries >= 10: + self.logger.error("%s still not registered" % filename) + return + self.core.fam.handle_events_in_interval(1) + try: + cfg.entries[filename].bind_entry(entry, metadata) + except Bcfg2.Server.Plugin.PluginExecutionError: + tries += 1 + continue + + # get current entry data + if entry.get("encoding") == "base64": + entrydata = binascii.a2b_base64(entry.text) + else: + entrydata = entry.text + if entrydata == contents: + updated = True + tries += 1 def write_infoxml(self, infoxml, entry, data): """ write an info.xml for the file """ + if os.path.exists(infoxml): + return + self.logger.info("Writing info.xml at %s for %s" % (infoxml, data.get("name"))) info = \ @@ -222,5 +223,10 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin, root = lxml.etree.Element("FileInfo") root.append(info) - open(infoxml, "w").write(lxml.etree.tostring(root, - pretty_print=True)) + try: + open(infoxml, "w").write(lxml.etree.tostring(root, + pretty_print=True)) + except IOError: + err = sys.exc_info()[1] + self.logger.error("Could not write %s: %s" % (fileloc, err)) + return diff --git a/src/lib/Server/Plugins/Packages/Source.py b/src/lib/Server/Plugins/Packages/Source.py index 910a90cac..1dfeecc40 100644 --- a/src/lib/Server/Plugins/Packages/Source.py +++ b/src/lib/Server/Plugins/Packages/Source.py @@ -136,7 +136,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable): def get_repo_name(self, url_map): # try to find a sensible name for a repo if url_map['component']: - return url_map['component'] + rname = url_map['component'] else: name = None for repo_re in (self.mrepo_re, @@ -144,14 +144,18 @@ class Source(Bcfg2.Server.Plugin.Debuggable): self.genericrepo_re): match = repo_re.search(url_map['url']) if match: - name = match.group(1).replace('/', '-') break if name is None: # couldn't figure out the name from the URL or URL map # (which probably means its a screwy URL), so we just # generate a random one name = base64.b64encode(os.urandom(16))[:-2] - return "%s-%s" % (self.groups[0], name) + rname = "%s-%s" % (self.groups[0], name) + # see yum/__init__.py in the yum source, lines 441-449, for + # the source of this regex. yum doesn't like anything but + # string.ascii_letters, string.digits, and [-_.:]. There + # doesn't seem to be a reason for this, because yum. + return re.sub(r'[^A-Za-z0-9-_.:]', '-', rname) def __str__(self): if self.rawurl: diff --git a/src/lib/Server/Plugins/Packages/Yum.py b/src/lib/Server/Plugins/Packages/Yum.py index a5fe706cd..416602b27 100644 --- a/src/lib/Server/Plugins/Packages/Yum.py +++ b/src/lib/Server/Plugins/Packages/Yum.py @@ -124,6 +124,7 @@ class YumCollection(Collection): mainopts = dict(cachedir=self.cachefile, keepcache="0", sslverify="0", + debuglevel="0", reposdir="/dev/null") try: for opt in self.config.options("yum"): @@ -394,6 +395,11 @@ class YumCollection(Collection): if rv: self.logger.error("Packages: error running bcfg2-yum-helper " "(returned %d): %s" % (rv, stderr)) + elif self.debug_flag: + self.logger.debug("Packages: debug info from bcfg2-yum-helper: %s" % + stderr) + self.logger.debug("Packages: output from bcfg2-yum-helper: %s" % + stderr) try: return json.loads(stdout) except ValueError: diff --git a/src/lib/Server/Plugins/SSHbase.py b/src/lib/Server/Plugins/SSHbase.py index 724d169f5..eb91bea39 100644 --- a/src/lib/Server/Plugins/SSHbase.py +++ b/src/lib/Server/Plugins/SSHbase.py @@ -235,6 +235,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, if entry.specific.match(event.filename): entry.handle_event(event) if event.filename.endswith(".pub"): + self.logger.info("New public key %s; invalidating " + "ssh_known_hosts cache" % event.filename) self.skn = False return @@ -244,6 +246,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, return if event.filename.endswith('.static'): + self.logger.info("Static key %s %s; invalidating ssh_known_hosts " + "cache" % (event.filename, action)) if action == "deleted" and event.filename in self.static: del self.static[event.filename] self.skn = False diff --git a/src/lib/Server/Plugins/Trigger.py b/src/lib/Server/Plugins/Trigger.py index f6dd47e12..eb3310a4e 100644 --- a/src/lib/Server/Plugins/Trigger.py +++ b/src/lib/Server/Plugins/Trigger.py @@ -26,12 +26,19 @@ class Trigger(Bcfg2.Server.Plugin.Plugin, try: os.stat(self.data) except: - self.logger.error("Trigger: spool directory %s does not exist; unloading" % self.data) + self.logger.error("Trigger: spool directory %s does not exist; " + "unloading" % self.data) raise Bcfg2.Server.Plugin.PluginInitError def process_statistics(self, metadata, _): args = [metadata.hostname, '-p', metadata.profile, '-g', ':'.join([g for g in metadata.groups])] for notifier in os.listdir(self.data): - n = self.data + '/' + notifier - async_run(n, args) + if ((notifier[-1] == '~') or + (notifier[:2] == '.#') or + (notifier[-4:] == '.swp') or + (notifier in ['SCCS', '.svn', '4913'])): + continue + npath = self.data + '/' + notifier + self.logger.debug("Running %s %s" % (npath, " ".join(args))) + async_run(npath, args) diff --git a/src/lib/Server/Reports/reports/templates/base.html b/src/lib/Server/Reports/reports/templates/base.html index a12bf66ba..f541c0d2b 100644 --- a/src/lib/Server/Reports/reports/templates/base.html +++ b/src/lib/Server/Reports/reports/templates/base.html @@ -87,7 +87,7 @@ <div style='clear:both'></div> </div><!-- document --> <div id="footer"> - <span>Bcfg2 Version 1.2.1</span> + <span>Bcfg2 Version 1.2.2</span> </div> <div id="calendar_div" style='position:absolute; visibility:hidden; background-color:white; layer-background-color:white;'></div> |