diff options
Diffstat (limited to 'pyfirstaidkit')
-rw-r--r-- | pyfirstaidkit/FirstAidKit.py | 6 | ||||
-rw-r--r-- | pyfirstaidkit/__init__.py | 6 | ||||
-rw-r--r-- | pyfirstaidkit/configuration.py | 65 | ||||
-rw-r--r-- | pyfirstaidkit/dependency.py | 11 | ||||
-rw-r--r-- | pyfirstaidkit/errors.py | 16 | ||||
-rw-r--r-- | pyfirstaidkit/interpret.py | 97 | ||||
-rw-r--r-- | pyfirstaidkit/issue.py | 29 | ||||
-rw-r--r-- | pyfirstaidkit/plugins.py | 263 | ||||
-rw-r--r-- | pyfirstaidkit/reporting.py | 56 | ||||
-rw-r--r-- | pyfirstaidkit/returns.py | 9 | ||||
-rw-r--r-- | pyfirstaidkit/utils/__init__.py | 6 | ||||
-rw-r--r-- | pyfirstaidkit/utils/backup.py | 4 |
12 files changed, 358 insertions, 210 deletions
diff --git a/pyfirstaidkit/FirstAidKit.py b/pyfirstaidkit/FirstAidKit.py index 44d6775..b8eb726 100644 --- a/pyfirstaidkit/FirstAidKit.py +++ b/pyfirstaidkit/FirstAidKit.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/pyfirstaidkit/__init__.py b/pyfirstaidkit/__init__.py index e7409b0..2468ddb 100644 --- a/pyfirstaidkit/__init__.py +++ b/pyfirstaidkit/__init__.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/pyfirstaidkit/configuration.py b/pyfirstaidkit/configuration.py index feb0b46..4ad74f3 100644 --- a/pyfirstaidkit/configuration.py +++ b/pyfirstaidkit/configuration.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -23,7 +23,8 @@ from shlex import shlex if os.environ.has_key("FIRST_AID_KIT_CONF"): cfgfile = os.environ["FIRST_AID_KIT_CONF"].split(":") else: - cfgfile = ["/etc/firstaidkit/firstaidkit.conf", os.environ["HOME"]+"/.firstaidkit.conf"] + cfgfile = ["/etc/firstaidkit/firstaidkit.conf", + os.environ["HOME"]+"/.firstaidkit.conf"] def createDefaultConfig(config): @@ -54,17 +55,21 @@ def createDefaultConfig(config): config.system.configuration = "/etc/firstaidkit" # Frontend modules are in specified directories - config.system.frontend = "'/usr/lib64/firstaidkit/frontend' '/usr/lib/firstaidkit/frontend'" + config.system.frontend = "'/usr/lib64/firstaidkit/frontend' \ + '/usr/lib/firstaidkit/frontend'" # - # There will be 4 default places where FAK will look for plugins, these 4 names - # will be reserved in the configuration. lib{,64}-firstaidkit-{,examples} + # There will be 4 default places where FAK will look for plugins, + # these 4 names will be reserved in the configuration. + # lib{,64}-firstaidkit-{,examples} # config.add_section("paths") for dir in ["firstaidkit/plugins", "firstaidkit/plugins/examples"]: for root in [ "usr/lib64", "usr/lib"]: if os.path.exists( "/%s/%s" % (root,dir)): - config.set( "paths", "%s/%s"%(dir[19:], root[4]),"/%s/%s" %(root, dir) ) + + config.set( "paths", "%s/%s"%(dir[19:], root[4]), + "/%s/%s" %(root, dir) ) class LockedError(Exception): @@ -85,21 +90,33 @@ class FAKConfigSection(object): self.__dict__["__use_lock"] = False def __getattr__(self, key): - if not self.__dict__["__configuration"].has_section(self.__dict__["__section_name"]) and self.__dict__["__section_name"]!="DEFAULT": + if not self.__dict__["__configuration"]. \ + has_section(self.__dict__["__section_name"]) and \ + self.__dict__["__section_name"]!="DEFAULT": + raise ConfigParser.NoSectionError(self.__dict__["__section_name"]) - if not self.__dict__["__configuration"].has_option(self.__dict__["__section_name"], key): - raise ConfigParser.NoOptionError(key, self.__dict__["__section_name"]) + if not self.__dict__["__configuration"]. \ + has_option(self.__dict__["__section_name"], key): + raise ConfigParser. \ + NoOptionError(key, self.__dict__["__section_name"]) - return self.__dict__["__configuration"].get(self.__dict__["__section_name"], key) + return self.__dict__["__configuration"]. \ + get(self.__dict__["__section_name"], key) def __setattr__(self, key, value): - if self.__dict__["__configuration"].__dict__.has_key("_lock") and self.__dict__["__configuration"].__dict__["_lock"] and self.__dict__["__use_lock"]: + if self.__dict__["__configuration"]. __dict__.has_key("_lock") and \ + self.__dict__["__configuration"].__dict__["_lock"] and \ + self.__dict__["__use_lock"]: raise LockedError(key) - if not self.__dict__["__configuration"].has_section(self.__dict__["__section_name"]) and self.__dict__["__section_name"]!="DEFAULT": - self.__dict__["__configuration"].add_section(self.__dict__["__section_name"]) - self.__dict__["__configuration"].set(self.__dict__["__section_name"], key, value) + if not self.__dict__["__configuration"]. \ + has_section(self.__dict__["__section_name"]) and \ + self.__dict__["__section_name"]!="DEFAULT": + self.__dict__["__configuration"]. \ + add_section(self.__dict__["__section_name"]) + self.__dict__["__configuration"].set(self.__dict__["__section_name"], \ + key, value) def _list(self, key): l = [] @@ -112,9 +129,12 @@ class FAKConfigSection(object): def valueItems(self): """Usefull when you don't care about the name of the items.""" - if not self.__dict__["__configuration"].has_section(self.__dict__["__section_name"]) and self.__dict__["__section_name"]!="DEFAULT": + if not self.__dict__["__configuration"]. \ + has_section(self.__dict__["__section_name"]) and \ + self.__dict__["__section_name"]!="DEFAULT": raise ConfigParser.NoSectionError(self.__dict__["__section_name"]) - tmpList = self.__dict__["__configuration"].items(self.__dict__["__section_name"]) + tmpList = self.__dict__["__configuration"]. \ + items(self.__dict__["__section_name"]) retVal = [] for element in tmpList: retVal.append(element[1]) @@ -122,7 +142,7 @@ class FAKConfigSection(object): class FAKConfigMixIn(object): - """Enhance ConfigParser so we can use it in the python way (config.section.value)""" + """Enhance ConfigParser so (config.section.value) is possible.""" def __getattr__(self, section): return FAKConfigSection(self, section) @@ -142,10 +162,11 @@ createDefaultConfig(Config) Config.read(cfgfile) def getConfigBits(name, cfg = Config): - """returns configuration object loaded with bits from designated configuration file/service - + """Returns conf object loaded with bits from designated config file/service + name - service you need info from - cfg - configuration object containing the system.configuration value, to specify, where to look for the service file""" + cfg - configuration object containing the system.configuration value, + to specify, where to look for the service file""" c = FAKConfig() c.read(os.path.join(cfg.system.configuration, name)) c.lock() diff --git a/pyfirstaidkit/dependency.py b/pyfirstaidkit/dependency.py index de80437..73100bd 100644 --- a/pyfirstaidkit/dependency.py +++ b/pyfirstaidkit/dependency.py @@ -36,8 +36,9 @@ class Dependencies(object): """Add flag""" Logger.info("Setting dependency flag %s", id) self._provide.add(id) - if setactionflag: self._provide.add(id+"?") #Action flags denote activity happening on some regular flag - + #Action flags denote activity happening on some regular flag + if setactionflag: self._provide.add(id+"?") + def unprovide(self, id, setactionflag = True): """Remove flag""" Logger.info("Resetting dependency flag %s", id) @@ -55,13 +56,15 @@ class Dependencies(object): return id in self._provide def introduce(self, s): - """Notifies the system about dependency names used in the plugins, so we can list them in help""" + """Notifies the system about dep names used in the plugins. + + This allows us to list them in help""" self._known = self._known.union(s) def known(self): """Returns list of known flags""" return list(self._known.union(self._provide)) - + def valid(self): """Returns list of valid/provided flags""" return list(self._provide) diff --git a/pyfirstaidkit/errors.py b/pyfirstaidkit/errors.py index 9ce9500..a32d1ec 100644 --- a/pyfirstaidkit/errors.py +++ b/pyfirstaidkit/errors.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -18,7 +18,8 @@ class InvalidFlowStateException(Exception): def __init__(self, flow): - self.message="There appears to be an inconsistency with the %s varialbe. " % flow + self.message="There appears to be an inconsistency with the %s \ + varialbe. " % flow def __str__(self): return self.message @@ -37,10 +38,11 @@ class InvalidPluginNameException(Exception): class GeneralPluginException(Exception): """General exception - This exception should be used for all exceptions that come from the plugins and - have no specific exception class yet. + This exception should be used for all exceptions that come from the + plugins and have no specific exception class yet. """ def __init__(self, plugin, message): - self.message="There was an exception in plugin %s with message %s"%(plugin,message) + self.message="There was an exception in plugin %s with message %s"% \ + (plugin,message) def __str__(self): return self.message diff --git a/pyfirstaidkit/interpret.py b/pyfirstaidkit/interpret.py index e140e4e..ecbfc3e 100644 --- a/pyfirstaidkit/interpret.py +++ b/pyfirstaidkit/interpret.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -33,7 +33,8 @@ class Tasker: name = "Task interpreter" - def __init__(self, cfg, reporting = None, dependencies = None, backups = None, pluginsystem = None): + def __init__(self, cfg, reporting = None, dependencies = None, + backups = None, pluginsystem = None): self._config = cfg self._running = True @@ -53,13 +54,16 @@ class Tasker: self._backups = backups if pluginsystem is None: - self.pluginSystem = PluginSystem(reporting = self._reporting, dependencies = self._provide, backups = self._backups) + self.pluginSystem = PluginSystem(reporting = self._reporting, + dependencies = self._provide, backups = self._backups) else: self.pluginSystem = pluginsystem def interrupt(self): self._running = False - self._reporting.info("You sent an interrupt signal to Tasker! This is not recommended.", level = TASKER, origin = self, importance = logging.WARNING) + self._reporting.info("You sent an interrupt signal to \ + Tasker! This is not recommended.", level = TASKER, + origin = self, importance = logging.WARNING) def flags(self): return self._provide @@ -84,8 +88,9 @@ class Tasker: level = TASKER, origin = self, importance = logging.WARNING) self._provide.provide("root") else: - self._reporting.info("You are not running the firstaidkit as root." \ - "Some plugins may not be available.", level = TASKER, origin = self, importance = logging.WARNING) + self._reporting.info("You are not running the firstaidkit as " \ + "root. Some plugins may not be available.", level = TASKER, + origin = self, importance = logging.WARNING) self._provide.unprovide("root") #initialize the startup set of flags @@ -93,13 +98,15 @@ class Tasker: for flag in self._config.operation._list("flags"): self._provide.provide(flag) - if self._config.operation.mode in ("auto", "auto-flow", "plugin", "flow"): + # For the auto, auto-flow, plugin, flow cases. + if self._config.operation.mode in ("auto", "auto-flow", "plugin", + "flow"): if self._config.operation.mode == "plugin": pluginlist = self._config.operation._list("plugin") else: pluginlist = set(pluginSystem.list()) - + if self._config.operation.mode == "auto-flow": flows = len(pluginlist)*[self._config.operation.flow] elif self._config.operation.mode == "flow": @@ -108,61 +115,87 @@ class Tasker: else: flows = len(pluginlist)*[None] - #iterate through plugins until there is no plugin left or no action performed during whole iteration + #iterate through plugins until there is no plugin left or no + #action performed during whole iteration oldlist = set() actlist = set(zip(pluginlist, flows)) self._running = True while self._running and len(actlist)>0 and oldlist!=actlist: oldlist = copy.copy(actlist) + for plugin,flow in oldlist: - #If plugin does not contain the automated flow or if it ran correctly, remove it from list - if ((flow and not flow in pluginSystem.getplugin(plugin).getFlows()) or - (not flow and not pluginSystem.getplugin(plugin).default_flow in - pluginSystem.getplugin(plugin).getFlows())): - self._reporting.info("Plugin %s does not contain flow %s"% - (plugin, flow or pluginSystem.getplugin(plugin).default_flow,), + #If plugin does not contain the automated flow or if + #it ran correctly, remove it from list + if ((flow and + not flow in pluginSystem.getplugin(plugin).getFlows()) + or (not flow and + not pluginSystem.getplugin(plugin).default_flow in + pluginSystem.getplugin(plugin).getFlows())): + + self._reporting.info("Plugin %s does not contain \ + flow %s"% (plugin, flow or \ + pluginSystem.getplugin(plugin).default_flow,), \ level = TASKER, origin = self) + actlist.remove((plugin, flow)) - elif pluginSystem.autorun(plugin, flow = flow, dependencies = self._config.operation.dependencies != "False"): + + elif (pluginSystem.autorun(plugin, flow = flow, + dependencies = self._config.operation.dependencies + != "False")): actlist.remove((plugin, flow)) #some plugins may not be called because of unfavorable flags for plugin in set(map(lambda x: x[0], actlist)): - self._reporting.info("Plugin %s was not called because of unsatisfied dependencies"% - (plugin,), level = TASKER, origin = self, importance = logging.WARNING) + self._reporting.info("Plugin %s was not called because of \ + unsatisfied dependencies"% (plugin,), level = TASKER, \ + origin = self, importance = logging.WARNING) + + # For the flags case elif self._config.operation.mode == "flags": - self._reporting.table(self._provide.known(), level = TASKER, origin = self, title = "List of flags") + self._reporting.table(self._provide.known(), level = TASKER, + origin = self, title = "List of flags") + + # For the list case elif self._config.operation.mode == "list": #get list of plugins rep = [] for k in pluginSystem.list(): p = pluginSystem.getplugin(k) - flowinfo = [ (f, p.getFlow(f).description) for f in p.getFlows() ] - rep.append((k, p.name, p.version, p.author, p.description, p.default_flow, flowinfo)) - self._reporting.table(rep, level = TASKER, origin = self, title = "List of plugins") + flowinfo = [(f, p.getFlow(f).description) for f in p.getFlows()] + rep.append((k, p.name, p.version, p.author, p.description, + p.default_flow, flowinfo)) + self._reporting.table(rep, level = TASKER, origin = self, + title = "List of plugins") + + # For the info case elif self._config.operation.mode == "info": #get info about plugin try: p = pluginSystem.getplugin(self._config.operation.params) except KeyError: - Logger.error("No such plugin '%s'" % (self._config.operation.params,)) + Logger.error("No such plugin '%s'" % + (self._config.operation.params,)) return False flowinfo = [ (f, p.getFlow(f).description) for f in p.getFlows() ] - rep = {"id": self._config.operation.params, "name": p.name, - "version": p.version, "author": p.author, - "description": p.description, "flow": p.default_flow, "flows": flowinfo} - self._reporting.tree(rep, level = TASKER, origin = self, - title = "Information about plugin %s" % (self._config.operation.params,)) + rep = {"id": self._config.operation.params, "name": p.name, + "version": p.version, "author": p.author, + "description": p.description, "flow": p.default_flow, + "flows": flowinfo} + self._reporting.tree(rep, level = TASKER, origin = self, + title = "Information about plugin %s" % \ + (self._config.operation.params,)) + + # Any other case else: Logger.error("Incorrect task specified") self._reporting.stop(level = TASKER, origin = self) return False if self._config.operation.printinfo: - print "--- Info section ---" + print("--- Info section ---") Info.write(sys.stdout) - print "--------------------" + print("--------------------") self._reporting.stop(level = TASKER, origin = self) return True diff --git a/pyfirstaidkit/issue.py b/pyfirstaidkit/issue.py index 2913943..e706fa4 100644 --- a/pyfirstaidkit/issue.py +++ b/pyfirstaidkit/issue.py @@ -31,8 +31,11 @@ class SimpleIssue(object): self._happened = False self._fixed = False - def set(self, happened = None, fixed = None, checked = None, reporting = None, **kwreportingargs): - """Set the state of this issue and send a report (if reporting is not None)""" + def set(self, happened = None, fixed = None, checked = None, + reporting = None, **kwreportingargs): + """Set the state of this issue and send a report + + The report is set if reporting is not None""" if happened: self._happened = happened if fixed: @@ -44,6 +47,7 @@ class SimpleIssue(object): def happened(self): """Get the 'issue happened' flag. + Return values: True - YES it happened False - NO, it is OK @@ -53,9 +57,10 @@ Return values: return None else: return self._happened - + def fixed(self): """Get the 'issue fixed' flag. + Return values: True - YES it is fixed False - NO, it is still broken @@ -91,7 +96,8 @@ Return values: class Issue(SimpleIssue): name = "Parent issue" - description = "This happens when you use the wrong object in the issues list" + description = "This happens when you use the wrong object in the issues \ + list" def __init__(self, plugin, reporting = None): SimpleIssue.__init__(self, self.name, self.description) @@ -99,7 +105,9 @@ class Issue(SimpleIssue): self._reporting = reporting def detect(self): - """Detect if this situation happened and store some information about it, so we can fix it + """Detect if situation happened and store some information about it. + + This is done so we can fix it Return values: True - check OK False - check Failed @@ -110,10 +118,13 @@ Return values: if self._checked or self._fixed: return not self._fixed and self._checked - return None #no error, please do the check (so the child-class knows to actually do something) + #no error, please do the check (so the child-class knows to actually + #do something) + return None def fix(self): """Fix the situation if needed + Return values: True - fix OK False - fix Failed @@ -123,6 +134,6 @@ Return values: #if it was not checked, the check si needed too if not self._checked or self._fixed: return self._fixed and self._checked - - return None #no fix error, please do the fix (so the child-class knows to actually do something) - + #no fix error, please do the fix (so the child-class knows to actually + #do something) + return None diff --git a/pyfirstaidkit/plugins.py b/pyfirstaidkit/plugins.py index 28b2221..5bd857f 100644 --- a/pyfirstaidkit/plugins.py +++ b/pyfirstaidkit/plugins.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -55,8 +55,8 @@ class Plugin(object): # Dictionary that holds all the flows. The keys for each flow is its # name. The flow will be addressed by this name. The plugin developer # Can add as many flows as he wants. The developer must use the instance. - # flows["name"] = SomeFlow. Be aware that you can overwirhte - # previously added flows. This class attribute has to be initialized by + # flows["name"] = SomeFlow. Be aware that you can overwirhte + # previously added flows. This class attribute has to be initialized by # each plugin using flows = Flow.init(ParentClass) # flows = {} @@ -79,36 +79,46 @@ class Plugin(object): # has to be named "diagnose" # flows["diagnose"] = Flow({ - initial : {Return: "prepare"}, - "prepare" : {ReturnSuccess: "diagnose", None: "clean"}, - "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "clean", None: "clean"}, - "clean" : {ReturnSuccess: final, ReturnFailure: final, None: final} - }, description="The default, fully automated, diagnose sequence") + initial : {Return: "prepare"}, + "prepare" : {ReturnSuccess: "diagnose", None: "clean"}, + "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "clean", + None: "clean"}, + "clean" : {ReturnSuccess: final, ReturnFailure: final, None: final} + }, description="The default, fully automated, diagnose sequence") + flows["fix"] = Flow({ - initial : {Return: "prepare"}, - "prepare" : {ReturnSuccess: "diagnose", None: "clean"}, - "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "backup", None: "clean"}, - "backup" : {ReturnSuccess: "fix", ReturnFailure: "clean", None: "clean"}, - "fix" : {ReturnSuccess: "clean", ReturnFailure: "restore", None: "restore"}, - "restore" : {ReturnSuccess: "clean", ReturnFailure: "clean", None: "clean"}, - "clean" : {ReturnSuccess: final, ReturnFailure: final, None: final} - }, description="The default, fully automated, fixing sequence") - - # By default, when no other parameters are passed, we use the diagnose flow as - # the default flow to run. You can change this, BUT it MUST always be a non-changing - # non-destructive and safe flow, which does the diagnostics + initial : {Return: "prepare"}, + "prepare" : {ReturnSuccess: "diagnose", None: "clean"}, + "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "backup", + None: "clean"}, + "backup" : {ReturnSuccess: "fix", ReturnFailure: "clean", + None: "clean"}, + "fix" : {ReturnSuccess: "clean", ReturnFailure: "restore", + None: "restore"}, + "restore" : {ReturnSuccess: "clean", ReturnFailure: "clean", + None: "clean"}, + "clean" : {ReturnSuccess: final, ReturnFailure: final, None: final} + }, description="The default, fully automated, fixing sequence") + + # By default, when no other parameters are passed, we use the diagnose + # flow as the default flow to run. You can change this, BUT it MUST always + # be a non-changing non-destructive and safe flow, which does the + # diagnostics default_flow = "diagnose" - def __init__(self, flow, reporting, dependencies, path = None, backups = None, info = None): + def __init__(self, flow, reporting, dependencies, path = None, + backups = None, info = None): """ Initialize the instance. flow -- Name of the flow to be used with this instance. reporting -- object used to report information to the user - dependencies -- object encapsulating the inter-plugin dependency API (require, provide) + dependencies -- object encapsulating the inter-plugin dependency API + (require, provide) path -- directory from where was this plugin imported - The flow is defined in the __init__ so we don't have to worry about changing it. + The flow is defined in the __init__ so we don't have to worry about + changing it. """ self._reporting = reporting self._dependencies = dependencies @@ -155,9 +165,10 @@ class Plugin(object): """Defines the current flow to name. flow -- Name of the flow - This function is to be called from the __init__ only. There will be the flows defined by the - Plugin class and the flows defined by the actual plugin. We will first search the Plugin - class and then the plugin itself for the name. + This function is to be called from the __init__ only. There will be the + flows defined by the Plugin class and the flows defined by the actual + plugin. We will first search the Plugin class and then the plugin + itself for the name. """ # # The flow that will be used for the instance. @@ -172,7 +183,7 @@ class Plugin(object): """Return a set with the names of all possible flows.""" fatherf = cls.flows.keys() return set(fatherf) - + @classmethod def getFlow(cls, name): """Return a Flow object associated with provided name""" @@ -184,31 +195,35 @@ class Plugin(object): #dependency stuff @classmethod def getDeps(cls): - """Return list of conditions required to be set before automated run can be done""" + """Return list of conditions required to be set before automated rune""" return set() - + @classmethod def getConflicts(cls): - """Return list of conditions required to be UNset before automated run can be done""" + """Return list of conditions required to be UNset before automated run""" return set() - #methods available only for instance, see interpreter.py and dependency stuff there + #methods available only for instance, see interpreter.py and dependency + #stuff there #def require(self, id) #def provide(self, id) #list of all actions provided def actions(self): """Returns list of available actions""" - return set(["prepare", "backup", "diagnose", "describe", "fix", "restore", "clean"]) + return set(["prepare", "backup", "diagnose", "describe", "fix", + "restore", "clean"]) def nextstate(self, state=None, result=None): - """Returns next state when analizing self._state, self._result and the self.cflow in automode. + """Returns next state when analizing self.{_state,_result,cflow}. + This is relevant for automode. state -- Name of hte function. result -- The return value of the previous function - We do not check for validity of the key in the self.cflow. If key is invalid, function will - Traceback. When self._state = self.final the function will traceback. This situation must - be handled outside this function. If an automatica iteration is needed that avoids the + We do not check for validity of the key in the self.cflow. If key is + invalid, function will Traceback. When self._state = self.final the + function will traceback. This situation must be handled outside this + function. If an automatica iteration is needed that avoids the necesity to address the self.final state, use __iter__ and next. """ # If any of the vals are missing, we default to the current ones. @@ -237,7 +252,8 @@ class Plugin(object): def next(self): """Iteration function. - Will return (self._state, self._result). The function that was executed and the return value. + Will return (self._state, self._result). The function that was executed + and the return value. """ func = self.nextstate() @@ -248,8 +264,10 @@ class Plugin(object): # Execute the function. self.call(func) except Exception, e: #fallback, when there is some error in plugin - self._reporting.exception(level = TASK, origin = self, message = func+" raised "+str(e)) - self._reporting.stop(level = TASK, origin = self, message = func) + self._reporting.exception(level = TASK, origin = self, + message = func+" raised "+str(e)) + self._reporting.stop(level = TASK, origin = self, + message = func) pass return (self._state, self._result) @@ -260,55 +278,65 @@ class Plugin(object): def prepare(self): """Initial actions. - All the actions that must be done before the execution of any plugin function. - This function generaly addresses things that are global to the plugin. + All the actions that must be done before the execution of any plugin + function. This function generaly addresses things that are global to + the plugin. """ #We want these functions to be overridden by the plugin developer. if self.__class__ is Plugin: - Logger.warning("Prepare is an abstract method, it should be used as such.") + Logger.warning("Prepare is an abstract method, it should be used \ + as such.") def clean(self): """Final actions. - All the actions that must be done after the exection of all plugin functions. - This function generaly addresses things that are global and need to be closed - off, like file descriptos, or mounted partitions.... + All the actions that must be done after the exection of all plugin + functions. This function generaly addresses things that are global + and need to be closed off, like file descriptos, or mounted + partitions.... """ #We want these functions to be overridden by the plugin developer. if self.__class__ is Plugin: - Logger.warning("Clean is an abstract method, it should be used as such.") + Logger.warning("Clean is an abstract method, it should be used as \ + such.") def backup(self): """Gather important information needed for restore.""" #We want these functions to be overridden by the plugin developer. if self.__class__ is Plugin: - Logger.warning("Backup is an abstract method, it should be used as such.") + Logger.warning("Backup is an abstract method, it should be used as \ + such.") def restore(self): """Try to restore the previous state described in backup.""" #We want these functions to be overridden by the plugin developer. if self.__class__ is Plugin: - Logger.warning("Restore is an abstract method, it should be used as such.") + Logger.warning("Restore is an abstract method, it should be used \ + as such.") def diagnose(self): """Diagnose the situation.""" #We want these functions to be overridden by the plugin developer. if self.__class__ is Plugin: - Logger.warning("Diagnose is an abstract method, it should be used as such.") + Logger.warning("Diagnose is an abstract method, it should be used \ + as such.") def fix(self): """Try to fix whatever is wrong in the system.""" #We want these functions to be overridden by the plugin developer. if self.__class__ is Plugin: - Logger.warning("Fix is an abstract method, it should be used as such.") + Logger.warning("Fix is an abstract method, it should be used as \ + such.") class IssuesPlugin(Plugin): - """Simple plugin which uses Issue classes to test more small and INDEPENDENT issues in the system. -Just fill the issue_tests list with classes describing the tests and let it run.""" + """Plugin which uses Issue classes to test smaller and INDEPENDENT issues. + + Just fill the issue_tests list with classes describing the tests and let + it run.""" issue_tests = [] #List of Issue classes to check set_flags = [] #flags to set when everything is OK - + def __init__(self, *args, **kwargs): Plugin.__init__(self, *args, **kwargs) self.tests = [] @@ -316,7 +344,8 @@ Just fill the issue_tests list with classes describing the tests and let it run. def prepare(self): """Prepare the issues list""" for i in self.issue_tests: - self._reporting.info(level = TASK, origin = self, message = "Preparing tests for '%s'" % (i.name,)) + self._reporting.info(level = TASK, origin = self, + message = "Preparing tests for '%s'" % (i.name,)) issue = i(plugin = self) self.tests.append(issue) self._reporting.issue(level = TASK, origin = self, issue = issue) @@ -328,12 +357,14 @@ Just fill the issue_tests list with classes describing the tests and let it run. result = False happened = False for i in self.tests: - self._reporting.info(level = TASK, origin = self, message = "Investigating '%s'" % (i.name,)) + self._reporting.info(level = TASK, origin = self, + message = "Investigating '%s'" % (i.name,)) result = result or i.detect() self._reporting.issue(level = TASK, origin = self, issue = i) if i.happened(): happened = True - self._reporting.info(level = TASK, origin = self, message = i.str()) + self._reporting.info(level = TASK, origin = self, + message = i.str()) if result and not happened: self._result=ReturnSuccess @@ -350,7 +381,8 @@ Just fill the issue_tests list with classes describing the tests and let it run. result = False fixed = True for i in self.tests: - self._reporting.info(level = TASK, origin = self, message = "Fixing '%s'" % (i.name,)) + self._reporting.info(level = TASK, origin = self, + message = "Fixing '%s'" % (i.name,)) result = result or i.fix() self._reporting.issue(level = TASK, origin = self, issue = i) if not i.fixed(): @@ -373,12 +405,12 @@ Just fill the issue_tests list with classes describing the tests and let it run. class FlagTrackerPlugin(Plugin): - """This kind of plugin monitores all the flags in the system and when certain flags - are set, provides some kind of higher level flag. + """This kind of plugin monitores all the flags in the system and when + certain flags are set, provides some kind of higher level flag. Example: - monitor flags 'filesystem_drive', 'filesystem_lvm' and 'filesystem_ext3' and if - everything is ok set the master flag 'filesystem'""" + monitor flags 'filesystem_drive', 'filesystem_lvm' and 'filesystem_ext3' + and if everything is ok set the master flag 'filesystem'""" # Higher level master flag to set #flag_decide = "x_decide" @@ -414,10 +446,12 @@ class FlagTrackerPlugin(Plugin): """Decide about state of higher level flags.""" #We want these functions to be overridden by the plugin developer. if self.__class__ is FlagTrackerPlugin: - Logger.warning("Decide is an abstract method, it should be used as such.") - + Logger.warning("Decide is an abstract method, it should be used \ + as such.") + if self.flag_decide is None: - Logger.warning("You have to specify flag to set when everything is ok.") + Logger.warning("You have to specify flag to set when everything \ + is ok.") return Return for flag in self.flag_list: @@ -433,7 +467,7 @@ class PluginSystem(object): name = "Plugin System" - def __init__(self, reporting, dependencies, config = Config, backups = None): + def __init__(self, reporting, dependencies, config=Config, backups=None): self._paths = Config.paths.valueItems() self._backups = backups self._reporting = reporting @@ -443,25 +477,32 @@ class PluginSystem(object): for path in self._paths: if not os.path.isdir(path): - self._reporting.debug("The path %s does not exist" % path, level = PLUGINSYSTEM, origin = self) + self._reporting.debug("The path %s does not exist" % path, + level = PLUGINSYSTEM, origin = self) continue #create list of potential modules in the path importlist = set() for f in os.listdir(path): fullpath = os.path.join(path, f) - self._reporting.debug("Processing file: %s" % (f,), level = PLUGINSYSTEM, origin = self) - if os.path.isdir(fullpath) and os.path.isfile(os.path.join(path, f, "__init__.py")): + self._reporting.debug("Processing file: %s" % (f,), + level = PLUGINSYSTEM, origin = self) + if os.path.isdir(fullpath) \ + and os.path.isfile(os.path.join(path, f, "__init__.py")): importlist.add(f) - self._reporting.debug("Adding python module (directory): %s" % (f,), - level = PLUGINSYSTEM, origin = self) - elif os.path.isfile(fullpath) and (f[-3:]==".so" or f[-3:]==".py"): + self._reporting.debug("Adding python module (directory): %s" + % (f,), level = PLUGINSYSTEM, origin = self) + + elif os.path.isfile(fullpath) and (f[-3:]==".so" + or f[-3:]==".py"): importlist.add(f[:-3]) - self._reporting.debug("Adding python module (file): %s" % (f,), - level = PLUGINSYSTEM, origin = self) - elif os.path.isfile(fullpath) and (f[-4:]==".pyc" or f[-4:]==".pyo"): + self._reporting.debug("Adding python module (file): %s" + % (f,), level = PLUGINSYSTEM, origin = self) + + elif os.path.isfile(fullpath) and (f[-4:]==".pyc" + or f[-4:]==".pyo"): importlist.add(f[:-4]) - self._reporting.debug("Adding python module (compiled): %s" % (f,), - level = PLUGINSYSTEM, origin = self) + self._reporting.debug("Adding python module (compiled): %s" + % (f,), level = PLUGINSYSTEM, origin = self) #try to import the modules as FirstAidKit.plugins.modulename for m in importlist: @@ -470,18 +511,25 @@ class PluginSystem(object): imp.acquire_lock() try: - self._reporting.debug("Importing module %s from %s" % (m, path), - level = PLUGINSYSTEM, origin = self) + self._reporting.debug("Importing module %s from %s" + % (m, path), level = PLUGINSYSTEM, origin = self) moduleinfo = imp.find_module(m, [path]) - module = imp.load_module(".".join([FirstAidKit.__name__, m]), *moduleinfo) - self._deps.introduce(module.get_plugin().getDeps()) #notify the dependency system about all used dependencies - self._deps.introduce(module.get_plugin().getConflicts()) #notify the dependency system about all used reverse-dependencies + module = imp.load_module(".".join([FirstAidKit.__name__,m]), + *moduleinfo) + #notify the dependency system about all used dependencies + self._deps.introduce(module.get_plugin().getDeps()) + #notify the dependency system about all used + #reverse-dependencies + self._deps.introduce(module.get_plugin().getConflicts()) self._plugins[m] = module - self._reporting.debug("Module %s successfully imported with basedir %s" % - (m, os.path.dirname(module.__file__)), level = PLUGINSYSTEM, origin = self) + self._reporting.debug("Module %s successfully imported \ + with basedir %s" % + (m, os.path.dirname(module.__file__)), + level = PLUGINSYSTEM, origin = self) except Exception, e: - self._reporting.error(message = "Module %s was NOT imported, because of %s" % - (m, str(e)), level = PLUGINSYSTEM, origin = self) + self._reporting.error(message = "Module %s was NOT \ + imported, because of %s" % + (m, str(e)), level = PLUGINSYSTEM, origin = self) finally: imp.release_lock() @@ -500,11 +548,12 @@ class PluginSystem(object): self._reporting.start(level = PLUGIN, origin = self, message = plugin) if plugin in self._plugins.keys(): - pklass = self._plugins[plugin].get_plugin() #get top level class of plugin + #get top level class of plugin + pklass = self._plugins[plugin].get_plugin() else: - self._reporting.exception(message = "Plugin %s was not found" % plugin, - level = PLUGINSYSTEM, origin = self) - self._reporting.stop(level = PLUGIN, origin = self, message = plugin) + self._reporting.exception(message = "Plugin %s was not found" % + plugin, level = PLUGINSYSTEM, origin = self) + self._reporting.stop(level=PLUGIN, origin=self, message=plugin) raise InvalidPluginNameException(plugin) plugindir = os.path.dirname(self._plugins[plugin].__file__) @@ -520,9 +569,10 @@ class PluginSystem(object): Logger.info("Using %s flow" % flowName) if flowName not in flows: - self._reporting.exception(message = "Flow %s does not exist in plugin %s" % - (flowName, plugin), level = PLUGINSYSTEM, origin = self) - self._reporting.stop(level = PLUGIN, origin = self, message = plugin) + self._reporting.exception(message = "Flow %s does not exist in \ + plugin %s" % (flowName, plugin), level = PLUGINSYSTEM, + origin = self) + self._reporting.stop(level=PLUGIN, origin=self, message=plugin) raise InvalidFlowNameException(flowName) if dependencies: @@ -531,29 +581,38 @@ class PluginSystem(object): Logger.info("depends on: %s" % (", ".join(deps),)) for d in deps: if not self._deps.require(d): - Logger.info("depends on usatisfied condition: %s" % (d,)) - self._reporting.stop(level = PLUGIN, origin = self, message = plugin) + Logger.info("depends on usatisfied condition: %s" % + (d,)) + self._reporting.stop(level = PLUGIN, origin = self, + message = plugin) return False deps = pklass.getConflicts() if len(deps)>0: - Logger.info("depends on flags to be unset: %s" % (", ".join(deps),)) + Logger.info("depends on flags to be unset: %s" % + (", ".join(deps),)) for d in deps: if self._deps.require(d): - Logger.info("depends on condition to be UNset: %s" % (d,)) - self._reporting.stop(level = PLUGIN, origin = self, message = plugin) + Logger.info("depends on condition to be UNset: %s" % + (d,)) + self._reporting.stop(level = PLUGIN, origin = self, + message = plugin) return False infosection = getattr(Info, plugin) infosection.unlock() - p = pklass(flowName, reporting = self._reporting, dependencies = self._deps, backups = self._backups, path = plugindir, info = infosection) + p = pklass(flowName, reporting = self._reporting, + dependencies = self._deps, backups = self._backups, + path = plugindir, info = infosection) for (step, rv) in p: #autorun all the needed steps Logger.info("Running step %s in plugin %s ...", step, plugin) - Logger.info("%s is current step and %s is result of that step." % (step, rv)) + Logger.info("%s is current step and %s is result of that step." % + (step, rv)) self._reporting.stop(level = PLUGIN, origin = self, message = plugin) return True def getplugin(self, plugin): - """Get top level class of plugin, so we can create the instance and call the steps manually""" + """Get top level class of plugin, so we can create the instance and + call the steps manually""" return self._plugins[plugin].get_plugin() diff --git a/pyfirstaidkit/reporting.py b/pyfirstaidkit/reporting.py index 8a19a07..0743ee9 100644 --- a/pyfirstaidkit/reporting.py +++ b/pyfirstaidkit/reporting.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -45,7 +45,8 @@ QUESTION = 999 #type of message which contains respond-to field END = 1000 #End of operations, final message class Origin(object): - """Class which defines mandatory interface for origin, when using the reporting system""" + """Class which defines mandatory interface for origin, + when using the reporting system""" name = "Origin:Unknown" def __init__(self, name): @@ -82,12 +83,17 @@ class Reports(object): self._notify = [] def notify(self, cb, *args, **kwargs): - """When putting anything new into the Queue, run notifications callbacks. Usefull for Gui and single-thread reporting. - The notification function has parameters: message recorded to the queue, any parameters provided when registering""" + """When putting anything new into the Queue, run notifications + callbacks. Usefull for Gui and single-thread reporting. + The notification function has parameters: message recorded to the + queue, any parameters provided when registering""" return self._notify.append((cb, args, kwargs)) - def put(self, message, level, origin, action, importance = logging.INFO, reply = None, title = "", destination = None): - data = {"level": level, "origin": origin, "action": action, "importance": importance, "message": message, "reply": reply, "title": title} + def put(self, message, level, origin, action, importance = logging.INFO, + reply = None, title = "", destination = None): + data = {"level": level, "origin": origin, "action": action, + "importance": importance, "message": message, + "reply": reply, "title": title} if destination is not None: return destination.put(data) @@ -99,7 +105,9 @@ class Reports(object): except Queue.Full, e: if not self._round: raise - destination.get() #Queue is full and it is a round buffer.. remove the oldest item and use the free space to put the new item + #Queue is full and it is a round buffer.. remove the oldest item + #and use the free space to put the new item + destination.get() ret=destination.put(data) finally: self._queue_lock.release() @@ -137,15 +145,20 @@ class Reports(object): def error(self, message, level, origin, action = INFO): Logger.error(origin.name+": "+message) - return self.put(message, level, origin, action, importance = logging.ERROR) + return self.put(message, level, origin, action, + importance = logging.ERROR) def start(self, level, origin, message = ""): - return self.put(message, level, origin, START, importance = logging.DEBUG) + return self.put(message, level, origin, START, + importance = logging.DEBUG) def stop(self, level, origin, message = ""): - return self.put(message, level, origin, STOP, importance = logging.DEBUG) + return self.put(message, level, origin, STOP, + importance = logging.DEBUG) - def progress(self, position, maximum, level, origin, importance = logging.INFO): - return self.put((position, maximum), level, origin, PROGRESS, importance = importance) + def progress(self, position, maximum, level, origin, + importance = logging.INFO): + return self.put((position, maximum), level, origin, PROGRESS, + importance = importance) def issue(self, issue, level, origin, importance = logging.INFO): Logger.debug(origin.name+": issue changed state to "+str(issue)) @@ -158,14 +171,19 @@ class Reports(object): Logger.debug(origin.name+": "+message) return self.put(message, level, origin, INFO, importance = importance) - def tree(self, message, level, origin, importance = logging.INFO, title = ""): - return self.put(message, level, origin, TREE, importance = importance, title = title) - def table(self, message, level, origin, importance = logging.INFO, title = ""): - return self.put(message, level, origin, TABLE, importance = importance, title = title) + def tree(self, message, level, origin, importance = logging.INFO, + title = ""): + return self.put(message, level, origin, TREE, importance = importance, + title = title) + def table(self, message, level, origin, importance = logging.INFO, + title = ""): + return self.put(message, level, origin, TABLE, + importance = importance, title = title) def alert(self, message, level, origin, importance = logging.WARNING): return self.put(message, level, origin, ALERT, importance = importance) def exception(self, message, level, origin, importance = logging.ERROR): Logger.error(origin.name+": "+message) - return self.put(message, level, origin, EXCEPTION, importance = importance) + return self.put(message, level, origin, EXCEPTION, + importance = importance) diff --git a/pyfirstaidkit/returns.py b/pyfirstaidkit/returns.py index 614e7a3..4757977 100644 --- a/pyfirstaidkit/returns.py +++ b/pyfirstaidkit/returns.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -51,7 +51,8 @@ class ReturnNone(Return): # 3. If the plugin is in fix stage and the problem was not fixed, the return # value should be Failure. On the other hand if the fix has been done # the return value should be Success. -# Remember that the actual values of the classes is not checked, what is checked +# Remember that the actual values of the classes is not checked, what is +# checked # is that the return value be the specific class. # diff --git a/pyfirstaidkit/utils/__init__.py b/pyfirstaidkit/utils/__init__.py index a6a1086..035de46 100644 --- a/pyfirstaidkit/utils/__init__.py +++ b/pyfirstaidkit/utils/__init__.py @@ -1,16 +1,16 @@ # First Aid Kit - diagnostic and repair tool for Linux # Copyright (C) 2007 Martin Sivak <msivak@redhat.com> -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/pyfirstaidkit/utils/backup.py b/pyfirstaidkit/utils/backup.py index f2edd03..5271cd6 100644 --- a/pyfirstaidkit/utils/backup.py +++ b/pyfirstaidkit/utils/backup.py @@ -208,7 +208,7 @@ class FileBackupStore(BackupStoreInterface): else: os.makedirs(self._path) self.__class__._singleton = weakref.proxy(self) - print "Backup system initialized" + print("Backup system initialized") def getBackup(self, id): if not self._backups.has_key(id): @@ -228,7 +228,7 @@ class FileBackupStore(BackupStoreInterface): for id,backup in self._backups.iteritems(): backup.cleanup() os.rmdir(self._path) - print "Backup closed" + print("Backup closed") @classmethod def get(cls, path): |