From b5ed496121ccf4661c48967cdf4ba487d8b80cab Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Wed, 12 Jul 2017 14:45:10 +0200 Subject: [PATCH 01/29] Add basic windows support for bcfg2 --- src/lib/Bcfg2/Client/Tools/WinAction.py | 79 ++++++++++ src/lib/Bcfg2/Client/Tools/WinFS.py | 178 +++++++++++++++++++++++ src/lib/Bcfg2/Client/Tools/WinService.py | 87 +++++++++++ src/lib/Bcfg2/Client/__init__.py | 40 +++-- src/lib/Bcfg2/Logger.py | 7 +- src/lib/Bcfg2/Options/Types.py | 5 +- src/lib/Bcfg2/Utils.py | 31 ++-- 7 files changed, 399 insertions(+), 28 deletions(-) create mode 100755 src/lib/Bcfg2/Client/Tools/WinAction.py create mode 100755 src/lib/Bcfg2/Client/Tools/WinFS.py create mode 100755 src/lib/Bcfg2/Client/Tools/WinService.py diff --git a/src/lib/Bcfg2/Client/Tools/WinAction.py b/src/lib/Bcfg2/Client/Tools/WinAction.py new file mode 100755 index 0000000000..cbc4b1088e --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/WinAction.py @@ -0,0 +1,79 @@ +"""WinAction driver""" +import subprocess + +import Bcfg2.Client.Tools +from Bcfg2.Utils import safe_input + + +class WinAction(Bcfg2.Client.Tools.Tool): + """Implement Actions""" + name = 'WinAction' + __handles__ = [('Action', None)] + __req__ = {'Action': ['name', 'timing', 'when', 'command', 'status']} + + def RunAction(self, entry): + """This method handles command execution and status return.""" + shell = True + shell_string = '' + if entry.get('shell', 'false') == 'true': + shell = True + shell_string = '(in shell) ' + + if not Bcfg2.Options.setup.dry_run: + if Bcfg2.Options.setup.interactive: + prompt = ('Run Action %s%s, %s: (y/N): ' % + (shell_string, entry.get('name'), + entry.get('command'))) + ans = safe_input(prompt) + if ans not in ['y', 'Y']: + return False + if False: + if entry.get('build', 'true') == 'false': + self.logger.debug("Action: Deferring execution of %s due " + "to build mode" % entry.get('command')) + return False + self.logger.debug("Running Action %s %s" % + (shell_string, entry.get('name'))) + rv = self.cmd.run(entry.get('command'), shell=shell, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False) + self.logger.debug("Action: %s got return code %s" % + (entry.get('command'), rv.retval)) + entry.set('rc', str(rv.retval)) + return entry.get('status', 'check') == 'ignore' or rv.success + else: + self.logger.debug("In dryrun mode: not running action: %s" % + (entry.get('name'))) + return False + + def VerifyAction(self, dummy, _): + """Actions always verify true.""" + return True + + def InstallAction(self, entry): + """Run actions as pre-checks for bundle installation.""" + if entry.get('timing') != 'post': + return self.RunAction(entry) + return True + + def BundleUpdated(self, bundle): + """Run postinstalls when bundles have been updated.""" + states = dict() + for action in bundle.findall("Action"): + if action.get('timing') in ['post', 'both']: + if not self._install_allowed(action): + continue + states[action] = self.RunAction(action) + return states + + def BundleNotUpdated(self, bundle): + """Run Actions when bundles have not been updated.""" + states = dict() + for action in bundle.findall("Action"): + if (action.get('timing') in ['post', 'both'] and + action.get('when') != 'modified'): + if not self._install_allowed(action): + continue + states[action] = self.RunAction(action) + return states diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py new file mode 100755 index 0000000000..6aa73597a7 --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -0,0 +1,178 @@ +"""All Windows Type client support for Bcfg2.""" +import sys +import os +import stat +import tempfile +import Bcfg2.Options +import Bcfg2.Client.Tools + + +class WinFS(Bcfg2.Client.Tools.Tool): + """Windows File support code.""" + name = 'WinFS' + __handles__ = [('Path', 'file')] + + def __init__(self, config): + Bcfg2.Client.Tools.Tool.__init__(self, config) + self.__req__ = dict(Path=dict()) + self.__req__['Path']['file'] = ['name', 'mode', 'owner', 'group'] + + def _getFilePath(self, entry): + filePath = os.path.expandvars(os.path.normpath(entry.get('name')[1:])) + if(not filePath[1] == ':'): + self.logger.info("Skipping \"%s\" because it doesnt look like a Windows Path" % filePath) + return False + return filePath + + def VerifyPath(self, entry, _): + """Path always verify true.""" + filePath = self._getFilePath(entry) + if(not filePath): + return False + ondisk = self._exists(filePath) + tempdata, is_binary = self._get_data(entry) + if isinstance(tempdata, str) and str != unicode: + tempdatasize = len(tempdata) + else: + tempdatasize = len(tempdata.encode(Bcfg2.Options.setup.encoding)) + + different = False + content = None + if not ondisk: + # first, see if the target file exists at all; if not, + # they're clearly different + different = True + content = "" + elif tempdatasize != ondisk[stat.ST_SIZE]: + # next, see if the size of the target file is different + # from the size of the desired content + different = True + else: + # finally, read in the target file and compare them + # directly. comparison could be done with a checksum, + # which might be faster for big binary files, but slower + # for everything else + try: + content = open(filePath).read() + except UnicodeDecodeError: + content = open(filePath, + encoding=Bcfg2.Options.setup.encoding).read() + except IOError: + self.logger.error("Windows: Failed to read %s: %s" % + (filePath, sys.exc_info()[1])) + return False + different = str(content) != str(tempdata) + return not different + + def InstallPath(self, entry): + """Install device entries.""" + filePath = self._getFilePath(entry) + + if not filePath: + return False + + self.logger.debug("Installing: " + filePath) + if not os.path.exists(os.path.dirname(filePath)): + if not self._makedirs(path=filePath): + return False + newfile = self._write_tmpfile(entry, filePath) + if not newfile: + return False + rv = True + if not self._rename_tmpfile(newfile, filePath): + return False + + return rv + + def _makedirs(self, path): + """ os.makedirs helpfully creates all parent directories for us.""" + created = [] + cur = path + #while cur and cur != '/': + # if not os.path.exists(cur): + # created.append(cur) + # cur = os.path.dirname(cur) + rv = True + try: + os.makedirs(os.path.dirname(path)) + except OSError: + err = sys.exc_info()[1] + self.logger.error('Windows: Failed to create directory %s: %s' % + (path, err)) + rv = False + return rv + + def _write_tmpfile(self, entry, filePath): + """ Write the file data to a temp file """ + filedata = self._get_data(entry)[0] + # get a temp file to write to that is in the same directory as + # the existing file in order to preserve any permissions + # protections on that directory, and also to avoid issues with + # /tmp set nosetuid while creating files that are supposed to + # be setuid + try: + (newfd, newfile) = \ + tempfile.mkstemp(prefix=os.path.basename(filePath), + dir=os.path.dirname(filePath)) + except OSError: + err = sys.exc_info()[1] + self.logger.error("Windows: Failed to create temp file in %s: %s" % (filePath, err)) + return False + try: + if isinstance(filedata, str) and str != unicode: + os.fdopen(newfd, 'w').write(filedata) + else: + os.fdopen(newfd, 'wb').write( + filedata.encode(Bcfg2.Options.setup.encoding)) + except (OSError, IOError): + err = sys.exc_info()[1] + self.logger.error("Windows: Failed to open temp file %s for writing " + "%s: %s" % + (newfile, filePath, err)) + return False + return newfile + + def _get_data(self, entry): + """ Get a tuple of (, ) for the given entry """ + is_binary = entry.get('encoding', 'ascii') == 'base64' + if entry.get('empty', 'false') == 'true' or not entry.text: + tempdata = '' + elif is_binary: + tempdata = b64decode(entry.text) + else: + tempdata = entry.text + if isinstance(tempdata, unicode) and unicode != str: + try: + tempdata = tempdata.encode(Bcfg2.Options.setup.encoding) + except UnicodeEncodeError: + err = sys.exc_info()[1] + self.logger.error("Windows: Error encoding file %s: %s" % + (entry.get('name'), err)) + return (tempdata, is_binary) + + def _rename_tmpfile(self, newfile, filePath): + """ Rename the given file to the appropriate filename for entry """ + try: + if(os.path.isfile(filePath)): + os.unlink(filePath) + os.rename(newfile, filePath) + return True + except OSError: + err = sys.exc_info()[1] + self.logger.error("Windows: Failed to rename temp file %s to %s: %s" + % (newfile, filePath, err)) + try: + os.unlink(newfile) + except OSError: + err = sys.exc_info()[1] + self.logger.error("Windows: Could not remove temp file %s: %s" % + (newfile, err)) + return False + + def _exists(self, filePath): + """ check for existing paths and optionally remove them. if + the path exists, return the lstat of it """ + try: + return os.lstat(filePath) + except OSError: + return None diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py new file mode 100755 index 0000000000..28b3011b95 --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -0,0 +1,87 @@ +"""WinService support for Bcfg2.""" + +import glob +import re +import subprocess + +import Bcfg2.Client.Tools +import Bcfg2.Client.XML + + +class WinService(Bcfg2.Client.Tools.SvcTool): + """WinService service support for Bcfg2.""" + name = 'WinService' + __handles__ = [('Service', 'windows')] + __req__ = {'Service': ['name', 'status']} + + def get_svc_command(self, service, action): + return "powershell.exe %s-Service %s" % (action, service.get('name')) + + def VerifyService(self, entry, _): + """Verify Service status for entry + """ + + if entry.get('status') == 'ignore': + return True + + if entry.get('parameters'): + params = entry.get('parameters') + else: + params = '' + + try: + output = self.cmd.run('powershell.exe (Get-Service %s).Status' % + (entry.get('name')), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).stdout.splitlines()[0] + except IndexError: + self.logger.error("Service %s not an Windows service" % + entry.get('name')) + return False + + + if output is None: + # service does not exist + entry.set('current_status', 'off') + status = False + elif output.lower() == 'running': + # service is running + entry.set('current_status', 'on') + if entry.get('status') == 'off': + status = False + else: + status = True + else: + # service is not running + entry.set('current_status', 'off') + if entry.get('status') == 'on': + status = False + else: + status = True + + return status + + def InstallService(self, entry): + """Install Service for entry.""" + if entry.get('status') == 'on': + cmd = "start" + elif entry.get('status') == 'off': + cmd = "stop" + return self.cmd.run(self.get_svc_command(entry, cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False).success + + def restart_service(self, service): + """Restart a service. + + :param service: The service entry to modify + :type service: lxml.etree._Element + :returns: Bcfg2.Utils.ExecutorResult - The return value from + :class:`Bcfg2.Utils.Executor.run` + """ + self.logger.debug('Restarting service %s' % service.get('name')) + restart_target = service.get('target', 'restart') + return self.cmd.run(self.get_svc_command(service, restart_target), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + + def FindExtra(self): + """Locate extra Windows services.""" + return [] diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index dc4dfb983e..9dddb303cd 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -4,7 +4,6 @@ import sys import stat import time -import fcntl import socket import fnmatch import logging @@ -13,6 +12,10 @@ import copy import Bcfg2.Logger import Bcfg2.Options +if os.name == 'posix': + import fcntl +elif os.name == 'nt': + import subprocess from Bcfg2.Client import XML from Bcfg2.Client import Proxy from Bcfg2.Client import Tools @@ -196,7 +199,10 @@ def run_probe(self, probe): self.logger.info("Running probe %s" % name) ret = XML.Element("probe-data", name=name, source=probe.get('source')) try: - scripthandle, scriptname = tempfile.mkstemp() + fileending = '' + if os.name == 'nt': + (interpreter, fileending) = probe.attrib.get('interpreter').split('*') + scripthandle, scriptname = tempfile.mkstemp(suffix = fileending) if sys.hexversion >= 0x03000000: script = os.fdopen(scripthandle, 'w', encoding=Bcfg2.Options.setup.encoding) @@ -210,11 +216,17 @@ def run_probe(self, probe): else: script.write(probe.text.encode('utf-8')) script.close() - os.chmod(scriptname, - stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | - stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | - stat.S_IWUSR) # 0755 - rv = self.cmd.run(scriptname) + if os.name == 'nt': + rv = self.cmd.run([interpreter, scriptname], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False) + else: + os.chmod(scriptname, + stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | + stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | + stat.S_IWUSR) # 0755 + rv = self.cmd.run(scriptname) if rv.stderr: self.logger.warning("Probe %s has error output: %s" % (name, rv.stderr)) @@ -409,15 +421,13 @@ def run(self): if not Bcfg2.Options.setup.no_lock: # check lock here try: - lockfile = open(Bcfg2.Options.setup.lockfile, 'w') - if locked(lockfile.fileno()): + if locked(Bcfg2.Options.setup.lockfile): self.fatal_error("Another instance of Bcfg2 is running. " "If you want to bypass the check, run " "with the -O/--no-lock option") except SystemExit: raise except: - lockfile = None self.logger.error("Failed to open lockfile %s: %s" % (Bcfg2.Options.setup.lockfile, sys.exc_info()[1])) @@ -427,10 +437,14 @@ def run(self): if not Bcfg2.Options.setup.no_lock: # unlock here - if lockfile: + if os.path.isfile(Bcfg2.Options.setup.lockfile): try: - fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN) - os.remove(Bcfg2.Options.setup.lockfile) + if os.name == 'nt': + os.unlink(Bcfg2.Options.setup.lockfile) + else: + lockfile = open(file, 'w') + fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN) + os.remove(Bcfg2.Options.setup.lockfile) except OSError: self.logger.error("Failed to unlock lockfile %s" % lockfile.name) diff --git a/src/lib/Bcfg2/Logger.py b/src/lib/Bcfg2/Logger.py index e5f316a186..1b2cb4151f 100644 --- a/src/lib/Bcfg2/Logger.py +++ b/src/lib/Bcfg2/Logger.py @@ -1,14 +1,15 @@ """Bcfg2 logging support""" - +import os import copy -import fcntl import logging import logging.handlers import math import socket import struct import sys -import termios +if os.name != 'nt': + import fcntl + import termios import Bcfg2.Options logging.raiseExceptions = 0 diff --git a/src/lib/Bcfg2/Options/Types.py b/src/lib/Bcfg2/Options/Types.py index ad2e04f10f..f3dce44d56 100644 --- a/src/lib/Bcfg2/Options/Types.py +++ b/src/lib/Bcfg2/Options/Types.py @@ -3,8 +3,9 @@ import os import re -import pwd -import grp +if os.name != 'nt': + import pwd + import grp from Bcfg2.Compat import literal_eval _COMMA_SPLIT_RE = re.compile(r'\s*,\s*') diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 2fdc0c3e0d..8cb267e99e 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -1,8 +1,6 @@ """ Miscellaneous useful utility functions, classes, etc., that are used by both client and server. Stuff that doesn't fit anywhere else. """ - -import fcntl import logging import os import re @@ -11,6 +9,8 @@ import sys import subprocess import threading +if os.name != 'nt': + import fcntl from Bcfg2.Compat import input, any # pylint: disable=W0622 @@ -86,18 +86,29 @@ def __str__(self): return "[%s]" % self.str -def locked(fd): +def locked(file): """ Acquire a lock on a file. - :param fd: The file descriptor to lock - :type fd: int + :param file: The path to the lockfile + :type fd: string :returns: bool - True if the file is already locked, False otherwise """ - try: - fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError: - return True - return False + if os.name == 'nt': + if (os.path.isfile(file)): + return True + try: + lockfile = open(file, 'w') + lockfile.write(str(os.getpid())) + except IOError: + return True + return False + else: + lockfile = open(file, 'w') + try: + fcntl.lockf(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + return True + return False class ExecutorResult(object): From 354df6394fa2a110081d33875ccee5344d996a7d Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 18 Jul 2017 12:25:43 +0200 Subject: [PATCH 02/29] Fix PEP-8 Style issues --- src/lib/Bcfg2/Client/Tools/WinFS.py | 95 ++++++++++++------------ src/lib/Bcfg2/Client/Tools/WinService.py | 23 +++--- src/lib/Bcfg2/Client/__init__.py | 3 +- src/lib/Bcfg2/Utils.py | 10 +-- 4 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index 6aa73597a7..6fe1cc3985 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -16,21 +16,24 @@ def __init__(self, config): Bcfg2.Client.Tools.Tool.__init__(self, config) self.__req__ = dict(Path=dict()) self.__req__['Path']['file'] = ['name', 'mode', 'owner', 'group'] - + def _getFilePath(self, entry): - filePath = os.path.expandvars(os.path.normpath(entry.get('name')[1:])) - if(not filePath[1] == ':'): - self.logger.info("Skipping \"%s\" because it doesnt look like a Windows Path" % filePath) + """Evaluates the enviroment Variables and returns the file path""" + file_path = os.path.expandvars(os.path.normpath(entry.get('name')[1:])) + if(not file_path[1] == ':'): + self.logger.info( + "Skipping \"%s\" because it doesnt look like a Windows Path" % + file_path) return False - return filePath - + return file_path + def VerifyPath(self, entry, _): """Path always verify true.""" - filePath = self._getFilePath(entry) - if(not filePath): + file_path = self._getFilePath(entry) + if(not file_path): return False - ondisk = self._exists(filePath) - tempdata, is_binary = self._get_data(entry) + ondisk = self._exists(file_path) + tempdata = self._get_data(entry)[0] if isinstance(tempdata, str) and str != unicode: tempdatasize = len(tempdata) else: @@ -53,45 +56,39 @@ def VerifyPath(self, entry, _): # which might be faster for big binary files, but slower # for everything else try: - content = open(filePath).read() + content = open(file_path).read() except UnicodeDecodeError: - content = open(filePath, + content = open(file_path, encoding=Bcfg2.Options.setup.encoding).read() except IOError: self.logger.error("Windows: Failed to read %s: %s" % - (filePath, sys.exc_info()[1])) + (file_path, sys.exc_info()[1])) return False different = str(content) != str(tempdata) return not different - + def InstallPath(self, entry): """Install device entries.""" - filePath = self._getFilePath(entry) + file_path = self._getFilePath(entry) - if not filePath: + if not file_path: return False - - self.logger.debug("Installing: " + filePath) - if not os.path.exists(os.path.dirname(filePath)): - if not self._makedirs(path=filePath): + + self.logger.debug("Installing: " + file_path) + if not os.path.exists(os.path.dirname(file_path)): + if not self._makedirs(path=file_path): return False - newfile = self._write_tmpfile(entry, filePath) + newfile = self._write_tmpfile(entry, file_path) if not newfile: return False rv = True - if not self._rename_tmpfile(newfile, filePath): + if not self._rename_tmpfile(newfile, file_path): return False return rv def _makedirs(self, path): """ os.makedirs helpfully creates all parent directories for us.""" - created = [] - cur = path - #while cur and cur != '/': - # if not os.path.exists(cur): - # created.append(cur) - # cur = os.path.dirname(cur) rv = True try: os.makedirs(os.path.dirname(path)) @@ -102,7 +99,7 @@ def _makedirs(self, path): rv = False return rv - def _write_tmpfile(self, entry, filePath): + def _write_tmpfile(self, entry, file_path): """ Write the file data to a temp file """ filedata = self._get_data(entry)[0] # get a temp file to write to that is in the same directory as @@ -112,11 +109,12 @@ def _write_tmpfile(self, entry, filePath): # be setuid try: (newfd, newfile) = \ - tempfile.mkstemp(prefix=os.path.basename(filePath), - dir=os.path.dirname(filePath)) + tempfile.mkstemp(prefix=os.path.basename(file_path), + dir=os.path.dirname(file_path)) except OSError: err = sys.exc_info()[1] - self.logger.error("Windows: Failed to create temp file in %s: %s" % (filePath, err)) + self.logger.error( + "Windows: Failed to create temp file in %s: %s" % (file_path, err)) return False try: if isinstance(filedata, str) and str != unicode: @@ -126,9 +124,10 @@ def _write_tmpfile(self, entry, filePath): filedata.encode(Bcfg2.Options.setup.encoding)) except (OSError, IOError): err = sys.exc_info()[1] - self.logger.error("Windows: Failed to open temp file %s for writing " - "%s: %s" % - (newfile, filePath, err)) + self.logger.error( + "Windows: Failed to open temp file %s for writing " + "%s: %s" % + (newfile, file_path, err)) return False return newfile @@ -149,30 +148,32 @@ def _get_data(self, entry): self.logger.error("Windows: Error encoding file %s: %s" % (entry.get('name'), err)) return (tempdata, is_binary) - - def _rename_tmpfile(self, newfile, filePath): + + def _rename_tmpfile(self, newfile, file_path): """ Rename the given file to the appropriate filename for entry """ try: - if(os.path.isfile(filePath)): - os.unlink(filePath) - os.rename(newfile, filePath) + if(os.path.isfile(file_path)): + os.unlink(file_path) + os.rename(newfile, file_path) return True except OSError: err = sys.exc_info()[1] - self.logger.error("Windows: Failed to rename temp file %s to %s: %s" - % (newfile, filePath, err)) + self.logger.error( + "Windows: Failed to rename temp file %s to %s: %s" + % (newfile, file_path, err)) try: os.unlink(newfile) except OSError: err = sys.exc_info()[1] - self.logger.error("Windows: Could not remove temp file %s: %s" % - (newfile, err)) + self.logger.error( + "Windows: Could not remove temp file %s: %s" % + (newfile, err)) return False - - def _exists(self, filePath): + + def _exists(self, file_path): """ check for existing paths and optionally remove them. if the path exists, return the lstat of it """ try: - return os.lstat(filePath) + return os.lstat(file_path) except OSError: return None diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py index 28b3011b95..d96cf0de13 100755 --- a/src/lib/Bcfg2/Client/Tools/WinService.py +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -1,7 +1,5 @@ """WinService support for Bcfg2.""" -import glob -import re import subprocess import Bcfg2.Client.Tools @@ -18,17 +16,11 @@ def get_svc_command(self, service, action): return "powershell.exe %s-Service %s" % (action, service.get('name')) def VerifyService(self, entry, _): - """Verify Service status for entry - """ + """Verify Service status for entry""" if entry.get('status') == 'ignore': return True - if entry.get('parameters'): - params = entry.get('parameters') - else: - params = '' - try: output = self.cmd.run('powershell.exe (Get-Service %s).Status' % (entry.get('name')), @@ -40,7 +32,6 @@ def VerifyService(self, entry, _): entry.get('name')) return False - if output is None: # service does not exist entry.set('current_status', 'off') @@ -68,8 +59,11 @@ def InstallService(self, entry): cmd = "start" elif entry.get('status') == 'off': cmd = "stop" - return self.cmd.run(self.get_svc_command(entry, cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False).success - + return self.cmd.run(self.get_svc_command(entry, cmd), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).success + def restart_service(self, service): """Restart a service. @@ -80,7 +74,10 @@ def restart_service(self, service): """ self.logger.debug('Restarting service %s' % service.get('name')) restart_target = service.get('target', 'restart') - return self.cmd.run(self.get_svc_command(service, restart_target), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + return self.cmd.run(self.get_svc_command(service, restart_target), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False) def FindExtra(self): """Locate extra Windows services.""" diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 9dddb303cd..45c799ffda 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -201,7 +201,8 @@ def run_probe(self, probe): try: fileending = '' if os.name == 'nt': - (interpreter, fileending) = probe.attrib.get('interpreter').split('*') + (interpreter, fileending) = + probe.attrib.get('interpreter').split('*') scripthandle, scriptname = tempfile.mkstemp(suffix = fileending) if sys.hexversion >= 0x03000000: script = os.fdopen(scripthandle, 'w', diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 8cb267e99e..e7b6717903 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -86,24 +86,24 @@ def __str__(self): return "[%s]" % self.str -def locked(file): +def locked(file_path): """ Acquire a lock on a file. - :param file: The path to the lockfile + :param file_path: The path to the lockfile :type fd: string :returns: bool - True if the file is already locked, False otherwise """ if os.name == 'nt': - if (os.path.isfile(file)): + if (os.path.isfile(file_path)): return True try: - lockfile = open(file, 'w') + lockfile = open(file_path, 'w') lockfile.write(str(os.getpid())) except IOError: return True return False else: - lockfile = open(file, 'w') + lockfile = open(file_path, 'w') try: fcntl.lockf(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: From cb7c96503c84e60cf6e169c41bbc048b6183f3c7 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 18 Jul 2017 14:18:00 +0200 Subject: [PATCH 03/29] Return extra services in WinService --- src/lib/Bcfg2/Client/Tools/WinService.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py index d96cf0de13..b627b2d434 100755 --- a/src/lib/Bcfg2/Client/Tools/WinService.py +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -81,4 +81,12 @@ def restart_service(self, service): def FindExtra(self): """Locate extra Windows services.""" - return [] + list = self.cmd.run("powershell.exe " + "\"Get-WMIObject win32_service -Filter " + "\\\"StartMode = 'auto'\\\"" + " | Format-Table Name -HideTableHeaders\"", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).stdout.splitlines() + return [Bcfg2.Client.XML.Element('Service', name=name, type='windows') + for name in list] From 63a45c2ca334c5d9ccf0c98a57efca3a0356b20f Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 18 Jul 2017 14:35:15 +0200 Subject: [PATCH 04/29] Add import for binary Files --- src/lib/Bcfg2/Client/Tools/WinFS.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index 6fe1cc3985..b6b1af62c6 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -5,7 +5,7 @@ import tempfile import Bcfg2.Options import Bcfg2.Client.Tools - +from Bcfg2.Compat import unicode, b64encode, b64decode class WinFS(Bcfg2.Client.Tools.Tool): """Windows File support code.""" From 7c7990c0bc0a9cf417c896a9420654a222e6d8aa Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Fri, 21 Jul 2017 11:18:41 +0200 Subject: [PATCH 05/29] Implement a locking mechanism which works on Windows and Linux --- src/lib/Bcfg2/Client/__init__.py | 29 +++------- src/lib/Bcfg2/Utils.py | 93 ++++++++++++++++++++++++-------- 2 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 45c799ffda..54dace0433 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -12,14 +12,11 @@ import copy import Bcfg2.Logger import Bcfg2.Options -if os.name == 'posix': - import fcntl -elif os.name == 'nt': - import subprocess +import subprocess from Bcfg2.Client import XML from Bcfg2.Client import Proxy from Bcfg2.Client import Tools -from Bcfg2.Utils import locked, Executor, safe_input +from Bcfg2.Utils import Flock, Executor, safe_input from Bcfg2.version import __version__ # pylint: disable=W0622 from Bcfg2.Compat import xmlrpclib, walk_packages, any, all, cmp @@ -161,7 +158,7 @@ def __init__(self): self.tools = [] self.times = dict() self.times['initialization'] = time.time() - + self.lock = Flock(Bcfg2.Options.setup.lockfile) if Bcfg2.Options.setup.bundle_quick: if (not Bcfg2.Options.setup.only_bundles and not Bcfg2.Options.setup.except_bundles): @@ -201,7 +198,7 @@ def run_probe(self, probe): try: fileending = '' if os.name == 'nt': - (interpreter, fileending) = + (interpreter, fileending) = \ probe.attrib.get('interpreter').split('*') scripthandle, scriptname = tempfile.mkstemp(suffix = fileending) if sys.hexversion >= 0x03000000: @@ -422,10 +419,12 @@ def run(self): if not Bcfg2.Options.setup.no_lock: # check lock here try: - if locked(Bcfg2.Options.setup.lockfile): + if self.lock.islocked() and not self.lock.ownlock(): self.fatal_error("Another instance of Bcfg2 is running. " "If you want to bypass the check, run " "with the -O/--no-lock option") + else: + self.lock.acquire() except SystemExit: raise except: @@ -436,20 +435,6 @@ def run(self): # execute the configuration self.Execute() - if not Bcfg2.Options.setup.no_lock: - # unlock here - if os.path.isfile(Bcfg2.Options.setup.lockfile): - try: - if os.name == 'nt': - os.unlink(Bcfg2.Options.setup.lockfile) - else: - lockfile = open(file, 'w') - fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN) - os.remove(Bcfg2.Options.setup.lockfile) - except OSError: - self.logger.error("Failed to unlock lockfile %s" % - lockfile.name) - if (not Bcfg2.Options.setup.file and not Bcfg2.Options.setup.bundle_quick): # upload statistics diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index e7b6717903..af600e10fe 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -86,29 +86,80 @@ def __str__(self): return "[%s]" % self.str -def locked(file_path): - """ Acquire a lock on a file. - - :param file_path: The path to the lockfile - :type fd: string - :returns: bool - True if the file is already locked, False - otherwise """ - if os.name == 'nt': - if (os.path.isfile(file_path)): - return True +class Flock(object): + '''Class to handle creating and removing (pid) lockfiles''' + + # custom exceptions + class FileLockAcquisitionError(Exception): pass + class FileLockReleaseError(Exception): pass + + # convenience callables for formatting + addr = lambda self: '%d' % (self.pid) + fddr = lambda self: '<%s %s>' % (self.path, self.addr()) + pddr = lambda self, lock: '<%s %s>' % (self.path, lock['pid']) + + def __init__(self, path, debug=None): + self.pid = os.getpid() + self.path = path + self.debug = debug + + def acquire(self): + '''Acquire a lock, returning self if successful, False otherwise''' + if self.islocked(): + if self.debug: + lock = self._readlock() + raise Exception ("Previous lock detected: %s" % self.pddr(lock)) try: - lockfile = open(file_path, 'w') - lockfile.write(str(os.getpid())) - except IOError: - return True - return False - else: - lockfile = open(file_path, 'w') + fh = open(self.path, 'w') + fh.write(self.addr()) + fh.close() + if self.debug: + print 'Acquired lock: %s' % self.fddr() + except Exception as e: + if os.path.isfile(self.path): + try: + os.unlink(self.path) + except: + pass + raise (self.FileLockAcquisitionError ("Error acquiring lock '%s': %s" % (self.fddr(), e))) + + def release(self): + '''Release lock, returning self''' + if self.ownlock(): + try: + os.unlink(self.path) + if self.debug: + print 'Released lock: %s' % self.fddr() + except Exception as e: + raise (self.FileLockReleaseError ("Error releasing lock: '%s': %s" % (self.fddr(), e))) + + + def _readlock(self): + '''Internal method to read lock info''' + lock = {} + fh = open(self.path) + data = fh.read().rstrip() + fh.close() + lock['pid'] = data + return lock + + def islocked(self): + '''Check if we already have a lock''' try: - fcntl.lockf(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError: - return True - return False + lock = self._readlock() + return (self.fddr() != self.pddr(lock)) + except Exception as e: + print("Excepted {}".format(e)) + return False + + def ownlock(self): + '''Check if we own the lock''' + lock = self._readlock() + return (self.fddr() == self.pddr(lock)) + + def __del__(self): + '''Magic method to clean up lock when program exits''' + self.release() class ExecutorResult(object): From 6c5832de32653239b32d86fbc68ffbad02fe90fb Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Fri, 21 Jul 2017 11:40:21 +0200 Subject: [PATCH 06/29] Readd locked function for bcfg2-server --- src/lib/Bcfg2/Utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index af600e10fe..ef6e0154f1 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -322,6 +322,19 @@ def run(self, command, inputdata=None, timeout=None, **kwargs): timer.cancel() +def locked(fd): + """ Acquire a lock on a file. + :param fd: The file descriptor to lock + :type fd: int + :returns: bool - True if the file is already locked, False + otherwise """ + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + return True + return False + + def list2range(lst): ''' convert a list of integers to a set of human-readable ranges. e.g.: From 0dc7c3fda3e4b33631c048b6c39fc1de0a48eb5d Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Fri, 21 Jul 2017 12:10:13 +0200 Subject: [PATCH 07/29] Fix Style Issues --- src/lib/Bcfg2/Client/Tools/WinFS.py | 25 ++++++++------- src/lib/Bcfg2/Client/Tools/WinService.py | 18 +++++------ src/lib/Bcfg2/Client/__init__.py | 10 +++--- src/lib/Bcfg2/Utils.py | 40 +++++++++++++----------- 4 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index b6b1af62c6..aa516fee54 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -7,6 +7,7 @@ import Bcfg2.Client.Tools from Bcfg2.Compat import unicode, b64encode, b64decode + class WinFS(Bcfg2.Client.Tools.Tool): """Windows File support code.""" name = 'WinFS' @@ -18,12 +19,12 @@ def __init__(self, config): self.__req__['Path']['file'] = ['name', 'mode', 'owner', 'group'] def _getFilePath(self, entry): - """Evaluates the enviroment Variables and returns the file path""" + """Evaluates the enviroment Variables and returns the file path""" file_path = os.path.expandvars(os.path.normpath(entry.get('name')[1:])) - if(not file_path[1] == ':'): + if not file_path[1] == ':': self.logger.info( - "Skipping \"%s\" because it doesnt look like a Windows Path" % - file_path) + "Skipping \"%s\" because it doesnt look like a Windows Path" % + file_path) return False return file_path @@ -114,7 +115,7 @@ def _write_tmpfile(self, entry, file_path): except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to create temp file in %s: %s" % (file_path, err)) + "Windows: Failed to create temp file in %s: %s" % (file_path, err)) return False try: if isinstance(filedata, str) and str != unicode: @@ -125,9 +126,9 @@ def _write_tmpfile(self, entry, file_path): except (OSError, IOError): err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to open temp file %s for writing " - "%s: %s" % - (newfile, file_path, err)) + "Windows: Failed to open temp file %s for writing " + "%s: %s" % + (newfile, file_path, err)) return False return newfile @@ -159,15 +160,15 @@ def _rename_tmpfile(self, newfile, file_path): except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to rename temp file %s to %s: %s" - % (newfile, file_path, err)) + "Windows: Failed to rename temp file %s to %s: %s" + % (newfile, file_path, err)) try: os.unlink(newfile) except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Could not remove temp file %s: %s" % - (newfile, err)) + "Windows: Could not remove temp file %s: %s" % + (newfile, err)) return False def _exists(self, file_path): diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py index b627b2d434..5856c80976 100755 --- a/src/lib/Bcfg2/Client/Tools/WinService.py +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -61,7 +61,7 @@ def InstallService(self, entry): cmd = "stop" return self.cmd.run(self.get_svc_command(entry, cmd), stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=False).success def restart_service(self, service): @@ -75,18 +75,18 @@ def restart_service(self, service): self.logger.debug('Restarting service %s' % service.get('name')) restart_target = service.get('target', 'restart') return self.cmd.run(self.get_svc_command(service, restart_target), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=False) def FindExtra(self): """Locate extra Windows services.""" list = self.cmd.run("powershell.exe " - "\"Get-WMIObject win32_service -Filter " - "\\\"StartMode = 'auto'\\\"" - " | Format-Table Name -HideTableHeaders\"", - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False).stdout.splitlines() + "\"Get-WMIObject win32_service -Filter " + "\\\"StartMode = 'auto'\\\"" + " | Format-Table Name -HideTableHeaders\"", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).stdout.splitlines() return [Bcfg2.Client.XML.Element('Service', name=name, type='windows') for name in list] diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 54dace0433..a75d547b89 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -199,8 +199,8 @@ def run_probe(self, probe): fileending = '' if os.name == 'nt': (interpreter, fileending) = \ - probe.attrib.get('interpreter').split('*') - scripthandle, scriptname = tempfile.mkstemp(suffix = fileending) + probe.attrib.get('interpreter').split('*') + (scripthandle, scriptname) = tempfile.mkstemp(suffix = fileending) if sys.hexversion >= 0x03000000: script = os.fdopen(scripthandle, 'w', encoding=Bcfg2.Options.setup.encoding) @@ -216,9 +216,9 @@ def run_probe(self, probe): script.close() if os.name == 'nt': rv = self.cmd.run([interpreter, scriptname], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False) else: os.chmod(scriptname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index ef6e0154f1..f367678b07 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -90,27 +90,30 @@ class Flock(object): '''Class to handle creating and removing (pid) lockfiles''' # custom exceptions - class FileLockAcquisitionError(Exception): pass - class FileLockReleaseError(Exception): pass + class FileLockAcquisitionError(Exception): + pass + + class FileLockReleaseError(Exception): + pass # convenience callables for formatting - addr = lambda self: '%d' % (self.pid) - fddr = lambda self: '<%s %s>' % (self.path, self.addr()) - pddr = lambda self, lock: '<%s %s>' % (self.path, lock['pid']) + def addr(self): return lambda self: '%d' % (self.pid) + def fddr(self): return lambda self: '<%s %s>' % (self.path, self.addr()) + def pddr(self): return lambda self, lock: '<%s %s>' % (self.path, lock['pid']) def __init__(self, path, debug=None): - self.pid = os.getpid() - self.path = path + self.pid = os.getpid() + self.path = path self.debug = debug def acquire(self): - '''Acquire a lock, returning self if successful, False otherwise''' + """Acquire a lock, returning self if successful, False otherwise""" if self.islocked(): if self.debug: lock = self._readlock() - raise Exception ("Previous lock detected: %s" % self.pddr(lock)) + raise Exception("Previous lock detected: %s" % self.pddr(lock)) try: - fh = open(self.path, 'w') + fh = open(self.path, 'w') fh.write(self.addr()) fh.close() if self.debug: @@ -121,10 +124,10 @@ def acquire(self): os.unlink(self.path) except: pass - raise (self.FileLockAcquisitionError ("Error acquiring lock '%s': %s" % (self.fddr(), e))) + raise (self.FileLockAcquisitionError("Error acquiring lock '%s': %s" % (self.fddr(), e))) def release(self): - '''Release lock, returning self''' + """Release lock, returning self""" if self.ownlock(): try: os.unlink(self.path) @@ -133,18 +136,17 @@ def release(self): except Exception as e: raise (self.FileLockReleaseError ("Error releasing lock: '%s': %s" % (self.fddr(), e))) - def _readlock(self): - '''Internal method to read lock info''' + """Internal method to read lock info""" lock = {} - fh = open(self.path) + fh = open(self.path) data = fh.read().rstrip() fh.close() lock['pid'] = data return lock def islocked(self): - '''Check if we already have a lock''' + """Check if we already have a lock""" try: lock = self._readlock() return (self.fddr() != self.pddr(lock)) @@ -153,12 +155,12 @@ def islocked(self): return False def ownlock(self): - '''Check if we own the lock''' + """Check if we own the lock""" lock = self._readlock() return (self.fddr() == self.pddr(lock)) def __del__(self): - '''Magic method to clean up lock when program exits''' + """Magic method to clean up lock when program exits""" self.release() @@ -327,7 +329,7 @@ def locked(fd): :param fd: The file descriptor to lock :type fd: int :returns: bool - True if the file is already locked, False - otherwise """ + otherwise """ try: fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: From 19f05d5bf7b643ea2a3987746c9d8f912031d89c Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Fri, 21 Jul 2017 12:23:55 +0200 Subject: [PATCH 08/29] More Style Fixes --- src/lib/Bcfg2/Client/Tools/WinService.py | 12 ++++++------ src/lib/Bcfg2/Client/__init__.py | 8 ++++---- src/lib/Bcfg2/Utils.py | 24 ++++++++++++++---------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py index 5856c80976..7774a3a0c6 100755 --- a/src/lib/Bcfg2/Client/Tools/WinService.py +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -82,11 +82,11 @@ def restart_service(self, service): def FindExtra(self): """Locate extra Windows services.""" list = self.cmd.run("powershell.exe " - "\"Get-WMIObject win32_service -Filter " - "\\\"StartMode = 'auto'\\\"" - " | Format-Table Name -HideTableHeaders\"", - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False).stdout.splitlines() + "\"Get-WMIObject win32_service -Filter " + "\\\"StartMode = 'auto'\\\"" + " | Format-Table Name -HideTableHeaders\"", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).stdout.splitlines() return [Bcfg2.Client.XML.Element('Service', name=name, type='windows') for name in list] diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index a75d547b89..8c6b2a8f5a 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -200,7 +200,7 @@ def run_probe(self, probe): if os.name == 'nt': (interpreter, fileending) = \ probe.attrib.get('interpreter').split('*') - (scripthandle, scriptname) = tempfile.mkstemp(suffix = fileending) + (scripthandle, scriptname) = tempfile.mkstemp(suffix=fileending) if sys.hexversion >= 0x03000000: script = os.fdopen(scripthandle, 'w', encoding=Bcfg2.Options.setup.encoding) @@ -216,9 +216,9 @@ def run_probe(self, probe): script.close() if os.name == 'nt': rv = self.cmd.run([interpreter, scriptname], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False) else: os.chmod(scriptname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index f367678b07..89020e1a9c 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -97,9 +97,14 @@ class FileLockReleaseError(Exception): pass # convenience callables for formatting - def addr(self): return lambda self: '%d' % (self.pid) - def fddr(self): return lambda self: '<%s %s>' % (self.path, self.addr()) - def pddr(self): return lambda self, lock: '<%s %s>' % (self.path, lock['pid']) + def addr(self): + return lambda addr: '%d' % (self.pid) + + def fddr(self): + return lambda fddr: '<%s %s>' % (self.path, self.addr()) + + def pddr(self, lock): + return lambda pddr: '<%s %s>' % (self.path, lock['pid']) def __init__(self, path, debug=None): self.pid = os.getpid() @@ -113,9 +118,9 @@ def acquire(self): lock = self._readlock() raise Exception("Previous lock detected: %s" % self.pddr(lock)) try: - fh = open(self.path, 'w') - fh.write(self.addr()) - fh.close() + file_handle = open(self.path, 'w') + file_handle.write(self.addr()) + file_handle.close() if self.debug: print 'Acquired lock: %s' % self.fddr() except Exception as e: @@ -134,7 +139,7 @@ def release(self): if self.debug: print 'Released lock: %s' % self.fddr() except Exception as e: - raise (self.FileLockReleaseError ("Error releasing lock: '%s': %s" % (self.fddr(), e))) + raise(self.FileLockReleaseError ("Error releasing lock: '%s': %s" % (self.fddr(), e))) def _readlock(self): """Internal method to read lock info""" @@ -149,9 +154,8 @@ def islocked(self): """Check if we already have a lock""" try: lock = self._readlock() - return (self.fddr() != self.pddr(lock)) - except Exception as e: - print("Excepted {}".format(e)) + return self.fddr() != self.pddr(lock) + except: return False def ownlock(self): From 1a28369fa59c7c1bf140779b331d49320206a06e Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Fri, 21 Jul 2017 12:46:03 +0200 Subject: [PATCH 09/29] Fix style issues --- src/lib/Bcfg2/Client/Tools/WinService.py | 16 ++++++++-------- src/lib/Bcfg2/Client/__init__.py | 6 +++--- src/lib/Bcfg2/Utils.py | 23 ++++++++++++++++------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py index 7774a3a0c6..57a817567c 100755 --- a/src/lib/Bcfg2/Client/Tools/WinService.py +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -81,12 +81,12 @@ def restart_service(self, service): def FindExtra(self): """Locate extra Windows services.""" - list = self.cmd.run("powershell.exe " - "\"Get-WMIObject win32_service -Filter " - "\\\"StartMode = 'auto'\\\"" - " | Format-Table Name -HideTableHeaders\"", - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False).stdout.splitlines() + services = self.cmd.run("powershell.exe " + "\"Get-WMIObject win32_service -Filter " + "\\\"StartMode = 'auto'\\\"" + " | Format-Table Name -HideTableHeaders\"", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).stdout.splitlines() return [Bcfg2.Client.XML.Element('Service', name=name, type='windows') - for name in list] + for name in services] diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 8c6b2a8f5a..3d1a6d91e5 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -216,9 +216,9 @@ def run_probe(self, probe): script.close() if os.name == 'nt': rv = self.cmd.run([interpreter, scriptname], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False) else: os.chmod(scriptname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 89020e1a9c..64e3e6a92b 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -91,19 +91,24 @@ class Flock(object): # custom exceptions class FileLockAcquisitionError(Exception): + """Exception if we fail to acquire the lock""" pass class FileLockReleaseError(Exception): + """Exception if we fail to release the lock""" pass # convenience callables for formatting def addr(self): + """Lambda which returns the pid""" return lambda addr: '%d' % (self.pid) def fddr(self): + """Lambda which returns the path and the pid""" return lambda fddr: '<%s %s>' % (self.path, self.addr()) def pddr(self, lock): + """Lambda which returns the path and the pid of a specific lock""" return lambda pddr: '<%s %s>' % (self.path, lock['pid']) def __init__(self, path, debug=None): @@ -123,13 +128,15 @@ def acquire(self): file_handle.close() if self.debug: print 'Acquired lock: %s' % self.fddr() - except Exception as e: + except Exception as exception: if os.path.isfile(self.path): try: os.unlink(self.path) except: pass - raise (self.FileLockAcquisitionError("Error acquiring lock '%s': %s" % (self.fddr(), e))) + raise (self.FileLockAcquisitionError( + "Error acquiring lock '%s': %s" % (self.fddr(), + exception))) def release(self): """Release lock, returning self""" @@ -138,15 +145,17 @@ def release(self): os.unlink(self.path) if self.debug: print 'Released lock: %s' % self.fddr() - except Exception as e: - raise(self.FileLockReleaseError ("Error releasing lock: '%s': %s" % (self.fddr(), e))) + except Exception as exception: + raise(self.FileLockReleaseError( + "Error releasing lock: '%s': %s" % (self.fddr(), + exception))) def _readlock(self): """Internal method to read lock info""" lock = {} - fh = open(self.path) - data = fh.read().rstrip() - fh.close() + file_handle = open(self.path) + data = file_handle.read().rstrip() + file_handle.close() lock['pid'] = data return lock From 4c907fe9d98111081e7d17cf87c4a4fbbb2bf5a0 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 13:57:34 +0200 Subject: [PATCH 10/29] Fix Error with lambda functions --- src/lib/Bcfg2/Utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 64e3e6a92b..443042e10b 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -100,16 +100,16 @@ class FileLockReleaseError(Exception): # convenience callables for formatting def addr(self): - """Lambda which returns the pid""" - return lambda addr: '%d' % (self.pid) + """Function which returns the pid""" + return '%d' % (self.pid) def fddr(self): - """Lambda which returns the path and the pid""" - return lambda fddr: '<%s %s>' % (self.path, self.addr()) + """Function which returns the path and the pid""" + return '<%s %s>' % (self.path, self.addr()) def pddr(self, lock): - """Lambda which returns the path and the pid of a specific lock""" - return lambda pddr: '<%s %s>' % (self.path, lock['pid']) + """Function which returns the path and the pid of a specific lock""" + return '<%s %s>' % (self.path, lock['pid']) def __init__(self, path, debug=None): self.pid = os.getpid() From 54c0c1ec961dbe9bf52fc6a300e6f4b93712bf23 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:03:14 +0200 Subject: [PATCH 11/29] Use compat to import modules which doesnt exists on windows --- src/lib/Bcfg2/Compat.py | 20 ++++++++++++++++++++ src/lib/Bcfg2/Logger.py | 4 +--- src/lib/Bcfg2/Options/Types.py | 5 +---- src/lib/Bcfg2/Utils.py | 4 +--- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py index 1c2420ccf9..986d5183ae 100644 --- a/src/lib/Bcfg2/Compat.py +++ b/src/lib/Bcfg2/Compat.py @@ -72,6 +72,26 @@ except ImportError: import http.client as httplib +try: + import fcntl +except ImportError: + fcntl = None + +try: + import termios +except ImportError: + termios = None + +try: + import pwd +except ImportError: + pwd = None + +try: + import grp +except ImportError: + grp = None + try: unicode = unicode except NameError: diff --git a/src/lib/Bcfg2/Logger.py b/src/lib/Bcfg2/Logger.py index 1b2cb4151f..c639467ad4 100644 --- a/src/lib/Bcfg2/Logger.py +++ b/src/lib/Bcfg2/Logger.py @@ -7,9 +7,7 @@ import socket import struct import sys -if os.name != 'nt': - import fcntl - import termios +from Bcfg2.Compat import fcntl, termios import Bcfg2.Options logging.raiseExceptions = 0 diff --git a/src/lib/Bcfg2/Options/Types.py b/src/lib/Bcfg2/Options/Types.py index f3dce44d56..37ecd7c026 100644 --- a/src/lib/Bcfg2/Options/Types.py +++ b/src/lib/Bcfg2/Options/Types.py @@ -3,10 +3,7 @@ import os import re -if os.name != 'nt': - import pwd - import grp -from Bcfg2.Compat import literal_eval +from Bcfg2.Compat import literal_eval, pwd, grp _COMMA_SPLIT_RE = re.compile(r'\s*,\s*') diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 443042e10b..c9b5f56f57 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -9,9 +9,7 @@ import sys import subprocess import threading -if os.name != 'nt': - import fcntl -from Bcfg2.Compat import input, any # pylint: disable=W0622 +from Bcfg2.Compat import input, any, fcntl # pylint: disable=W0622 class ClassName(object): From ef4aea5cce186bd1b66b6bc8bd8918893e35b293 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:12:06 +0200 Subject: [PATCH 12/29] Fix more Style Issues --- src/lib/Bcfg2/Client/Tools/WinService.py | 12 ++++++------ src/lib/Bcfg2/Utils.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinService.py b/src/lib/Bcfg2/Client/Tools/WinService.py index 57a817567c..64d4b7b717 100755 --- a/src/lib/Bcfg2/Client/Tools/WinService.py +++ b/src/lib/Bcfg2/Client/Tools/WinService.py @@ -82,11 +82,11 @@ def restart_service(self, service): def FindExtra(self): """Locate extra Windows services.""" services = self.cmd.run("powershell.exe " - "\"Get-WMIObject win32_service -Filter " - "\\\"StartMode = 'auto'\\\"" - " | Format-Table Name -HideTableHeaders\"", - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=False).stdout.splitlines() + "\"Get-WMIObject win32_service -Filter " + "\\\"StartMode = 'auto'\\\"" + " | Format-Table Name -HideTableHeaders\"", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False).stdout.splitlines() return [Bcfg2.Client.XML.Element('Service', name=name, type='windows') for name in services] diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index c9b5f56f57..1352e6f9cb 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -134,7 +134,7 @@ def acquire(self): pass raise (self.FileLockAcquisitionError( "Error acquiring lock '%s': %s" % (self.fddr(), - exception))) + exception))) def release(self): """Release lock, returning self""" @@ -146,7 +146,7 @@ def release(self): except Exception as exception: raise(self.FileLockReleaseError( "Error releasing lock: '%s': %s" % (self.fddr(), - exception))) + exception))) def _readlock(self): """Internal method to read lock info""" From a19f5c3034ceee5784bdc66a181a9bd0db1bc86e Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:21:33 +0200 Subject: [PATCH 13/29] Fix Style Issues --- src/lib/Bcfg2/Utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 1352e6f9cb..add6862d9b 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -133,8 +133,8 @@ def acquire(self): except: pass raise (self.FileLockAcquisitionError( - "Error acquiring lock '%s': %s" % (self.fddr(), - exception))) + "Error acquiring lock '%s': %s" % (self.fddr(), + exception))) def release(self): """Release lock, returning self""" @@ -145,8 +145,8 @@ def release(self): print 'Released lock: %s' % self.fddr() except Exception as exception: raise(self.FileLockReleaseError( - "Error releasing lock: '%s': %s" % (self.fddr(), - exception))) + "Error releasing lock: '%s': %s" % (self.fddr(), + exception))) def _readlock(self): """Internal method to read lock info""" From ad69ab5dbe5f01ed0aee154c843f9f2ce491d539 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:27:45 +0200 Subject: [PATCH 14/29] More Style Fixes --- src/lib/Bcfg2/Utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index add6862d9b..5e1a463c15 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -134,7 +134,7 @@ def acquire(self): pass raise (self.FileLockAcquisitionError( "Error acquiring lock '%s': %s" % (self.fddr(), - exception))) + exception))) def release(self): """Release lock, returning self""" @@ -146,7 +146,7 @@ def release(self): except Exception as exception: raise(self.FileLockReleaseError( "Error releasing lock: '%s': %s" % (self.fddr(), - exception))) + exception))) def _readlock(self): """Internal method to read lock info""" From cdd5a7dd6922f26c874b09e80c886900587b8393 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:28:46 +0200 Subject: [PATCH 15/29] Fix more Style Issues --- src/lib/Bcfg2/Client/Tools/WinFS.py | 5 +++-- src/lib/Bcfg2/Logger.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index aa516fee54..d0c7c2646c 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -5,7 +5,7 @@ import tempfile import Bcfg2.Options import Bcfg2.Client.Tools -from Bcfg2.Compat import unicode, b64encode, b64decode +from Bcfg2.Compat import b64decode class WinFS(Bcfg2.Client.Tools.Tool): @@ -115,7 +115,8 @@ def _write_tmpfile(self, entry, file_path): except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to create temp file in %s: %s" % (file_path, err)) + "Windows: Failed to create temp file in %s: %s" % + (file_path, err)) return False try: if isinstance(filedata, str) and str != unicode: diff --git a/src/lib/Bcfg2/Logger.py b/src/lib/Bcfg2/Logger.py index c639467ad4..7b859eba9e 100644 --- a/src/lib/Bcfg2/Logger.py +++ b/src/lib/Bcfg2/Logger.py @@ -1,5 +1,4 @@ """Bcfg2 logging support""" -import os import copy import logging import logging.handlers From 4059e22a2b9db52d8d866d34709e2fa015ee1f9a Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:44:29 +0200 Subject: [PATCH 16/29] Better error handling in Flock class --- src/lib/Bcfg2/Utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 5e1a463c15..b9fc2c0b27 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -126,15 +126,15 @@ def acquire(self): file_handle.close() if self.debug: print 'Acquired lock: %s' % self.fddr() - except Exception as exception: + except IOError as exception: if os.path.isfile(self.path): try: os.unlink(self.path) except: pass raise (self.FileLockAcquisitionError( - "Error acquiring lock '%s': %s" % (self.fddr(), - exception))) + "Error acquiring lock '%s': %s" % (self.fddr(), + exception))) def release(self): """Release lock, returning self""" @@ -145,8 +145,8 @@ def release(self): print 'Released lock: %s' % self.fddr() except Exception as exception: raise(self.FileLockReleaseError( - "Error releasing lock: '%s': %s" % (self.fddr(), - exception))) + "Error releasing lock: '%s': %s" % (self.fddr(), + exception))) def _readlock(self): """Internal method to read lock info""" @@ -162,7 +162,7 @@ def islocked(self): try: lock = self._readlock() return self.fddr() != self.pddr(lock) - except: + except IOError: return False def ownlock(self): From 190d7852d442cbf84ee6a207c7c69ccd43299ecb Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 24 Jul 2017 14:51:44 +0200 Subject: [PATCH 17/29] Fix style and error handling issues --- src/lib/Bcfg2/Utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index b9fc2c0b27..c1bf3f6815 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -130,11 +130,11 @@ def acquire(self): if os.path.isfile(self.path): try: os.unlink(self.path) - except: + except OSError: pass raise (self.FileLockAcquisitionError( - "Error acquiring lock '%s': %s" % (self.fddr(), - exception))) + "Error acquiring lock '%s': %s" % (self.fddr(), + exception))) def release(self): """Release lock, returning self""" @@ -145,8 +145,8 @@ def release(self): print 'Released lock: %s' % self.fddr() except Exception as exception: raise(self.FileLockReleaseError( - "Error releasing lock: '%s': %s" % (self.fddr(), - exception))) + "Error releasing lock: '%s': %s" % (self.fddr(), + exception))) def _readlock(self): """Internal method to read lock info""" From 2bd891fe19b117f2e2552fadd2c5b9c6507b7873 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 8 Aug 2017 14:47:50 +0200 Subject: [PATCH 18/29] In Actions Shell should be False by default --- src/lib/Bcfg2/Client/Tools/WinAction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinAction.py b/src/lib/Bcfg2/Client/Tools/WinAction.py index cbc4b1088e..33f943c801 100755 --- a/src/lib/Bcfg2/Client/Tools/WinAction.py +++ b/src/lib/Bcfg2/Client/Tools/WinAction.py @@ -13,7 +13,7 @@ class WinAction(Bcfg2.Client.Tools.Tool): def RunAction(self, entry): """This method handles command execution and status return.""" - shell = True + shell = False shell_string = '' if entry.get('shell', 'false') == 'true': shell = True @@ -27,7 +27,7 @@ def RunAction(self, entry): ans = safe_input(prompt) if ans not in ['y', 'Y']: return False - if False: + if Bcfg2.Options.setup.service_mode == 'build': if entry.get('build', 'true') == 'false': self.logger.debug("Action: Deferring execution of %s due " "to build mode" % entry.get('command')) From c7e9e5aae221553cdae59f7b6b15262782f63b76 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Mon, 23 Apr 2018 13:05:05 +0200 Subject: [PATCH 19/29] Add WinFS support for nonexistent path types --- src/lib/Bcfg2/Client/Tools/WinFS.py | 78 +++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index d0c7c2646c..f443fdddbe 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -2,29 +2,32 @@ import sys import os import stat +import shutil import tempfile import Bcfg2.Options import Bcfg2.Client.Tools -from Bcfg2.Compat import b64decode +from Bcfg2.Compat import unicode, b64encode, b64decode class WinFS(Bcfg2.Client.Tools.Tool): """Windows File support code.""" name = 'WinFS' - __handles__ = [('Path', 'file')] + __handles__ = [('Path', 'file'), ('Path', 'nonexistent')] def __init__(self, config): Bcfg2.Client.Tools.Tool.__init__(self, config) self.__req__ = dict(Path=dict()) self.__req__['Path']['file'] = ['name', 'mode', 'owner', 'group'] + self.__req__['Path']['nonexistent'] = ['name', 'recursive'] def _getFilePath(self, entry): """Evaluates the enviroment Variables and returns the file path""" file_path = os.path.expandvars(os.path.normpath(entry.get('name')[1:])) - if not file_path[1] == ':': + if(not file_path[1] == ':'): self.logger.info( - "Skipping \"%s\" because it doesnt look like a Windows Path" % - file_path) + "Skipping \"%s\" because it doesnt look like a " + "Windows Path" % + file_path) return False return file_path @@ -33,6 +36,14 @@ def VerifyPath(self, entry, _): file_path = self._getFilePath(entry) if(not file_path): return False + + if entry.get('type') == 'nonexistent': + if os.path.exists(file_path): + self.logger.debug("WinFS: %s exists but should not" % + file_path) + return False + return True + ondisk = self._exists(file_path) tempdata = self._get_data(entry)[0] if isinstance(tempdata, str) and str != unicode: @@ -71,9 +82,33 @@ def VerifyPath(self, entry, _): def InstallPath(self, entry): """Install device entries.""" file_path = self._getFilePath(entry) - + ename = entry.get('name') + print("Handeling %s" % file_path) if not file_path: return False + if entry.get('type') == "nonexistent": + recursive = entry.get('recursive', '').lower() == 'true' + if recursive: + # ensure that configuration spec is consistent first + for struct in self.config.getchildren(): + for el in struct.getchildren(): + if (el.tag == 'Path' and + el.get('type') != 'nonexistent' and + el.get('name').startswith(ename)): + self.logger.error('POSIX: Not removing %s. One ' + 'or more files in this ' + 'directory are specified ' + 'in your configuration.' % + file_path) + return False + try: + self._remove(file_path, recursive=recursive) + return True + except OSError: + err = sys.exec_info()[1] + self.logger.error('WinFS: Failed to renive %s: %s' % + (file_path, err)) + return False self.logger.debug("Installing: " + file_path) if not os.path.exists(os.path.dirname(file_path)): @@ -115,8 +150,8 @@ def _write_tmpfile(self, entry, file_path): except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to create temp file in %s: %s" % - (file_path, err)) + "Windows: Failed to create " + "temp file in %s: %s" % (file_path, err)) return False try: if isinstance(filedata, str) and str != unicode: @@ -127,9 +162,9 @@ def _write_tmpfile(self, entry, file_path): except (OSError, IOError): err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to open temp file %s for writing " - "%s: %s" % - (newfile, file_path, err)) + "Windows: Failed to open temp file %s for writing " + "%s: %s" % + (newfile, file_path, err)) return False return newfile @@ -161,15 +196,15 @@ def _rename_tmpfile(self, newfile, file_path): except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to rename temp file %s to %s: %s" - % (newfile, file_path, err)) + "Windows: Failed to rename temp file %s to %s: %s" + % (newfile, file_path, err)) try: os.unlink(newfile) except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Could not remove temp file %s: %s" % - (newfile, err)) + "Windows: Could not remove temp file %s: %s" % + (newfile, err)) return False def _exists(self, file_path): @@ -179,3 +214,16 @@ def _exists(self, file_path): return os.lstat(file_path) except OSError: return None + + def _remove(self, file_path, recursive=True): + """ Remove a Path entry, whatever that takes """ + if os.path.islink(file_path): + os.unlink(file_path) + elif os.path.isdir(file_path): + if recursive: + shutil.rmtree(file_path) + else: + os.rmdir(file_path) + else: + os.unlink(file_path) + From c40da0349f3a481e5bf171e94e60328b63053753 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Fri, 8 Jun 2018 11:20:00 +0200 Subject: [PATCH 20/29] Fixing pylint errors --- src/lib/Bcfg2/Client/Tools/WinFS.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index f443fdddbe..b067420c3f 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -6,7 +6,7 @@ import tempfile import Bcfg2.Options import Bcfg2.Client.Tools -from Bcfg2.Compat import unicode, b64encode, b64decode +from Bcfg2.Compat import b64decode class WinFS(Bcfg2.Client.Tools.Tool): @@ -103,12 +103,13 @@ def InstallPath(self, entry): return False try: self._remove(file_path, recursive=recursive) - return True + rv = True except OSError: - err = sys.exec_info()[1] + err = sys.exc_info()[1] self.logger.error('WinFS: Failed to renive %s: %s' % (file_path, err)) - return False + rv = False + return rv self.logger.debug("Installing: " + file_path) if not os.path.exists(os.path.dirname(file_path)): @@ -119,7 +120,7 @@ def InstallPath(self, entry): return False rv = True if not self._rename_tmpfile(newfile, file_path): - return False + rv = False return rv @@ -225,5 +226,5 @@ def _remove(self, file_path, recursive=True): else: os.rmdir(file_path) else: - os.unlink(file_path) + os.unlink(file_path) From 03ad988d8eb3618a5403ab22879a723316af2b89 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Wed, 13 Jun 2018 11:12:10 +0200 Subject: [PATCH 21/29] Fixing more pylint errors --- src/lib/Bcfg2/Client/Tools/WinFS.py | 31 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index b067420c3f..6f06db99c4 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -23,18 +23,18 @@ def __init__(self, config): def _getFilePath(self, entry): """Evaluates the enviroment Variables and returns the file path""" file_path = os.path.expandvars(os.path.normpath(entry.get('name')[1:])) - if(not file_path[1] == ':'): + if not file_path[1] == ':': self.logger.info( - "Skipping \"%s\" because it doesnt look like a " - "Windows Path" % - file_path) + "Skipping \"%s\" because it doesnt look like a " + "Windows Path" % + file_path) return False return file_path def VerifyPath(self, entry, _): """Path always verify true.""" file_path = self._getFilePath(entry) - if(not file_path): + if not file_path: return False if entry.get('type') == 'nonexistent': @@ -151,8 +151,8 @@ def _write_tmpfile(self, entry, file_path): except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to create " - "temp file in %s: %s" % (file_path, err)) + "Windows: Failed to create " + "temp file in %s: %s" % (file_path, err)) return False try: if isinstance(filedata, str) and str != unicode: @@ -163,9 +163,9 @@ def _write_tmpfile(self, entry, file_path): except (OSError, IOError): err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to open temp file %s for writing " - "%s: %s" % - (newfile, file_path, err)) + "Windows: Failed to open temp file %s for writing " + "%s: %s" % + (newfile, file_path, err)) return False return newfile @@ -190,22 +190,22 @@ def _get_data(self, entry): def _rename_tmpfile(self, newfile, file_path): """ Rename the given file to the appropriate filename for entry """ try: - if(os.path.isfile(file_path)): + if os.path.isfile(file_path): os.unlink(file_path) os.rename(newfile, file_path) return True except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Failed to rename temp file %s to %s: %s" - % (newfile, file_path, err)) + "Windows: Failed to rename temp file %s to %s: %s" + % (newfile, file_path, err)) try: os.unlink(newfile) except OSError: err = sys.exc_info()[1] self.logger.error( - "Windows: Could not remove temp file %s: %s" % - (newfile, err)) + "Windows: Could not remove temp file %s: %s" % + (newfile, err)) return False def _exists(self, file_path): @@ -227,4 +227,3 @@ def _remove(self, file_path, recursive=True): os.rmdir(file_path) else: os.unlink(file_path) - From 82dd867f8218069f6e9211d609dc317f026b5236 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Wed, 1 Aug 2018 09:58:26 +0200 Subject: [PATCH 22/29] Don't use "except as" because it only works in python 2.6 and newer --- src/lib/Bcfg2/Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index c1bf3f6815..c50182990a 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -126,7 +126,7 @@ def acquire(self): file_handle.close() if self.debug: print 'Acquired lock: %s' % self.fddr() - except IOError as exception: + except IOError, exception: if os.path.isfile(self.path): try: os.unlink(self.path) From 17d88885652350aeecc440339cf56dbc58b0b859 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Wed, 1 Aug 2018 10:06:04 +0200 Subject: [PATCH 23/29] Don't use "except as" because it only works in python 2.6 and newer --- src/lib/Bcfg2/Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index c50182990a..950fdd32e1 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -143,7 +143,7 @@ def release(self): os.unlink(self.path) if self.debug: print 'Released lock: %s' % self.fddr() - except Exception as exception: + except Exception, exception: raise(self.FileLockReleaseError( "Error releasing lock: '%s': %s" % (self.fddr(), exception))) From 80efd678909bf5f291ad6803b5bfd23d5a06e922 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Thu, 20 Sep 2018 11:45:05 +0200 Subject: [PATCH 24/29] Added the "real" logger to locking mechanism --- src/lib/Bcfg2/Client/__init__.py | 2 +- src/lib/Bcfg2/Utils.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 3d1a6d91e5..c748aa2553 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -158,7 +158,7 @@ def __init__(self): self.tools = [] self.times = dict() self.times['initialization'] = time.time() - self.lock = Flock(Bcfg2.Options.setup.lockfile) + self.lock = Flock(Bcfg2.Options.setup.lockfile, self.logger) if Bcfg2.Options.setup.bundle_quick: if (not Bcfg2.Options.setup.only_bundles and not Bcfg2.Options.setup.except_bundles): diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 950fdd32e1..29fcfcb487 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -109,24 +109,22 @@ def pddr(self, lock): """Function which returns the path and the pid of a specific lock""" return '<%s %s>' % (self.path, lock['pid']) - def __init__(self, path, debug=None): + def __init__(self, path, logger): self.pid = os.getpid() self.path = path - self.debug = debug + self.logger = logger def acquire(self): """Acquire a lock, returning self if successful, False otherwise""" if self.islocked(): - if self.debug: - lock = self._readlock() + lock = self._readlock() raise Exception("Previous lock detected: %s" % self.pddr(lock)) try: file_handle = open(self.path, 'w') file_handle.write(self.addr()) file_handle.close() - if self.debug: - print 'Acquired lock: %s' % self.fddr() - except IOError, exception: + self.logger.debug('Acquired lock: %s' % self.fddr()) + except (IOError, exception): if os.path.isfile(self.path): try: os.unlink(self.path) @@ -141,9 +139,8 @@ def release(self): if self.ownlock(): try: os.unlink(self.path) - if self.debug: - print 'Released lock: %s' % self.fddr() - except Exception, exception: + self.logger.debug('Released lock: %s' % self.fddr()) + except (Exception, exception): raise(self.FileLockReleaseError( "Error releasing lock: '%s': %s" % (self.fddr(), exception))) From febfafd77392f0c7806720938428f1bdd58abd48 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Thu, 20 Sep 2018 11:45:51 +0200 Subject: [PATCH 25/29] Added missing import for python3 compatibility --- src/lib/Bcfg2/Client/Tools/WinFS.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index 6f06db99c4..078768822a 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -6,7 +6,7 @@ import tempfile import Bcfg2.Options import Bcfg2.Client.Tools -from Bcfg2.Compat import b64decode +from Bcfg2.Compat import unicode, b64decode class WinFS(Bcfg2.Client.Tools.Tool): From 460979cca037ca7d736dd34fb7f1c613d0597c58 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 25 Sep 2018 13:56:24 +0200 Subject: [PATCH 26/29] Fixing exception Handling for Python 2.4 --- src/lib/Bcfg2/Utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index 29fcfcb487..b1b2bed646 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -124,7 +124,7 @@ def acquire(self): file_handle.write(self.addr()) file_handle.close() self.logger.debug('Acquired lock: %s' % self.fddr()) - except (IOError, exception): + except IOError, (errno, exception): if os.path.isfile(self.path): try: os.unlink(self.path) @@ -140,7 +140,7 @@ def release(self): try: os.unlink(self.path) self.logger.debug('Released lock: %s' % self.fddr()) - except (Exception, exception): + except Exception, (errno, exception): raise(self.FileLockReleaseError( "Error releasing lock: '%s': %s" % (self.fddr(), exception))) From 73adee813f736812f67f763b84745219d76a467a Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 25 Sep 2018 14:03:48 +0200 Subject: [PATCH 27/29] Fixing exception Handling for Python 2.4 --- src/lib/Bcfg2/Utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py index b1b2bed646..275614129a 100644 --- a/src/lib/Bcfg2/Utils.py +++ b/src/lib/Bcfg2/Utils.py @@ -124,7 +124,7 @@ def acquire(self): file_handle.write(self.addr()) file_handle.close() self.logger.debug('Acquired lock: %s' % self.fddr()) - except IOError, (errno, exception): + except IOError, exception: if os.path.isfile(self.path): try: os.unlink(self.path) @@ -140,7 +140,7 @@ def release(self): try: os.unlink(self.path) self.logger.debug('Released lock: %s' % self.fddr()) - except Exception, (errno, exception): + except Exception, exception: raise(self.FileLockReleaseError( "Error releasing lock: '%s': %s" % (self.fddr(), exception))) From ebc546da6b11701289c7beb801d43a4d241ec85b Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 25 Sep 2018 14:12:08 +0200 Subject: [PATCH 28/29] Disable pylint W0622 in WinFS.py like in POSIX/File.py --- src/lib/Bcfg2/Client/Tools/WinFS.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index 078768822a..e27d9a7d68 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -6,7 +6,7 @@ import tempfile import Bcfg2.Options import Bcfg2.Client.Tools -from Bcfg2.Compat import unicode, b64decode +from Bcfg2.Compat import unicode, b64decode # pylint: disable=W0622 class WinFS(Bcfg2.Client.Tools.Tool): From 8815e8f03831922b0e6c509dddf6a21863948553 Mon Sep 17 00:00:00 2001 From: Christopher Schenk Date: Tue, 25 Sep 2018 14:16:15 +0200 Subject: [PATCH 29/29] Added missing space to comment --- src/lib/Bcfg2/Client/Tools/WinFS.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Bcfg2/Client/Tools/WinFS.py b/src/lib/Bcfg2/Client/Tools/WinFS.py index e27d9a7d68..4be1530eeb 100755 --- a/src/lib/Bcfg2/Client/Tools/WinFS.py +++ b/src/lib/Bcfg2/Client/Tools/WinFS.py @@ -6,7 +6,7 @@ import tempfile import Bcfg2.Options import Bcfg2.Client.Tools -from Bcfg2.Compat import unicode, b64decode # pylint: disable=W0622 +from Bcfg2.Compat import unicode, b64decode # pylint: disable=W0622 class WinFS(Bcfg2.Client.Tools.Tool):