Dear Lords of Func, This is a pretty significant change to the way our modules are currently written. The result is that it makes writing modules much more simple, by removing all hard-coded method definitions and register_rpc methods. Some brief testing on my end shows it to work fine, but still needs more testing to ensure that we don't regress. If no one has any problems with it, I can commit it. luke === - Auto-detect and load all FuncModules. This obsoletes the need to have our modules define a register_rpc method. - Use introspection in our FuncModule to auto-register all method handlers that do not being with an underscore. This obsoletes the need to hardcode methods in our modules. - Remove all __init__ methods from our modules, along with register_rpc - Modify the func-create-module script to reflect these changes. Note that doing 'from modules import func_module' is no longer supported in our modules, do to some interesting path issues with our auto-detection code. Supported methods are now: 'import func_module' or 'from func.minion.modules import func_module' diff --git a/func/minion/module_loader.py b/func/minion/module_loader.py index 6fb2a6b..f8f8f42 100755 --- a/func/minion/module_loader.py +++ b/func/minion/module_loader.py @@ -19,10 +19,11 @@ import sys from gettext import gettext _ = gettext - from func import logger logger = logger.Logger().logger +from inspect import isclass +from func.minion.modules import func_module def module_walker(topdir): module_files = [] @@ -80,13 +81,15 @@ def load_modules(blacklist=None): continue try: + # Auto-detect and load all FuncModules blip = __import__("modules.%s" % ( mod_imp_name), globals(), locals(), [mod_imp_name]) - if not hasattr(blip, "register_rpc"): - errmsg = _("%(module_path)s%(modname)s module not a proper module") - logger.warning(errmsg % {'module_path': module_file_path, 'modname':mod_imp_name}) - bad_mods[mod_imp_name] = True - continue - mods[mod_imp_name] = blip + for obj in dir(blip): + attr = getattr(blip, obj) + if isclass(attr): + if issubclass(attr, func_module.FuncModule): + logger.debug("Loading %s module" % attr) + mods[mod_imp_name] = attr() + except ImportError, e: # A module that raises an ImportError is (for now) simply not loaded. errmsg = _("Could not load %s module: %s") diff --git a/func/minion/modules/command.py b/func/minion/modules/command.py index 3329927..9b93de5 100644 --- a/func/minion/modules/command.py +++ b/func/minion/modules/command.py @@ -13,19 +13,11 @@ Abitrary command execution module for func. """ -from modules import func_module - +import func_module import sub_process class Command(func_module.FuncModule): - def __init__(self): - self.methods = { - "run" : self.run, - "exists" : self.exists, - } - func_module.FuncModule.__init__(self) - def run(self, command): """ Runs a command, returning the return code, stdout, and stderr as a tuple. @@ -46,7 +38,3 @@ class Command(func_module.FuncModule): if os.access(command, os.X_OK): return True return False - - -methods = Command() -register_rpc = methods.register_rpc \ No newline at end of file diff --git a/func/minion/modules/copyfile.py b/func/minion/modules/copyfile.py index 6c81098..85001f8 100644 --- a/func/minion/modules/copyfile.py +++ b/func/minion/modules/copyfile.py @@ -14,7 +14,7 @@ import os import time import shutil -from modules import func_module +import func_module @@ -22,15 +22,6 @@ from modules import func_module class CopyFile(func_module.FuncModule): version = "0.0.1" api_version = "0.0.2" - - - - def __init__(self): - self.methods = { - "copyfile" : self.copyfile, - "checksum" : self.checksum - } - func_module.FuncModule.__init__(self) def _checksum_blob(self, blob): CHUNK=2**16 @@ -118,8 +109,3 @@ class CopyFile(func_module.FuncModule): #XXX logger output here return False return True - - - -methods = CopyFile() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/filetracker.py b/func/minion/modules/filetracker.py index 0aa4a49..8deb382 100644 --- a/func/minion/modules/filetracker.py +++ b/func/minion/modules/filetracker.py @@ -16,7 +16,7 @@ ## # func modules -from modules import func_module +import func_module # other modules from stat import * @@ -30,17 +30,6 @@ CONFIG_FILE='/etc/func/modules/filetracker.conf' class FileTracker(func_module.FuncModule): - def __init__(self): - self.methods = { - "track" : self.track, - "untrack" : self.untrack, - "info" : self.inventory, - "inventory" : self.inventory, - } - func_module.FuncModule.__init__(self) - - #========================================================== - def __load(self): """ Parse file and return data structure. @@ -145,7 +134,7 @@ class FileTracker(func_module.FuncModule): gid = filestat[ST_GID] if not os.path.isdir(file_name) and checksum_enabled: sum_handle = open(file_name) - hash = self.sumfile(sum_handle) + hash = self.__sumfile(sum_handle) sum_handle.close() else: hash = "N/A" @@ -185,7 +174,7 @@ class FileTracker(func_module.FuncModule): #========================================================== - def sumfile(self, fobj): + def __sumfile(self, fobj): """ Returns an md5 hash for an object with read() method. credit: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266486 @@ -198,6 +187,3 @@ class FileTracker(func_module.FuncModule): break m.update(d) return m.hexdigest() - -methods = FileTracker() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/func_module.py b/func/minion/modules/func_module.py index 5965e24..c911b91 100644 --- a/func/minion/modules/func_module.py +++ b/func/minion/modules/func_module.py @@ -10,6 +10,7 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## +import inspect from func import logger from func.config import read_config @@ -34,7 +35,7 @@ class FuncModule(object): "module_api_version" : self.__module_api_version, "module_description" : self.__module_description, "list_methods" : self.__list_methods - } + } def __init_log(self): log = logger.Logger() @@ -45,8 +46,11 @@ class FuncModule(object): # can get clobbbered by subclass versions for meth in self.__base_methods: handlers["%s.%s" % (module_name, meth)] = self.__base_methods[meth] - for meth in self.methods: - handlers["%s.%s" % (module_name,meth)] = self.methods[meth] + + # register all methods that don't start with an underscore + for attr in dir(self): + if inspect.ismethod(getattr(self, attr)) and attr[0] != '_': + handlers["%s.%s" % (module_name, attr)] = getattr(self, attr) def __list_methods(self): return self.methods.keys() + self.__base_methods.keys() @@ -59,7 +63,3 @@ class FuncModule(object): def __module_description(self): return self.description - - -methods = FuncModule() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/hardware.py b/func/minion/modules/hardware.py index acf5988..5136228 100644 --- a/func/minion/modules/hardware.py +++ b/func/minion/modules/hardware.py @@ -20,18 +20,11 @@ import sys # our modules import sub_process -from modules import func_module +import func_module # ================================= class HardwareModule(func_module.FuncModule): - def __init__(self): - self.methods = { - "info" : self.info, - "inventory" : self.inventory, # for func-inventory - "hal_info" : self.hal_info - } - func_module.FuncModule.__init__(self) def hal_info(self): """ @@ -131,6 +124,3 @@ def hw_info(with_devices=True): }) return data - -methods = HardwareModule() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/mount.py b/func/minion/modules/mount.py index e8e41ce..1b4671d 100644 --- a/func/minion/modules/mount.py +++ b/func/minion/modules/mount.py @@ -13,18 +13,10 @@ ## import sub_process, os -from modules import func_module +import func_module class MountModule(func_module.FuncModule): - def __init__(self): - self.methods = { - "list": self.list, - "mount": self.mount, - "umount": self.umount, - "inventory": self.inventory, - } - func_module.FuncModule.__init__(self) def list(self): cmd = sub_process.Popen(["/bin/cat", "/proc/mounts"], executable="/bin/cat", stdout=sub_process.PIPE, shell=False) @@ -86,7 +78,3 @@ class MountModule(func_module.FuncModule): def inventory(self, flatten=True): return self.list() - - -methods = MountModule() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/nagios-check.py b/func/minion/modules/nagios-check.py index a902762..80889ce 100644 --- a/func/minion/modules/nagios-check.py +++ b/func/minion/modules/nagios-check.py @@ -13,18 +13,11 @@ Abitrary command execution module for func. """ -from modules import func_module - +import func_module import sub_process class Nagios(func_module.FuncModule): - def __init__(self): - self.methods = { - "run" : self.run - } - func_module.FuncModule.__init__(self) - def run(self, check_command): """ Runs a nagios check returning the return code, stdout, and stderr as a tuple. @@ -35,8 +28,3 @@ class Nagios(func_module.FuncModule): cmdref = sub_process.Popen(command.split(),stdout=sub_process.PIPE,stderr=sub_process.PIPE, shell=False) data = cmdref.communicate() return (cmdref.returncode, data[0], data[1]) - -methods = Nagios() -register_rpc = methods.register_rpc - - diff --git a/func/minion/modules/process.py b/func/minion/modules/process.py index 92c6d6a..18b5abe 100644 --- a/func/minion/modules/process.py +++ b/func/minion/modules/process.py @@ -18,19 +18,11 @@ import sub_process import codes # our modules -from modules import func_module +import func_module # ================================= class ProcessModule(func_module.FuncModule): - def __init__(self): - self.methods = { - "info" : self.info, - "kill" : self.kill, - "pkill" : self.pkill, - "mem" : self.mem - } - func_module.FuncModule.__init__(self) def info(self,flags="-auxh"): """ @@ -208,6 +200,3 @@ class ProcessModule(func_module.FuncModule): # example killall("thunderbird","-9") rc = sub_process.call(["/usr/bin/pkill", name, level], executable="/usr/bin/pkill", shell=False) return rc - -methods = ProcessModule() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/reboot.py b/func/minion/modules/reboot.py index 8772b8f..0af78f9 100644 --- a/func/minion/modules/reboot.py +++ b/func/minion/modules/reboot.py @@ -8,22 +8,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -from modules import func_module - +import func_module import sub_process class Reboot(func_module.FuncModule): - def __init__(self): - self.methods = { - "reboot" : self.reboot - } - func_module.FuncModule.__init__(self) - def reboot(self, when='now', message=''): return sub_process.call(["/sbin/shutdown", '-r', when, message]) - - -methods = Reboot() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/rpms.py b/func/minion/modules/rpms.py index 901a9d6..f73e9db 100644 --- a/func/minion/modules/rpms.py +++ b/func/minion/modules/rpms.py @@ -8,18 +8,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -from modules import func_module +import func_module import rpm class RpmModule(func_module.FuncModule): - def __init__(self): - self.methods = { - "inventory" : self.inventory, - "info" : self.inventory - } - func_module.FuncModule.__init__(self) - def inventory(self, flatten=True): """ Returns information on all installed packages. @@ -43,6 +36,3 @@ class RpmModule(func_module.FuncModule): else: results.append([name,epoch,version,release,arch]) return results - -methods = RpmModule() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/service.py b/func/minion/modules/service.py index d088907..d1170a8 100644 --- a/func/minion/modules/service.py +++ b/func/minion/modules/service.py @@ -13,26 +13,13 @@ ## import codes -from modules import func_module +import func_module import sub_process import os class Service(func_module.FuncModule): - def __init__(self): - self.methods = { - "start" : self.start, - "stop" : self.stop, - "restart" : self.restart, - "reload" : self.reload, - "status" : self.status, - "get_enabled" : self.get_enabled, - "get_running" : self.get_running, - "inventory" : self.inventory, - } - func_module.FuncModule.__init__(self) - def __command(self, service_name, command): filename = os.path.join("/etc/rc.d/init.d/",service_name) @@ -95,6 +82,3 @@ class Service(func_module.FuncModule): tokens = line.split() results.append((tokens[0], tokens[-1].replace("...",""))) return results - -methods = Service() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/smart.py b/func/minion/modules/smart.py index e85b90c..ff7afd9 100644 --- a/func/minion/modules/smart.py +++ b/func/minion/modules/smart.py @@ -17,17 +17,11 @@ import sub_process # our modules -from modules import func_module +import func_module # ================================= class SmartModule(func_module.FuncModule): - def __init__(self): - self.methods = { - "info" : self.info, - "inventory" : self.info, # for func-inventory - } - func_module.FuncModule.__init__(self) def info(self,flags="-q onecheck"): """ @@ -47,6 +41,3 @@ class SmartModule(func_module.FuncModule): results.append(x) return (cmd.returncode, results) - -methods = SmartModule() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/snmp.py b/func/minion/modules/snmp.py index 3144144..374821c 100644 --- a/func/minion/modules/snmp.py +++ b/func/minion/modules/snmp.py @@ -13,19 +13,12 @@ Abitrary command execution module for func. """ -from modules import func_module - +import func_module import sub_process base_snmp_command = '/usr/bin/snmpget -v2c -Ov -OQ' class Snmp(func_module.FuncModule): - def __init__(self): - self.methods = { - "get" : self.get - } - func_module.FuncModule.__init__(self) - def get(self, oid, rocommunity, hostname='localhost'): """ Runs an snmpget on a specific oid returns the output of the call. @@ -39,8 +32,3 @@ class Snmp(func_module.FuncModule): #def walk(self, oid, rocommunity): #def table(self, oid, rocommunity): - -methods = Snmp() -register_rpc = methods.register_rpc - - diff --git a/func/minion/modules/test.py b/func/minion/modules/test.py index 24af03e..6718fed 100644 --- a/func/minion/modules/test.py +++ b/func/minion/modules/test.py @@ -1,17 +1,10 @@ -from modules import func_module +import func_module import time class Test(func_module.FuncModule): version = "11.11.11" api_version = "0.0.1" description = "Just a very simple example module" - def __init__(self): - self.methods = { - "add": self.add, - "ping": self.ping, - "sleep": self.sleep - } - func_module.FuncModule.__init__(self) def add(self, numb1, numb2): return numb1 + numb2 @@ -27,6 +20,3 @@ class Test(func_module.FuncModule): t = int(t) time.sleep(t) return time.time() - -methods = Test() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/virt.py b/func/minion/modules/virt.py index ba888ec..3671172 100644 --- a/func/minion/modules/virt.py +++ b/func/minion/modules/virt.py @@ -119,31 +119,8 @@ class FuncLibvirtConnection(object): class Virt(func_module.FuncModule): - - def __init__(self): - - """ - Constructor. Register methods and make them available. - """ - - self.methods = { - "install" : self.install, - "shutdown" : self.shutdown, - "destroy" : self.destroy, - "start" : self.create, - "pause" : self.pause, - "unpause" : self.unpause, - "delete" : self.undefine, - "status" : self.get_status, - "info" : self.info, - "inventory" : self.info, # for func-inventory - "list_vms" : self.list_vms, - } - - func_module.FuncModule.__init__(self) - - def get_conn(self): - self.conn = FuncLibvirtConnection() + def __get_conn(self): + self.conn = FuncLibvirtConnection() return self.conn def state(self): @@ -176,7 +153,7 @@ class Virt(func_module.FuncModule): def list_vms(self): - self.conn = self.get_conn() + self.conn = self.__get_conn() vms = self.conn.find_vm(-1) results = [] for x in vms: @@ -195,7 +172,7 @@ class Virt(func_module.FuncModule): # Example: # install("bootserver.example.org", "fc7webserver", True) - conn = self.get_conn() + conn = self.__get_conn() if conn is None: raise codes.FuncException("no connection") @@ -227,7 +204,7 @@ class Virt(func_module.FuncModule): Make the machine with the given vmid stop running. Whatever that takes. """ - self.get_conn() + self.__get_conn() self.conn.shutdown(vmid) return 0 @@ -237,7 +214,7 @@ class Virt(func_module.FuncModule): """ Pause the machine with the given vmid. """ - self.get_conn() + self.__get_conn() self.conn.suspend(vmid) return 0 @@ -248,7 +225,7 @@ class Virt(func_module.FuncModule): Unpause the machine with the given vmid. """ - self.get_conn() + self.__get_conn() self.conn.resume(vmid) return 0 @@ -258,7 +235,7 @@ class Virt(func_module.FuncModule): """ Start the machine via the given mac address. """ - self.get_conn() + self.__get_conn() self.conn.create(vmid) return 0 @@ -269,7 +246,7 @@ class Virt(func_module.FuncModule): Pull the virtual power from the virtual domain, giving it virtually no time to virtually shut down. """ - self.get_conn() + self.__get_conn() self.conn.destroy(vmid) return 0 @@ -281,7 +258,7 @@ class Virt(func_module.FuncModule): by deleting the disk image and it's configuration file. """ - self.get_conn() + self.__get_conn() self.conn.undefine(vmid) return 0 @@ -292,9 +269,5 @@ class Virt(func_module.FuncModule): Return a state suitable for server consumption. Aka, codes.py values, not XM output. """ - self.get_conn() + self.__get_conn() return self.conn.get_status(vmid) - - -methods = Virt() -register_rpc = methods.register_rpc diff --git a/func/minion/modules/yumcmd.py b/func/minion/modules/yumcmd.py index b3976fb..8bf760c 100644 --- a/func/minion/modules/yumcmd.py +++ b/func/minion/modules/yumcmd.py @@ -8,8 +8,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -from modules import func_module +import func_module import yum from yum import repos @@ -22,13 +21,6 @@ class DummyCallback(object): class Yum(func_module.FuncModule): - def __init__(self): - self.methods = { - "update" : self.update, - "check_update" : self.check_update - } - func_module.FuncModule.__init__(self) - def update(self): # XXX support updating specific rpms ayum = yum.YumBase() @@ -52,7 +44,3 @@ class Yum(func_module.FuncModule): ayum.doTsSetup() ayum.repos.enableRepo(repo) return map(str, ayum.doPackageLists('updates').updates) - - -methods = Yum() -register_rpc = methods.register_rpc diff --git a/scripts/func-create-module b/scripts/func-create-module index afb4d09..7cbf9eb 100755 --- a/scripts/func-create-module +++ b/scripts/func-create-module @@ -10,9 +10,7 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. TEMPLATE = """\ -from modules import func_module -# Add your imports here -import sub_process +import func_module class %s(func_module.FuncModule): @@ -21,17 +19,7 @@ class %s(func_module.FuncModule): api_version = "0.0.1" description = "%s" - def __init__(self): - self.methods = { %s - } - func_module.FuncModule.__init__(self) - -%s - - -methods = %s() -register_rpc = methods.register_rpc """ @@ -40,12 +28,9 @@ def populate_template(module_name, desc, methods): Makes the method strings and populates the template. """ actual_methods = "" - method_str_dict = "" for method in methods: - method_str_dict += ' "%s": self.%s,\n' % (method, method) - actual_methods += " def self.%s(self):\n pass\n\n" % method - return TEMPLATE % (module_name, desc, - method_str_dict[:-1], actual_methods[:-2], module_name) + actual_methods += " def %s(self):\n pass\n\n" % method + return TEMPLATE % (module_name, desc, actual_methods[:-2]) if __name__ == '__main__': @@ -63,4 +48,4 @@ if __name__ == '__main__': file_obj = open(file_name, "w") file_obj.write(populate_template(module_name, desc, methods)) file_obj.close() - print "Your module is ready to be hacked on. Wrote out to %s." % file_name \ No newline at end of file + print "Your module is ready to be hacked on. Wrote out to %s." % file_name -- 1.5.3.3 _______________________________________________ Func-list mailing list Func-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/func-list