summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/Bcfg2/Client/Tools/APT.py102
-rw-r--r--src/lib/Bcfg2/Client/Tools/Action.py22
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIXUsers.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/SYSV.py43
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py20
-rw-r--r--src/lib/Bcfg2/Compat.py4
-rw-r--r--src/lib/Bcfg2/Reporting/Collector.py38
-rw-r--r--src/lib/Bcfg2/Reporting/Storage/DjangoORM.py11
-rw-r--r--src/lib/Bcfg2/Reporting/models.py3
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/item.html2
-rw-r--r--src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py77
-rwxr-xr-xsrc/lib/Bcfg2/Reporting/utils.py12
-rw-r--r--src/lib/Bcfg2/Server/BuiltinCore.py27
-rw-r--r--src/lib/Bcfg2/Server/Core.py8
-rw-r--r--src/lib/Bcfg2/Server/FileMonitor/__init__.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Apt.py3
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Source.py19
18 files changed, 245 insertions, 154 deletions
diff --git a/src/lib/Bcfg2/Client/Tools/APT.py b/src/lib/Bcfg2/Client/Tools/APT.py
index cf4e7c7ea..9fcb7a3e0 100644
--- a/src/lib/Bcfg2/Client/Tools/APT.py
+++ b/src/lib/Bcfg2/Client/Tools/APT.py
@@ -5,6 +5,7 @@ import warnings
warnings.filterwarnings("ignore", "apt API not stable yet",
FutureWarning)
import os
+import sys
import apt.cache
import Bcfg2.Options
import Bcfg2.Client.Tools
@@ -12,7 +13,7 @@ import Bcfg2.Client.Tools
class APT(Bcfg2.Client.Tools.Tool):
"""The Debian toolset implements package and service operations
- and inherits the rest from Tools.Tool. """
+ and inherits the rest from Tools.Tool."""
options = Bcfg2.Client.Tools.Tool.options + [
Bcfg2.Options.PathOption(
@@ -79,10 +80,14 @@ class APT(Bcfg2.Client.Tools.Tool):
try:
self.pkg_cache = apt.cache.Cache()
except SystemError:
- e = sys.exc_info()[1]
- self.logger.info("Failed to initialize APT cache: %s" % e)
+ err = sys.exc_info()[1]
+ self.logger.info("Failed to initialize APT cache: %s" % err)
raise Bcfg2.Client.Tools.ToolInstantiationError
- self.pkg_cache.update()
+ try:
+ self.pkg_cache.update()
+ except apt.cache.FetchFailedException:
+ err = sys.exc_info()[1]
+ self.logger.info("Failed to update APT cache: %s" % err)
self.pkg_cache = apt.cache.Cache()
if 'req_reinstall_pkgs' in dir(self.pkg_cache):
self._newapi = True
@@ -103,9 +108,10 @@ class APT(Bcfg2.Client.Tools.Tool):
for (name, version) in extras]
def VerifyDebsums(self, entry, modlist):
+ """Verify the package contents with debsum information."""
output = \
self.cmd.run("%s -as %s" %
- (self.debsums, entry.get('name'))).stdout.splitlines()
+ (self.debsums, entry.get('name'))).stderr.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'))
@@ -127,11 +133,11 @@ class APT(Bcfg2.Client.Tools.Tool):
# these files should not exist
continue
elif "is not installed" in item or "missing file" in item:
- self.logger.error("Package %s is not fully installed" %
- entry.get('name'))
+ self.logger.error("Package %s is not fully installed"
+ % entry.get('name'))
else:
- self.logger.error("Got Unsupported pattern %s from debsums" %
- item)
+ self.logger.error("Got Unsupported pattern %s from debsums"
+ % item)
files.append(item)
files = list(set(files) - set(self.ignores))
# We check if there is file in the checksum to do
@@ -142,30 +148,31 @@ class APT(Bcfg2.Client.Tools.Tool):
bad = [filename for filename in files if filename not in modlist]
if bad:
self.logger.debug("It is suggested that you either manage "
- "these files, revert the changes, or ignore "
- "false failures:")
- self.logger.info("Package %s failed validation. Bad files "
- "are:" % entry.get('name'))
+ "these files, revert the changes, or "
+ "ignore false failures:")
+ self.logger.info("Package %s failed validation. Bad files are:"
+ % entry.get('name'))
self.logger.info(bad)
- entry.set('qtext',
- "Reinstall Package %s-%s to fix failing files? "
- "(y/N) " % (entry.get('name'), entry.get('version')))
+ entry.set(
+ 'qtext',
+ "Reinstall Package %s-%s to fix failing files? (y/N) "
+ % (entry.get('name'), entry.get('version')))
return False
return True
def VerifyPackage(self, entry, modlist, checksums=True):
"""Verify package for entry."""
- if not 'version' in entry.attrib:
+ if 'version' not in entry.attrib:
self.logger.info("Cannot verify unversioned package %s" %
(entry.attrib['name']))
return False
pkgname = entry.get('name')
- if self.pkg_cache.has_key(pkgname): # nopep8
+ if self.pkg_cache.has_key(pkgname): # noqa
if self._newapi:
is_installed = self.pkg_cache[pkgname].is_installed
else:
is_installed = self.pkg_cache[pkgname].isInstalled
- if not self.pkg_cache.has_key(pkgname) or not is_installed: # nopep8
+ if not self.pkg_cache.has_key(pkgname) or not is_installed: # noqa
self.logger.info("Package %s not installed" % (entry.get('name')))
entry.set('current_exists', 'false')
return False
@@ -178,31 +185,33 @@ class APT(Bcfg2.Client.Tools.Tool):
installed_version = pkg.installedVersion
candidate_version = pkg.candidateVersion
if entry.get('version') == 'auto':
+ # pylint: disable=W0212
if self._newapi:
- is_upgradable = \
- self.pkg_cache._depcache.is_upgradable(pkg._pkg)
+ is_upgradable = self.pkg_cache._depcache.is_upgradable(
+ pkg._pkg)
else:
- is_upgradable = \
- self.pkg_cache._depcache.IsUpgradable(pkg._pkg)
+ is_upgradable = self.pkg_cache._depcache.IsUpgradable(
+ pkg._pkg)
+ # pylint: enable=W0212
if is_upgradable:
- desiredVersion = candidate_version
+ desired_version = candidate_version
else:
- desiredVersion = installed_version
+ desired_version = installed_version
elif entry.get('version') == 'any':
- desiredVersion = installed_version
+ desired_version = installed_version
else:
- desiredVersion = entry.get('version')
- if desiredVersion != installed_version:
+ desired_version = entry.get('version')
+ if desired_version != installed_version:
entry.set('current_version', installed_version)
entry.set('qtext', "Modify Package %s (%s -> %s)? (y/N) " %
(entry.get('name'), entry.get('current_version'),
- desiredVersion))
+ desired_version))
return False
else:
# version matches
- if (not Bcfg2.Options.setup.quick and
- entry.get('verify', 'true') == 'true'
- and checksums):
+ if not Bcfg2.Options.setup.quick \
+ and entry.get('verify', 'true') == 'true' \
+ and checksums:
pkgsums = self.VerifyDebsums(entry, modlist)
return pkgsums
return True
@@ -220,7 +229,7 @@ class APT(Bcfg2.Client.Tools.Tool):
self.pkg_cache[pkg].mark_delete(purge=True)
else:
self.pkg_cache[pkg].markDelete(purge=True)
- except:
+ except: # pylint: disable=W0702
if self._newapi:
self.pkg_cache[pkg].mark_delete()
else:
@@ -240,24 +249,26 @@ class APT(Bcfg2.Client.Tools.Tool):
ipkgs = []
bad_pkgs = []
for pkg in packages:
- if not self.pkg_cache.has_key(pkg.get('name')): # nopep8
- self.logger.error("APT has no information about package %s" %
- (pkg.get('name')))
+ if not self.pkg_cache.has_key(pkg.get('name')): # noqa
+ self.logger.error("APT has no information about package %s"
+ % (pkg.get('name')))
continue
if pkg.get('version') in ['auto', 'any']:
if self._newapi:
try:
- cversion = \
- self.pkg_cache[pkg.get('name')].candidate.version
- ipkgs.append("%s=%s" % (pkg.get('name'), cversion))
+ ipkgs.append("%s=%s" % (
+ pkg.get('name'),
+ self.pkg_cache[pkg.get('name')].candidate.version))
except AttributeError:
self.logger.error("Failed to find %s in apt package "
"cache" % pkg.get('name'))
continue
else:
- cversion = self.pkg_cache[pkg.get('name')].candidateVersion
- ipkgs.append("%s=%s" % (pkg.get('name'), cversion))
+ ipkgs.append("%s=%s" % (
+ pkg.get('name'),
+ self.pkg_cache[pkg.get('name')].candidateVersion))
continue
+ # pylint: disable=W0212
if self._newapi:
avail_vers = [
x.ver_str for x in
@@ -266,13 +277,14 @@ class APT(Bcfg2.Client.Tools.Tool):
avail_vers = [
x.VerStr for x in
self.pkg_cache[pkg.get('name')]._pkg.VersionList]
+ # pylint: enable=W0212
if pkg.get('version') in avail_vers:
ipkgs.append("%s=%s" % (pkg.get('name'), pkg.get('version')))
continue
else:
- self.logger.error("Package %s: desired version %s not in %s" %
- (pkg.get('name'), pkg.get('version'),
- avail_vers))
+ self.logger.error("Package %s: desired version %s not in %s"
+ % (pkg.get('name'), pkg.get('version'),
+ avail_vers))
bad_pkgs.append(pkg.get('name'))
if bad_pkgs:
self.logger.error("Cannot find correct versions of packages:")
@@ -290,6 +302,6 @@ class APT(Bcfg2.Client.Tools.Tool):
self.modified.append(package)
return states
- def VerifyPath(self, entry, _):
+ def VerifyPath(self, entry, _): # pylint: disable=W0613
"""Do nothing here since we only verify Path type=ignore."""
return True
diff --git a/src/lib/Bcfg2/Client/Tools/Action.py b/src/lib/Bcfg2/Client/Tools/Action.py
index dedc50d89..ca0502b75 100644
--- a/src/lib/Bcfg2/Client/Tools/Action.py
+++ b/src/lib/Bcfg2/Client/Tools/Action.py
@@ -2,7 +2,6 @@
import Bcfg2.Client.Tools
from Bcfg2.Utils import safe_input
-from Bcfg2.Client import matches_white_list, passes_black_list
class Action(Bcfg2.Client.Tools.Tool):
@@ -11,23 +10,6 @@ class Action(Bcfg2.Client.Tools.Tool):
__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
- the whitelist or blacklist """
- if (Bcfg2.Options.setup.decision == 'whitelist' and
- not matches_white_list(action,
- Bcfg2.Options.setup.decision_list)):
- self.logger.info("In whitelist mode: suppressing Action: %s" %
- action.get('name'))
- return False
- if (Bcfg2.Options.setup.decision == 'blacklist' and
- not passes_black_list(action,
- Bcfg2.Options.setup.decision_list)):
- self.logger.info("In blacklist mode: suppressing Action: %s" %
- action.get('name'))
- return False
- return True
-
def RunAction(self, entry):
"""This method handles command execution and status return."""
shell = False
@@ -76,7 +58,7 @@ class Action(Bcfg2.Client.Tools.Tool):
states = dict()
for action in bundle.findall("Action"):
if action.get('timing') in ['post', 'both']:
- if not self._action_allowed(action):
+ if not self._install_allowed(action):
continue
states[action] = self.RunAction(action)
return states
@@ -87,7 +69,7 @@ class Action(Bcfg2.Client.Tools.Tool):
for action in bundle.findall("Action"):
if (action.get('timing') in ['post', 'both'] and
action.get('when') != 'modified'):
- if not self._action_allowed(action):
+ if not self._install_allowed(action):
continue
states[action] = self.RunAction(action)
return states
diff --git a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py
index a7fcb6709..7200b0fc2 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py
@@ -160,7 +160,7 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool):
""" Get a list of supplmentary groups that the user in the
given entry is a member of """
return [g for g in self.existing['POSIXGroup'].values()
- if entry.get("name") in g[3] and g[0] != entry.get("group")
+ if entry.get("name") in g[3]
and self._in_managed_range('POSIXGroup', g[2])]
def VerifyPOSIXUser(self, entry, _):
diff --git a/src/lib/Bcfg2/Client/Tools/SYSV.py b/src/lib/Bcfg2/Client/Tools/SYSV.py
index 5698f237a..332638de4 100644
--- a/src/lib/Bcfg2/Client/Tools/SYSV.py
+++ b/src/lib/Bcfg2/Client/Tools/SYSV.py
@@ -4,6 +4,8 @@ import tempfile
from Bcfg2.Compat import any # pylint: disable=W0622
import Bcfg2.Client.Tools
import Bcfg2.Client.XML
+from Bcfg2.Compat import urlretrieve
+
# pylint: disable=C0103
noask = '''
@@ -37,6 +39,8 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
# noaskfile needs to live beyond __init__ otherwise file is removed
self.noaskfile = tempfile.NamedTemporaryFile()
self.noaskname = self.noaskfile.name
+ # for any pkg files downloaded
+ self.tmpfiles = []
try:
self.noaskfile.write(noask)
# flush admin file contents to disk
@@ -45,6 +49,41 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
self.pkgtool[1])
except: # pylint: disable=W0702
self.pkgtool = (self.pkgtool[0] % "", self.pkgtool[1])
+ self.origpkgtool = self.pkgtool
+
+ def pkgmogrify(self, packages):
+ """ Take a list of pkg objects, check for a 'simplefile' attribute.
+ If present, insert a _sysv_pkg_path attribute to the package and
+ download the datastream format SYSV package to a temporary file.
+ """
+ for pkg in packages:
+ if pkg.get('simplefile'):
+ tmpfile = tempfile.NamedTemporaryFile()
+ self.tmpfiles.append(tmpfile)
+ self.logger.info("Downloading %s to %s" % (pkg.get('url'),
+ tmpfile.name))
+ urlretrieve(pkg.get('url'), tmpfile.name)
+ pkg.set('_sysv_pkg_path', tmpfile.name)
+
+ def _get_package_command(self, packages):
+ """Override the default _get_package_command, replacing the attribute
+ 'url' if '_sysv_pkg_path' if necessary in the returned command
+ string
+ """
+ if hasattr(self, 'origpkgtool'):
+ if len(packages) == 1 and '_sysv_pkg_path' in packages[0].keys():
+ self.pkgtool = (self.pkgtool[0], ('%s %s',
+ ['_sysv_pkg_path', 'name']))
+ else:
+ self.pkgtool = self.origpkgtool
+
+ pkgcmd = super(SYSV, self)._get_package_command(packages)
+ self.logger.debug("Calling install command: %s" % pkgcmd)
+ return pkgcmd
+
+ def Install(self, packages):
+ self.pkgmogrify(packages)
+ super(SYSV, self).Install(packages)
def RefreshPackages(self):
"""Refresh memory hashes of packages."""
@@ -80,8 +119,8 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
self.logger.debug("Package %s not installed" %
entry.get("name"))
else:
- if (Bcfg2.Options.setup.quick or
- entry.attrib.get('verify', 'true') == 'false'):
+ if Bcfg2.Options.setup.quick or \
+ entry.attrib.get('verify', 'true') == 'false':
return True
rv = self.cmd.run("/usr/sbin/pkgchk -n %s" % entry.get('name'))
if rv.success:
diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py
index b1caf3332..a8a80974a 100644
--- a/src/lib/Bcfg2/Client/Tools/YUM.py
+++ b/src/lib/Bcfg2/Client/Tools/YUM.py
@@ -977,8 +977,8 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
nevra2string(build_yname(pkg.get('name'), inst)))
continue
status = self.instance_status[inst]
- if (not status.get('installed', False) and
- Bcfg2.Options.setup.yum_install_missing):
+ if not status.get('installed', False) and \
+ Bcfg2.Options.setup.yum_install_missing:
queue_pkg(pkg, inst, install_pkgs)
elif (status.get('version_fail', False) and
Bcfg2.Options.setup.yum_fix_version):
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index cd294db98..ae7fa3aed 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -129,6 +129,23 @@ class Tool(object):
raise ToolInstantiationError("%s: %s not executable" %
(self.name, filename))
+ def _install_allowed(self, entry):
+ """ Return true if the given entry is allowed to be installed by
+ the whitelist or blacklist """
+ if (Bcfg2.Options.setup.decision == 'whitelist' and
+ not Bcfg2.Client.matches_white_list(
+ entry, Bcfg2.Options.setup.decision_list)):
+ self.logger.info("In whitelist mode: suppressing Action: %s" %
+ entry.get('name'))
+ return False
+ if (Bcfg2.Options.setup.decision == 'blacklist' and
+ not Bcfg2.Client.passes_black_list(
+ entry, Bcfg2.Options.setup.decision_list)):
+ self.logger.info("In blacklist mode: suppressing Action: %s" %
+ entry.get('name'))
+ return False
+ return True
+
def BundleUpdated(self, bundle): # pylint: disable=W0613
""" Callback that is invoked when a bundle has been updated.
@@ -587,7 +604,8 @@ class SvcTool(Tool):
return
for entry in bundle:
- if not self.handlesEntry(entry):
+ if (not self.handlesEntry(entry)
+ or not self._install_allowed(entry)):
continue
estatus = entry.get('status')
diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py
index 049236e03..b8a75a0c5 100644
--- a/src/lib/Bcfg2/Compat.py
+++ b/src/lib/Bcfg2/Compat.py
@@ -20,6 +20,7 @@ except ImportError:
# urllib imports
try:
from urllib import quote_plus
+ from urllib import urlretrieve
from urlparse import urljoin, urlparse
from urllib2 import HTTPBasicAuthHandler, \
HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, \
@@ -27,7 +28,8 @@ try:
except ImportError:
from urllib.parse import urljoin, urlparse, quote_plus
from urllib.request import HTTPBasicAuthHandler, \
- HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, urlopen
+ HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, \
+ urlopen, urlretrieve
from urllib.error import HTTPError, URLError
try:
diff --git a/src/lib/Bcfg2/Reporting/Collector.py b/src/lib/Bcfg2/Reporting/Collector.py
index e29bd5a99..153809a35 100644
--- a/src/lib/Bcfg2/Reporting/Collector.py
+++ b/src/lib/Bcfg2/Reporting/Collector.py
@@ -1,3 +1,4 @@
+import os
import sys
import atexit
import daemon
@@ -5,13 +6,12 @@ import logging
import time
import threading
-# pylint: disable=E0611
from lockfile import LockFailed, LockTimeout
+# pylint: disable=E0611
try:
- from lockfile.pidlockfile import PIDLockFile
- from lockfile import Error as PIDFileError
+ from daemon.pidfile import TimeoutPIDLockFile
except ImportError:
- from daemon.pidlockfile import PIDLockFile, PIDFileError
+ from daemon.pidlockfile import TimeoutPIDLockFile
# pylint: enable=E0611
import Bcfg2.Logger
@@ -129,25 +129,31 @@ class ReportingCollector(object):
if Bcfg2.Options.setup.daemon:
self.logger.debug("Daemonizing")
+ self.context.pidfile = TimeoutPIDLockFile(
+ Bcfg2.Options.setup.daemon, acquire_timeout=5)
+ # Attempt to ensure lockfile is able to be created and not stale
try:
- self.context.pidfile = PIDLockFile(Bcfg2.Options.setup.daemon)
- self.context.open()
+ self.context.pidfile.acquire()
except LockFailed:
self.logger.error("Failed to daemonize: %s" %
sys.exc_info()[1])
self.shutdown()
return
except LockTimeout:
- self.logger.error("Failed to daemonize: "
- "Failed to acquire lock on %s" %
- self.setup['daemon'])
- self.shutdown()
- return
- except PIDFileError:
- self.logger.error("Error writing pid file: %s" %
- sys.exc_info()[1])
- self.shutdown()
- return
+ try: # attempt to break the lock
+ os.kill(self.context.pidfile.read_pid(), 0)
+ except (OSError, TypeError): # No process with locked PID
+ self.context.pidfile.break_lock()
+ else:
+ self.logger.error("Failed to daemonize: "
+ "Failed to acquire lock on %s" %
+ Bcfg2.Options.setup.daemon)
+ self.shutdown()
+ return
+ else:
+ self.context.pidfile.release()
+
+ self.context.open()
self.logger.info("Starting daemon")
self.transport.start_monitor(self)
diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
index 406216861..96226c424 100644
--- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
+++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
@@ -168,7 +168,7 @@ class DjangoORM(StorageBase):
# TODO - vcs output
act_dict['detail_type'] = PathEntry.DETAIL_UNUSED
if path_type == 'directory' and entry.get('prune', 'false') == 'true':
- unpruned_elist = [e.get('path') for e in entry.findall('Prune')]
+ unpruned_elist = [e.get('name') for e in entry.findall('Prune')]
if unpruned_elist:
act_dict['detail_type'] = PathEntry.DETAIL_PRUNED
act_dict['details'] = "\n".join(unpruned_elist)
@@ -367,10 +367,11 @@ class DjangoORM(StorageBase):
def import_interaction(self, interaction):
"""Import the data into the backend"""
try:
- self._import_interaction(interaction)
- except:
- self.logger.error("Failed to import interaction: %s" %
- traceback.format_exc().splitlines()[-1])
+ try:
+ self._import_interaction(interaction)
+ except:
+ self.logger.error("Failed to import interaction: %s" %
+ traceback.format_exc().splitlines()[-1])
finally:
self.logger.debug("%s: Closing database connection" %
self.__class__.__name__)
diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py
index 2d96990b1..ae6f6731b 100644
--- a/src/lib/Bcfg2/Reporting/models.py
+++ b/src/lib/Bcfg2/Reporting/models.py
@@ -717,9 +717,6 @@ class PathEntry(SuccessEntry):
def has_detail(self):
return self.detail_type != PathEntry.DETAIL_UNUSED
- def is_sensitive(self):
- return self.detail_type == PathEntry.DETAIL_SENSITIVE
-
def is_diff(self):
return self.detail_type == PathEntry.DETAIL_DIFF
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/item.html b/src/lib/Bcfg2/Reporting/templates/config_items/item.html
index b03d48045..c6e6df020 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/item.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/item.html
@@ -107,7 +107,7 @@ div.entry_list h3 {
{{ item.details|syntaxhilight }}
</div>
{% else %}
- {{ item.details }}
+ {{ item.details|linebreaks }}
{% endif %}
</div>
{% endif %}
diff --git a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
index 4a93e77e0..09aebc7fd 100644
--- a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
+++ b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
@@ -111,47 +111,58 @@ def filter_navigator(context):
try:
path = context['request'].META['PATH_INFO']
view, args, kwargs = resolve(path)
+ except (Resolver404, KeyError):
+ return dict()
- # Strip any page limits and numbers
- if 'page_number' in kwargs:
- del kwargs['page_number']
- if 'page_limit' in kwargs:
- del kwargs['page_limit']
-
- # get a query string
- qs = context['request'].GET.urlencode()
- if qs:
- qs = '?' + qs
-
- filters = []
- for filter in filter_list:
- if filter == 'group':
- continue
- if filter in kwargs:
- myargs = kwargs.copy()
- del myargs[filter]
+ # Strip any page limits and numbers
+ if 'page_number' in kwargs:
+ del kwargs['page_number']
+ if 'page_limit' in kwargs:
+ del kwargs['page_limit']
+
+ # get a query string
+ qs = context['request'].GET.urlencode()
+ if qs:
+ qs = '?' + qs
+
+ filters = []
+ for filter in filter_list:
+ if filter == 'group':
+ continue
+ if filter in kwargs:
+ myargs = kwargs.copy()
+ del myargs[filter]
+ try:
filters.append((filter,
reverse(view, args=args, kwargs=myargs) + qs))
- filters.sort(key=lambda x: x[0])
-
- myargs = kwargs.copy()
- selected = True
- if 'group' in myargs:
- del myargs['group']
- selected = False
- groups = [('---',
- reverse(view, args=args, kwargs=myargs) + qs,
- selected)]
- for group in Group.objects.values('name'):
+ except NoReverseMatch:
+ pass
+ filters.sort(key=lambda x: x[0])
+
+ myargs = kwargs.copy()
+ selected = True
+ if 'group' in myargs:
+ del myargs['group']
+ selected = False
+
+ groups = []
+ try:
+ groups.append(('---',
+ reverse(view, args=args, kwargs=myargs) + qs,
+ selected))
+ except NoReverseMatch:
+ pass
+
+ for group in Group.objects.values('name'):
+ try:
myargs['group'] = group['name']
groups.append((group['name'],
reverse(view, args=args, kwargs=myargs) + qs,
group['name'] == kwargs.get('group', '')))
+ except NoReverseMatch:
+ pass
- return {'filters': filters, 'groups': groups}
- except (Resolver404, NoReverseMatch, ValueError, KeyError):
- pass
- return dict()
+ return {'filters': filters, 'groups': groups}
def _subtract_or_na(mdict, x, y):
diff --git a/src/lib/Bcfg2/Reporting/utils.py b/src/lib/Bcfg2/Reporting/utils.py
index 0d394fcd8..694f38824 100755
--- a/src/lib/Bcfg2/Reporting/utils.py
+++ b/src/lib/Bcfg2/Reporting/utils.py
@@ -96,12 +96,12 @@ def filteredUrls(pattern, view, kwargs=None, name=None):
tail = mtail.group(1)
pattern = pattern[:len(pattern) - len(tail)]
for filter in ('/state/(?P<state>\w+)',
- '/group/(?P<group>[\w\-\.]+)',
- '/group/(?P<group>[\w\-\.]+)/(?P<state>[A-Za-z]+)',
- '/server/(?P<server>[\w\-\.]+)',
- '/server/(?P<server>[\w\-\.]+)/(?P<state>[A-Za-z]+)',
- '/server/(?P<server>[\w\-\.]+)/group/(?P<group>[\w\-\.]+)',
- '/server/(?P<server>[\w\-\.]+)/group/(?P<group>[\w\-\.]+)/(?P<state>[A-Za-z]+)'):
+ '/group/(?P<group>[^/]+)',
+ '/group/(?P<group>[^/]+)/(?P<state>[A-Za-z]+)',
+ '/server/(?P<server>[^/]+)',
+ '/server/(?P<server>[^/]+)/(?P<state>[A-Za-z]+)',
+ '/server/(?P<server>[^/]+)/group/(?P<group>[^/]+)',
+ '/server/(?P<server>[^/]+)/group/(?P<group>[^/]+)/(?P<state>[A-Za-z]+)'):
results += [(pattern + filter + tail, view, kwargs)]
return results
diff --git a/src/lib/Bcfg2/Server/BuiltinCore.py b/src/lib/Bcfg2/Server/BuiltinCore.py
index 769addf55..e138c57e4 100644
--- a/src/lib/Bcfg2/Server/BuiltinCore.py
+++ b/src/lib/Bcfg2/Server/BuiltinCore.py
@@ -1,5 +1,6 @@
""" The core of the builtin Bcfg2 server. """
+import os
import sys
import time
import socket
@@ -85,20 +86,30 @@ class BuiltinCore(NetworkCore):
def _daemonize(self):
""" Open :attr:`context` to drop privileges, write the PID
file, and daemonize the server core. """
+ # Attempt to ensure lockfile is able to be created and not stale
try:
- self.context.open()
- self.logger.info("%s daemonized" % self.name)
- return True
+ self.context.pidfile.acquire()
except LockFailed:
err = sys.exc_info()[1]
self.logger.error("Failed to daemonize %s: %s" % (self.name, err))
return False
except LockTimeout:
- err = sys.exc_info()[1]
- self.logger.error("Failed to daemonize %s: Failed to acquire lock "
- "on %s" % (self.name,
- Bcfg2.Options.setup.daemon))
- return False
+ try: # attempt to break the lock
+ os.kill(self.context.pidfile.read_pid(), 0)
+ except (OSError, TypeError): # No process with locked PID
+ self.context.pidfile.break_lock()
+ else:
+ err = sys.exc_info()[1]
+ self.logger.error("Failed to daemonize %s: Failed to acquire"
+ "lock on %s" % (self.name,
+ Bcfg2.Options.setup.daemon))
+ return False
+ else:
+ self.context.pidfile.release()
+
+ self.context.open()
+ self.logger.info("%s daemonized" % self.name)
+ return True
def _run(self):
""" Create :attr:`server` to start the server listening. """
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 892f2832a..bc305e47a 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -84,7 +84,7 @@ def close_db_connection(func):
if self._database_available: # pylint: disable=W0212
from django import db
self.logger.debug("%s: Closing database connection" %
- threading.current_thread().name)
+ threading.current_thread().getName())
db.close_connection()
return rv
@@ -783,13 +783,13 @@ class Core(object):
for plug in self.plugins_by_type(Threaded):
plug.start_threads()
+
+ self.block_for_fam_events()
+ self._block()
except:
self.shutdown()
raise
- self.block_for_fam_events()
- self._block()
-
def _run(self):
""" Start up the server; this method should return
immediately. This must be overridden by a core
diff --git a/src/lib/Bcfg2/Server/FileMonitor/__init__.py b/src/lib/Bcfg2/Server/FileMonitor/__init__.py
index d0fd70c5c..8e0dd2efe 100644
--- a/src/lib/Bcfg2/Server/FileMonitor/__init__.py
+++ b/src/lib/Bcfg2/Server/FileMonitor/__init__.py
@@ -238,6 +238,8 @@ class FileMonitor(Debuggable):
self.handles[event.requestID]))
try:
self.handles[event.requestID].HandleEvent(event)
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
err = sys.exc_info()[1]
self.logger.error("Error in handling of event %s for %s: %s" %
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
index 3d5c68e3f..cfabd8457 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
@@ -102,7 +102,8 @@ class AptSource(Source):
bdeps[barch][pkgname] = []
brecs[barch][pkgname] = []
elif words[0] == 'Essential' and self.essential:
- self.essentialpkgs.add(pkgname)
+ if words[1].strip() == 'yes':
+ self.essentialpkgs.add(pkgname)
elif words[0] in ['Depends', 'Pre-Depends', 'Recommends']:
vindex = 0
for dep in words[1].split(','):
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
index 24db2963d..67ada2399 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
@@ -199,6 +199,9 @@ class Source(Debuggable): # pylint: disable=R0902
#: The "version" attribute from :attr:`xsource`
self.version = xsource.get('version', '')
+ #: The "name" attribute from :attr:`xsource`
+ self.name = xsource.get('name', None)
+
#: A list of predicates that are used to determine if this
#: source applies to a given
#: :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata`
@@ -274,11 +277,11 @@ class Source(Debuggable): # pylint: disable=R0902
for arch in self.arches:
if self.url:
usettings = [dict(version=self.version, component=comp,
- arch=arch)
+ arch=arch, debsrc=self.debsrc)
for comp in self.components]
else: # rawurl given
usettings = [dict(version=self.version, component=None,
- arch=arch)]
+ arch=arch, debsrc=self.debsrc)]
for setting in usettings:
if not self.rawurl:
@@ -286,6 +289,7 @@ class Source(Debuggable): # pylint: disable=R0902
else:
setting['baseurl'] = self.rawurl
setting['url'] = baseurl % setting
+ setting['name'] = self.get_repo_name(setting)
self.url_map.extend(usettings)
@property
@@ -353,7 +357,7 @@ class Source(Debuggable): # pylint: disable=R0902
if os.path.exists(self.cachefile):
try:
self.load_state()
- except:
+ except (OSError, cPickle.UnpicklingError):
err = sys.exc_info()[1]
self.logger.error("Packages: Cachefile %s load failed: %s"
% (self.cachefile, err))
@@ -388,8 +392,10 @@ class Source(Debuggable): # pylint: disable=R0902
doing other operations that require repository names. This
function tries several approaches:
- #. First, if the map contains a ``component`` key, use that as
- the name.
+ #. First, if the source element containts a ``name`` attribute,
+ use that as the name.
+ #. If the map contains a ``component`` key, use that as the
+ name.
#. If not, then try to match the repository URL against
:attr:`Bcfg2.Server.Plugins.Packages.Source.REPO_RE`. If
that succeeds, use the first matched group; additionally,
@@ -419,6 +425,9 @@ class Source(Debuggable): # pylint: disable=R0902
:type url_map: dict
:returns: string - the name of the repository.
"""
+ if self.name:
+ return self.name
+
if url_map['component']:
rname = url_map['component']
else: