From 827d0a83b8c9148598c23cb550862c0cf50b5a23 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 18 Jul 2013 14:22:11 -0400 Subject: Packages: added lock to yum cache update --- src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/lib/Bcfg2/Server/Plugins/Packages/Yum.py') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index 7c950a435..7a90f4f2e 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -53,11 +53,13 @@ The Yum Backend import os import re import sys +import time import copy import errno import socket import logging import lxml.etree +from lockfile import FileLock from subprocess import Popen, PIPE import Bcfg2.Server.Plugin # pylint: disable=W0622 @@ -864,6 +866,17 @@ class YumCollection(Collection): if not self.use_yum: return Collection.complete(self, packagelist) + lock = FileLock(os.path.join(self.cachefile, "lock")) + slept = 0 + while lock.is_locked(): + if slept > 30: + self.logger.warning("Packages: Timeout waiting for yum cache " + "to release its lock") + return set(), set() + self.logger.debug("Packages: Yum cache is locked, waiting...") + time.sleep(3) + slept += 3 + if packagelist: try: result = self.call_helper( -- cgit v1.2.3-1-g7c22 From 7d544a289852c761fc209772064f794fb0472198 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Mon, 22 Jul 2013 09:08:32 -0400 Subject: Packages: Added timeout to bcfg2-yum-helper calls This involved making the Yum backend use Bcfg2.Utils.Executor to call bcfg2-yum-helper instead of subprocess.Popen directly. This was cherry-picked (kinda) from 3d06f311274d6b942ee89d8cdb13b2ecc99af1b0, so will likely break the maint -> master merge in spectacular ways. --- src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 54 +++++++++++----------------- 1 file changed, 20 insertions(+), 34 deletions(-) (limited to 'src/lib/Bcfg2/Server/Plugins/Packages/Yum.py') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index 7a90f4f2e..e0002ef34 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -59,9 +59,9 @@ import errno import socket import logging import lxml.etree -from lockfile import FileLock -from subprocess import Popen, PIPE import Bcfg2.Server.Plugin +from lockfile import FileLock +from Bcfg2.Utils import Executor # pylint: disable=W0622 from Bcfg2.Compat import StringIO, cPickle, HTTPError, URLError, \ ConfigParser, any @@ -279,6 +279,7 @@ class YumCollection(Collection): debug=debug) self.keypath = os.path.join(self.cachepath, "keys") + self._helper = None if self.use_yum: #: Define a unique cache file for this collection to use #: for cached yum metadata @@ -294,8 +295,10 @@ class YumCollection(Collection): os.mkdir(self.cachefile) if not self.disableMetaData: self.setup_data() + self.cmd = Executor() else: self.cachefile = None + self.cmd = None if HAS_PULP and self.has_pulp_sources: _setup_pulp(self.setup) @@ -348,20 +351,18 @@ class YumCollection(Collection): a call to it; I wish there was a way to do this without forking, but apparently not); finally we check in /usr/sbin, the default location. """ - global HELPER - if not HELPER: + if not self._helper: try: - HELPER = self.setup.cfp.get("packages:yum", "helper") + self._helper = self.setup.cfp.get("packages:yum", "helper") except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): # first see if bcfg2-yum-helper is in PATH try: self.debug_log("Checking for bcfg2-yum-helper in $PATH") - Popen(['bcfg2-yum-helper'], - stdin=PIPE, stdout=PIPE, stderr=PIPE).wait() - HELPER = 'bcfg2-yum-helper' + self.cmd.run(['bcfg2-yum-helper']) + self._helper = 'bcfg2-yum-helper' except OSError: - HELPER = "/usr/sbin/bcfg2-yum-helper" - return HELPER + self._helper = "/usr/sbin/bcfg2-yum-helper" + return self._helper @property def use_yum(self): @@ -925,36 +926,21 @@ class YumCollection(Collection): cmd.append("-v") cmd.append(command) self.debug_log("Packages: running %s" % " ".join(cmd)) - try: - helper = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) - except OSError: - err = sys.exc_info()[1] - self.logger.error("Packages: Failed to execute %s: %s" % - (" ".join(cmd), err)) - return None if inputdata: - idata = json.dumps(inputdata) - (stdout, stderr) = helper.communicate(idata) + result = self.cmd.run(cmd, timeout=self.setup['client_timeout'], + inputdata=json.dumps(inputdata)) else: - (stdout, stderr) = helper.communicate() - rv = helper.wait() - errlines = stderr.splitlines() - if rv: - if not errlines: - errlines.append("No error output") - self.logger.error("Packages: error running bcfg2-yum-helper " - "(returned %d): %s" % (rv, errlines[0])) - for line in errlines[1:]: - self.logger.error("Packages: %s" % line) - elif errlines: + result = self.cmd.run(cmd, timeout=self.setup['client_timeout']) + if not result.success: + self.logger.error("Packages: error running bcfg2-yum-helper: %s" % + result.error) + elif result.stderr: self.debug_log("Packages: debug info from bcfg2-yum-helper: %s" % - errlines[0]) - for line in errlines[1:]: - self.debug_log("Packages: %s" % line) + result.stderr) try: - return json.loads(stdout) + return json.loads(result.stdout) except ValueError: err = sys.exc_info()[1] self.logger.error("Packages: error reading bcfg2-yum-helper " -- cgit v1.2.3-1-g7c22 From 752da22a2247892f647c0a9c46e7b0faf9351ea6 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Mon, 22 Jul 2013 11:44:23 -0400 Subject: Packages: instantiate Executor before determining path to helper --- src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/lib/Bcfg2/Server/Plugins/Packages/Yum.py') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index e0002ef34..48c5b1f65 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -104,9 +104,6 @@ FL = '{http://linux.duke.edu/metadata/filelists}' PULPSERVER = None PULPCONFIG = None -#: The path to bcfg2-yum-helper -HELPER = None - def _setup_pulp(setup): """ Connect to a Pulp server and pass authentication credentials. @@ -279,6 +276,10 @@ class YumCollection(Collection): debug=debug) self.keypath = os.path.join(self.cachepath, "keys") + #: A :class:`Bcfg2.Utils.Executor` object to use to run + #: external commands + self.cmd = Executor() + self._helper = None if self.use_yum: #: Define a unique cache file for this collection to use @@ -295,10 +296,8 @@ class YumCollection(Collection): os.mkdir(self.cachefile) if not self.disableMetaData: self.setup_data() - self.cmd = Executor() else: self.cachefile = None - self.cmd = None if HAS_PULP and self.has_pulp_sources: _setup_pulp(self.setup) -- cgit v1.2.3-1-g7c22