summaryrefslogtreecommitdiffstats
path: root/func/minion/modules
diff options
context:
space:
mode:
Diffstat (limited to 'func/minion/modules')
-rwxr-xr-xfunc/minion/modules/Makefile18
-rw-r--r--func/minion/modules/__init__.py0
-rw-r--r--func/minion/modules/certmaster.py65
-rw-r--r--func/minion/modules/command.py44
-rw-r--r--func/minion/modules/copyfile.py109
-rw-r--r--func/minion/modules/filetracker.py192
-rw-r--r--func/minion/modules/func_module.py76
-rw-r--r--func/minion/modules/func_module.py.orig65
-rw-r--r--func/minion/modules/hardware.py130
-rw-r--r--func/minion/modules/jobs.py36
-rw-r--r--func/minion/modules/mount.py84
-rw-r--r--func/minion/modules/nagios-check.py34
-rw-r--r--func/minion/modules/netapp/README8
-rw-r--r--func/minion/modules/netapp/TODO5
-rw-r--r--func/minion/modules/netapp/__init__.py0
-rw-r--r--func/minion/modules/netapp/common.py49
-rw-r--r--func/minion/modules/netapp/snap.py49
-rw-r--r--func/minion/modules/netapp/vol/__init__.py128
-rw-r--r--func/minion/modules/netapp/vol/clone.py46
-rw-r--r--func/minion/modules/networktest.py64
-rw-r--r--func/minion/modules/process.py216
-rw-r--r--func/minion/modules/process.py.orig221
-rw-r--r--func/minion/modules/reboot.py21
-rw-r--r--func/minion/modules/rpms.py44
-rw-r--r--func/minion/modules/service.py88
-rw-r--r--func/minion/modules/smart.py47
-rw-r--r--func/minion/modules/snmp.py38
-rw-r--r--func/minion/modules/sysctl.py31
-rw-r--r--func/minion/modules/test.py29
-rw-r--r--func/minion/modules/virt.py277
-rw-r--r--func/minion/modules/yumcmd.py50
31 files changed, 2264 insertions, 0 deletions
diff --git a/func/minion/modules/Makefile b/func/minion/modules/Makefile
new file mode 100755
index 0000000..f2bc6c4
--- /dev/null
+++ b/func/minion/modules/Makefile
@@ -0,0 +1,18 @@
+
+
+PYFILES = $(wildcard *.py)
+
+PYCHECKER = /usr/bin/pychecker
+PYFLAKES = /usr/bin/pyflakes
+
+clean::
+ @rm -fv *.pyc *~ .*~ *.pyo
+ @find . -name .\#\* -exec rm -fv {} \;
+ @rm -fv *.rpm
+
+
+pychecker::
+ @$(PYCHECKER) $(PYFILES) || exit 0
+
+pyflakes::
+ @$(PYFLAKES) $(PYFILES) || exit 0
diff --git a/func/minion/modules/__init__.py b/func/minion/modules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/func/minion/modules/__init__.py
diff --git a/func/minion/modules/certmaster.py b/func/minion/modules/certmaster.py
new file mode 100644
index 0000000..9ca484f
--- /dev/null
+++ b/func/minion/modules/certmaster.py
@@ -0,0 +1,65 @@
+## -*- coding: utf-8 -*-
+##
+## Process lister (control TBA)
+##
+## Copyright 2008, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+# other modules
+import sub_process
+import codes
+
+# our modules
+import func_module
+from func import certmaster as certmaster
+
+# =================================
+
+class CertMasterModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Administers certs on an overlord."
+
+ def get_hosts_to_sign(self, list_of_hosts):
+ """
+ ...
+ """
+ list_of_hosts = self.__listify(list_of_hosts)
+ cm = certmaster.CertMaster()
+ return cm.get_csrs_waiting()
+
+ def sign_hosts(self, list_of_hosts):
+ """
+ ...
+ """
+ list_of_hosts = self.__listify(list_of_hosts)
+ cm = certmaster.CertMaster()
+ for x in list_of_hosts:
+ cm.sign_this_csr(x)
+ return True
+
+ def cleanup_hosts(self, list_of_hosts):
+ """
+ ...
+ """
+ list_of_hosts = self.__listify(list_of_hosts)
+ cm = certmaster.CertMaster()
+ for x in list_of_hosts:
+ cm.remove_this_cert(x)
+ return True
+
+ def __listify(self, list_of_hosts):
+ if type(list_of_hosts) is type([]):
+ return list_of_hosts
+ else:
+ return [ list_of_hosts ]
+
diff --git a/func/minion/modules/command.py b/func/minion/modules/command.py
new file mode 100644
index 0000000..cc463cf
--- /dev/null
+++ b/func/minion/modules/command.py
@@ -0,0 +1,44 @@
+# Copyright 2007, Red Hat, Inc
+# James Bowes <jbowes@redhat.com>
+# Steve 'Ashcrow' Milner <smilner@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+"""
+Abitrary command execution module for func.
+"""
+
+import func_module
+import sub_process
+
+class Command(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Works with shell commands."
+
+ def run(self, command):
+ """
+ Runs a command, returning the return code, stdout, and stderr as a tuple.
+ NOT FOR USE WITH INTERACTIVE COMMANDS.
+ """
+
+ 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])
+
+ def exists(self, command):
+ """
+ Checks to see if a command exists on the target system(s).
+ """
+ import os
+
+ if os.access(command, os.X_OK):
+ return True
+ return False
diff --git a/func/minion/modules/copyfile.py b/func/minion/modules/copyfile.py
new file mode 100644
index 0000000..150af88
--- /dev/null
+++ b/func/minion/modules/copyfile.py
@@ -0,0 +1,109 @@
+# Copyright 2007, Red Hat, Inc
+# seth vidal
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+
+import sha
+import os
+import time
+import shutil
+
+import func_module
+
+
+class CopyFile(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.2"
+ description = "Allows for smart copying of a file."
+
+ def _checksum_blob(self, blob):
+ thissum = sha.new()
+ thissum.update(blob)
+ return thissum.hexdigest()
+
+ def checksum(self, thing):
+
+ CHUNK=2**16
+ thissum = sha.new()
+ if os.path.exists(thing):
+ fo = open(thing, 'r', CHUNK)
+ chunk = fo.read
+ while chunk:
+ chunk = fo.read(CHUNK)
+ thissum.update(chunk)
+ fo.close()
+ del fo
+ else:
+ # assuming it's a string of some kind
+ thissum.update(thing)
+
+ return thissum.hexdigest()
+
+
+ def copyfile(self, filepath, filebuf, mode=0644, uid=0, gid=0, force=None):
+ # -1 = problem file was not copied
+ # 1 = file was copied
+ # 0 = file was not copied b/c file is unchanged
+
+
+ # we should probably verify mode,uid,gid are valid as well
+
+ dirpath = os.path.dirname(filepath)
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+
+ remote_sum = self._checksum_blob(filebuf.data)
+ local_sum = 0
+ if os.path.exists(filepath):
+ local_sum = self.checksum(filepath)
+
+ if remote_sum != local_sum or force is not None:
+ # back up the localone
+ if os.path.exists(filepath):
+ if not self._backuplocal(filepath):
+ return -1
+
+ # do the new write
+ try:
+ fo = open(filepath, 'w')
+ fo.write(filebuf.data)
+ fo.close()
+ del fo
+ except (IOError, OSError), e:
+ # XXX logger output here
+ return -1
+ else:
+ return 0
+
+ # hmm, need to figure out proper exceptions -akl
+ try:
+ # we could intify the mode here if it's a string
+ os.chmod(filepath, mode)
+ os.chown(filepath, uid, gid)
+ except (IOError, OSError), e:
+ return -1
+
+ return 1
+
+ def _backuplocal(self, fn):
+ """
+ make a date-marked backup of the specified file,
+ return True or False on success or failure
+ """
+ # backups named basename-YYYY-MM-DD@HH:MM~
+ ext = time.strftime("%Y-%m-%d@%H:%M~", time.localtime(time.time()))
+ backupdest = '%s.%s' % (fn, ext)
+
+ try:
+ shutil.copy2(fn, backupdest)
+ except shutil.Error, e:
+ #XXX logger output here
+ return False
+ return True
diff --git a/func/minion/modules/filetracker.py b/func/minion/modules/filetracker.py
new file mode 100644
index 0000000..f5f9dbb
--- /dev/null
+++ b/func/minion/modules/filetracker.py
@@ -0,0 +1,192 @@
+## func
+##
+## filetracker
+## maintains a manifest of files of which to keep track
+## provides file meta-data (and optionally full data) to func-inventory
+##
+## (C) Vito Laurenza <vitolaurenza@gmail.com>
+## + Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+# func modules
+import func_module
+
+# other modules
+from stat import *
+import glob
+import os
+import md5
+
+# defaults
+CONFIG_FILE='/etc/func/modules/filetracker.conf'
+
+class FileTracker(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Maintains a manifest of files to keep track of."
+
+ def __load(self):
+ """
+ Parse file and return data structure.
+ """
+
+ filehash = {}
+ if os.path.exists(CONFIG_FILE):
+ config = open(CONFIG_FILE, "r")
+ data = config.read()
+ lines = data.split("\n")
+ for line in lines:
+ tokens = line.split(None)
+ if len(tokens) < 2:
+ continue
+ scan_mode = tokens[0]
+ path = " ".join(tokens[1:])
+ if str(scan_mode).lower() == "0":
+ scan_mode = 0
+ else:
+ scan_mode = 1
+ filehash[path] = scan_mode
+ return filehash
+
+ #==========================================================
+
+ def __save(self, filehash):
+ """
+ Write data structure to file.
+ """
+
+ config = open(CONFIG_FILE, "w+")
+ for (path, scan_mode) in filehash.iteritems():
+ config.write("%s %s\n" % (scan_mode, path))
+ config.close()
+
+ #==========================================================
+
+ def track(self, file_name, full_scan=0):
+ """
+ Adds files to keep track of.
+ full_scan implies tracking the full contents of the file, defaults to off
+ """
+
+ filehash = self.__load()
+ filehash[file_name] = full_scan
+ self.__save(filehash)
+ return 1
+
+ #==========================================================
+
+ def untrack(self, file_name):
+ """
+ Stop keeping track of a file.
+ This routine is tolerant of most errors since we're forgetting about the file anyway.
+ """
+
+ filehash = self.__load()
+ if file_name in filehash.keys():
+ del filehash[file_name]
+ self.__save(filehash)
+ return 1
+
+ #==========================================================
+
+ def inventory(self, flatten=1, checksum_enabled=1):
+ """
+ Returns information on all tracked files
+ By default, 'flatten' is passed in as True, which makes printouts very clean in diffs
+ for use by func-inventory. If you are writting another software application, using flatten=False will
+ prevent the need to parse the returns.
+ """
+
+ # XMLRPC feeds us strings from the CLI when it shouldn't
+ flatten = int(flatten)
+ checksum_enabled = int(checksum_enabled)
+
+ filehash = self.__load()
+
+ # we'll either return a very flat string (for clean diffs)
+ # or a data structure
+ if flatten:
+ results = ""
+ else:
+ results = []
+
+ for (file_name, scan_type) in filehash.iteritems():
+
+ if not os.path.exists(file_name):
+ if flatten:
+ results = results + "%s: does not exist\n" % file_name
+ else:
+ results.append("%s: does not exist\n" % file_name)
+ continue
+
+ this_result = []
+
+ # ----- always process metadata
+ filestat = os.stat(file_name)
+ mode = filestat[ST_MODE]
+ mtime = filestat[ST_MTIME]
+ uid = filestat[ST_UID]
+ gid = filestat[ST_GID]
+ if not os.path.isdir(file_name) and checksum_enabled:
+ sum_handle = open(file_name)
+ hash = self.__sumfile(sum_handle)
+ sum_handle.close()
+ else:
+ hash = "N/A"
+
+ # ------ what we return depends on flatten
+ if flatten:
+ this_result = "%s: mode=%s mtime=%s uid=%s gid=%s md5sum=%s\n" % (file_name,mode,mtime,uid,gid,hash)
+ else:
+ this_result = [file_name,mode,mtime,uid,gid,hash]
+
+ # ------ add on file data only if requested
+ if scan_type != 0 and os.path.isfile(file_name):
+ tracked_file = open(file_name)
+ data = tracked_file.read()
+ if flatten:
+ this_result = this_result + "*** DATA ***\n" + data + "\n*** END DATA ***\n\n"
+ else:
+ this_result.append(data)
+ tracked_file.close()
+
+ if os.path.isdir(file_name):
+ if not file_name.endswith("/"):
+ file_name = file_name + "/"
+ files = glob.glob(file_name + "*")
+ if flatten:
+ this_result = this_result + "*** FILES ***\n" + "\n".join(files) + "\n*** END FILES ***\n\n"
+ else:
+ this_result.append({"files" : files})
+
+ if flatten:
+ results = results + "\n" + this_result
+ else:
+ results.append(this_result)
+
+
+ return results
+
+ #==========================================================
+
+ def __sumfile(self, fobj):
+ """
+ Returns an md5 hash for an object with read() method.
+ credit: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266486
+ """
+
+ m = md5.new()
+ while True:
+ d = fobj.read(8096)
+ if not d:
+ break
+ m.update(d)
+ return m.hexdigest()
diff --git a/func/minion/modules/func_module.py b/func/minion/modules/func_module.py
new file mode 100644
index 0000000..7d476dc
--- /dev/null
+++ b/func/minion/modules/func_module.py
@@ -0,0 +1,76 @@
+##
+## Copyright 2007, Red Hat, Inc
+## see AUTHORS
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import inspect
+
+from func import logger
+from func.config import read_config
+from func.commonconfig import FuncdConfig
+
+
+class FuncModule(object):
+
+ # the version is meant to
+ version = "0.0.0"
+ api_version = "0.0.0"
+ description = "No Description provided"
+
+ def __init__(self):
+
+ config_file = '/etc/func/minion.conf'
+ self.config = read_config(config_file, FuncdConfig)
+ self.__init_log()
+ self.__base_methods = {
+ # __'s so we don't clobber useful names
+ "module_version" : self.__module_version,
+ "module_api_version" : self.__module_api_version,
+ "module_description" : self.__module_description,
+ "list_methods" : self.__list_methods
+ }
+
+ def __init_log(self):
+ log = logger.Logger()
+ self.logger = log.logger
+
+ def register_rpc(self, handlers, module_name):
+ # add the internal methods, note that this means they
+ # can get clobbbered by subclass versions
+ for meth in self.__base_methods:
+ handlers["%s.%s" % (module_name, meth)] = self.__base_methods[meth]
+
+ # register our module's handlers
+ for name, handler in self.__list_handlers().items():
+ handlers["%s.%s" % (module_name, name)] = handler
+
+ def __list_handlers(self):
+ """ Return a dict of { handler_name, method, ... }.
+ All methods that do not being with an underscore will be exposed.
+ We also make sure to not expose our register_rpc method.
+ """
+ handlers = {}
+ for attr in dir(self):
+ if inspect.ismethod(getattr(self, attr)) and attr[0] != '_' and \
+ attr != 'register_rpc':
+ handlers[attr] = getattr(self, attr)
+ return handlers
+
+ def __list_methods(self):
+ return self.__list_handlers().keys() + self.__base_methods.keys()
+
+ def __module_version(self):
+ return self.version
+
+ def __module_api_version(self):
+ return self.api_version
+
+ def __module_description(self):
+ return self.description
diff --git a/func/minion/modules/func_module.py.orig b/func/minion/modules/func_module.py.orig
new file mode 100644
index 0000000..c911b91
--- /dev/null
+++ b/func/minion/modules/func_module.py.orig
@@ -0,0 +1,65 @@
+##
+## Copyright 2007, Red Hat, Inc
+## see AUTHORS
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import inspect
+
+from func import logger
+from func.config import read_config
+from func.commonconfig import FuncdConfig
+
+
+class FuncModule(object):
+
+ # the version is meant to
+ version = "0.0.0"
+ api_version = "0.0.0"
+ description = "No Description provided"
+
+ def __init__(self):
+
+ config_file = '/etc/func/minion.conf'
+ self.config = read_config(config_file, FuncdConfig)
+ self.__init_log()
+ self.__base_methods = {
+ # __'s so we don't clobber useful names
+ "module_version" : self.__module_version,
+ "module_api_version" : self.__module_api_version,
+ "module_description" : self.__module_description,
+ "list_methods" : self.__list_methods
+ }
+
+ def __init_log(self):
+ log = logger.Logger()
+ self.logger = log.logger
+
+ def register_rpc(self, handlers, module_name):
+ # add the internal methods, note that this means they
+ # can get clobbbered by subclass versions
+ for meth in self.__base_methods:
+ handlers["%s.%s" % (module_name, meth)] = self.__base_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()
+
+ def __module_version(self):
+ return self.version
+
+ def __module_api_version(self):
+ return self.api_version
+
+ def __module_description(self):
+ return self.description
diff --git a/func/minion/modules/hardware.py b/func/minion/modules/hardware.py
new file mode 100644
index 0000000..46b1821
--- /dev/null
+++ b/func/minion/modules/hardware.py
@@ -0,0 +1,130 @@
+##
+## Hardware profiler plugin
+## requires the "smolt" client package be installed
+## but also relies on lspci for some things
+##
+## Copyright 2007, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+
+# other modules
+import sys
+
+# our modules
+import sub_process
+import func_module
+
+# =================================
+
+class HardwareModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Hardware profiler."
+
+ def hal_info(self):
+ """
+ Returns the output of lshal, but split up into seperate devices
+ for easier parsing. Each device is a entry in the return hash.
+ """
+
+ cmd = sub_process.Popen(["/usr/bin/lshal"],shell=False,stdout=sub_process.PIPE)
+ data = cmd.communicate()[0]
+
+ data = data.split("\n")
+
+ results = {}
+ current = ""
+ label = data[0]
+ for d in data:
+ if d == '':
+ results[label] = current
+ current = ""
+ label = ""
+ else:
+ if label == "":
+ label = d
+ current = current + d
+
+ return results
+
+ def inventory(self):
+ data = hw_info(with_devices=True)
+ # remove bogomips because it keeps changing for laptops
+ # and makes inventory tracking noisy
+ if data.has_key("bogomips"):
+ del data["bogomips"]
+ return data
+
+ def info(self,with_devices=True):
+ """
+ Returns a struct of hardware information. By default, this pulls down
+ all of the devices. If you don't care about them, set with_devices to
+ False.
+ """
+ return hw_info(with_devices)
+
+# =================================
+
+def hw_info(with_devices=True):
+
+ # this may fail if smolt is not installed. That's ok. hal_info will
+ # still work.
+
+ # hack: smolt is not installed in site-packages
+ sys.path.append("/usr/share/smolt/client")
+ import smolt
+
+ hardware = smolt.Hardware()
+ host = hardware.host
+
+ # NOTE: casting is needed because these are DBusStrings, not real strings
+ data = {
+ 'os' : str(host.os),
+ 'defaultRunlevel' : str(host.defaultRunlevel),
+ 'bogomips' : str(host.bogomips),
+ 'cpuVendor' : str(host.cpuVendor),
+ 'cpuModel' : str(host.cpuModel),
+ 'numCpus' : str(host.numCpus),
+ 'cpuSpeed' : str(host.cpuSpeed),
+ 'systemMemory' : str(host.systemMemory),
+ 'systemSwap' : str(host.systemSwap),
+ 'kernelVersion' : str(host.kernelVersion),
+ 'language' : str(host.language),
+ 'platform' : str(host.platform),
+ 'systemVendor' : str(host.systemVendor),
+ 'systemModel' : str(host.systemModel),
+ 'formfactor' : str(host.formfactor),
+ 'selinux_enabled' : str(host.selinux_enabled),
+ 'selinux_enforce' : str(host.selinux_enforce)
+ }
+
+ # if no hardware info requested, just return the above bits
+ if not with_devices:
+ return data
+
+ collection = data["devices"] = []
+
+ for item in hardware.deviceIter():
+
+ (VendorID,DeviceID,SubsysVendorID,SubsysDeviceID,Bus,Driver,Type,Description) = item
+
+ collection.append({
+ "VendorID" : str(VendorID),
+ "DeviceID" : str(DeviceID),
+ "SubsysVendorID" : str(SubsysVendorID),
+ "Bus" : str(Bus),
+ "Driver" : str(Driver),
+ "Type" : str(Type),
+ "Description" : str(Description)
+ })
+
+ return data
diff --git a/func/minion/modules/jobs.py b/func/minion/modules/jobs.py
new file mode 100644
index 0000000..69fb75f
--- /dev/null
+++ b/func/minion/modules/jobs.py
@@ -0,0 +1,36 @@
+## (Largely internal) module for access to asynchoronously dispatched
+## module job ID's. The Func Client() module wraps most of this usage
+## so it's not entirely relevant to folks using the CLI or Func API
+## directly.
+##
+## Copyright 2008, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import codes
+from func import jobthing
+import func_module
+
+# =================================
+
+class JobsModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Internal module for tracking background minion tasks."
+
+ def job_status(self, job_id):
+ """
+ Returns job status in the form of (status, datastruct).
+ Datastruct is undefined for unfinished jobs. See jobthing.py and
+ Wiki details on async invocation for more information.
+ """
+ return jobthing.job_status(job_id)
+
diff --git a/func/minion/modules/mount.py b/func/minion/modules/mount.py
new file mode 100644
index 0000000..0db914f
--- /dev/null
+++ b/func/minion/modules/mount.py
@@ -0,0 +1,84 @@
+##
+## Mount manager
+##
+## Copyright 2007, Red Hat, Inc
+## John Eckersberg <jeckersb@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import sub_process, os
+import func_module
+
+
+class MountModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Mounting, unmounting and getting information on mounted filesystems."
+
+ def list(self):
+ cmd = sub_process.Popen(["/bin/cat", "/proc/mounts"], executable="/bin/cat", stdout=sub_process.PIPE, shell=False)
+ data = cmd.communicate()[0]
+
+ mounts = []
+ lines = [l for l in data.split("\n") if l] #why must you append blank crap?
+
+ for line in lines:
+ curmount = {}
+ tokens = line.split()
+ curmount['device'] = tokens[0]
+ curmount['dir'] = tokens[1]
+ curmount['type'] = tokens[2]
+ curmount['options'] = tokens[3]
+ mounts.append(curmount)
+
+ return mounts
+
+ def mount(self, device, dir, type="auto", options=None, createdir=False):
+ cmdline = ["/bin/mount", "-t", type]
+ if options:
+ cmdline.append("-o")
+ cmdline.append(options)
+ cmdline.append(device)
+ cmdline.append(dir)
+ if createdir:
+ try:
+ os.makedirs(dir)
+ except:
+ return False
+ cmd = sub_process.Popen(cmdline, executable="/bin/mount", stdout=sub_process.PIPE, shell=False)
+ if cmd.wait() == 0:
+ return True
+ else:
+ return False
+
+ def umount(self, dir, killall=False, force=False, lazy=False):
+ # succeed if its not mounted
+ if not os.path.ismount(dir):
+ return True
+
+ if killall:
+ cmd = sub_process.Popen(["/sbin/fuser", "-mk", dir], executable="/sbin/fuser", stdout=sub_process.PIPE, shell=False)
+ cmd.wait()
+
+ cmdline = ["/bin/umount"]
+ if force:
+ cmdline.append("-f")
+ if lazy:
+ cmdline.append("-l")
+ cmdline.append(dir)
+
+ cmd = sub_process.Popen(cmdline, executable="/bin/umount", stdout=sub_process.PIPE, shell=False)
+ if cmd.wait() == 0:
+ return True
+ else:
+ return False
+
+ def inventory(self, flatten=True):
+ return self.list()
diff --git a/func/minion/modules/nagios-check.py b/func/minion/modules/nagios-check.py
new file mode 100644
index 0000000..f1c0714
--- /dev/null
+++ b/func/minion/modules/nagios-check.py
@@ -0,0 +1,34 @@
+# Copyright 2007, Red Hat, Inc
+# James Bowes <jbowes@redhat.com>
+# Seth Vidal modified command.py to be nagios-check.py
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+"""
+Abitrary command execution module for func.
+"""
+
+import func_module
+import sub_process
+
+class Nagios(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Runs nagios checks."
+
+ def run(self, check_command):
+ """
+ Runs a nagios check returning the return code, stdout, and stderr as a tuple.
+ """
+ nagios_path='/usr/lib/nagios/plugins'
+ command = '%s/%s' % (nagios_path, check_command)
+
+ 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])
diff --git a/func/minion/modules/netapp/README b/func/minion/modules/netapp/README
new file mode 100644
index 0000000..5ecb205
--- /dev/null
+++ b/func/minion/modules/netapp/README
@@ -0,0 +1,8 @@
+This module is meant to be installed on a minion which is configured
+as an admin host for one or more NetApp filers. Since we can't get
+our funcy awesomeness on the actual filer the admin host will have to do.
+
+Requirements:
+
+- passphraseless ssh key access from root on the netapp admin minion
+ to root on the target filer
diff --git a/func/minion/modules/netapp/TODO b/func/minion/modules/netapp/TODO
new file mode 100644
index 0000000..25d914c
--- /dev/null
+++ b/func/minion/modules/netapp/TODO
@@ -0,0 +1,5 @@
+Wrap every possible NetApp command :)
+
+I'm only going to do the ones that are important to me. If you have
+some that are important to you, feel free to submit patches to
+func-list@redhat.com and harness the power of open source!
diff --git a/func/minion/modules/netapp/__init__.py b/func/minion/modules/netapp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/func/minion/modules/netapp/__init__.py
diff --git a/func/minion/modules/netapp/common.py b/func/minion/modules/netapp/common.py
new file mode 100644
index 0000000..979c95c
--- /dev/null
+++ b/func/minion/modules/netapp/common.py
@@ -0,0 +1,49 @@
+##
+## NetApp Filer 'common' Module
+##
+## Copyright 2008, Red Hat, Inc
+## John Eckersberg <jeckersb@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import re
+import sub_process
+
+SSH = '/usr/bin/ssh'
+SSH_USER = 'root'
+SSH_OPTS = '-o forwardagent=no'
+class GenericSSHError(Exception): pass
+class NetappCommandError(Exception): pass
+
+def ssh(host, cmdargs, input=None, user=SSH_USER):
+ cmdline = [SSH, SSH_OPTS, "%s@%s" % (user, host)]
+ cmdline.extend(cmdargs)
+
+ cmd = sub_process.Popen(cmdline,
+ executable=SSH,
+ stdin=sub_process.PIPE,
+ stdout=sub_process.PIPE,
+ stderr=sub_process.PIPE,
+ shell=False)
+
+ (out, err) = cmd.communicate(input)
+
+ if cmd.wait() != 0:
+ raise GenericSSHError, err
+ else:
+ return out + err
+
+def check_output(regex, output):
+ #strip newlines
+ output = output.replace('\n', ' ')
+ if re.search(regex, output):
+ return True
+ else:
+ raise NetappCommandError, output
+
diff --git a/func/minion/modules/netapp/snap.py b/func/minion/modules/netapp/snap.py
new file mode 100644
index 0000000..8f3f209
--- /dev/null
+++ b/func/minion/modules/netapp/snap.py
@@ -0,0 +1,49 @@
+##
+## NetApp Filer 'snap' Module
+##
+## Copyright 2008, Red Hat, Inc
+## John Eckersberg <jeckersb@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import re
+from func.minion.modules import func_module
+from func.minion.modules.netapp.common import *
+
+class Snap(func_module.FuncModule):
+
+ # Update these if need be.
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Interface to the 'snap' command"
+
+ def create(self, filer, vol, snap):
+ """
+ TODO: Document me ...
+ """
+ regex = """creating snapshot..."""
+ cmd_opts = ['snap', 'create', vol, snap]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def delete(self, filer, vol, snap):
+ """
+ TODO: Document me ...
+ """
+ regex = """deleting snapshot..."""
+ cmd_opts = ['snap', 'delete', vol, snap]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def list(self, filer, vol):
+ """
+ TODO: Document me ...
+ """
+ return True
+
diff --git a/func/minion/modules/netapp/vol/__init__.py b/func/minion/modules/netapp/vol/__init__.py
new file mode 100644
index 0000000..14ce0ac
--- /dev/null
+++ b/func/minion/modules/netapp/vol/__init__.py
@@ -0,0 +1,128 @@
+##
+## NetApp Filer 'Vol' Module
+##
+## Copyright 2008, Red Hat, Inc
+## John Eckersberg <jeckersb@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import re
+from func.minion.modules import func_module
+from func.minion.modules.netapp.common import *
+
+class Vol(func_module.FuncModule):
+
+ # Update these if need be.
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Interface to the 'vol' command"
+
+ def create(self, filer, vol, aggr, size):
+ """
+ TODO: Document me ...
+ """
+ regex = """Creation of volume .* has completed."""
+ cmd_opts = ['vol', 'create', vol, aggr, size]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def destroy(self, filer, vol):
+ """
+ TODO: Document me ...
+ """
+ regex = """Volume .* destroyed."""
+ cmd_opts = ['vol', 'destroy', vol, '-f']
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def offline(self, filer, vol):
+ """
+ TODO: Document me ...
+ """
+ regex = """Volume .* is now offline."""
+ cmd_opts = ['vol', 'offline', vol]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def online(self, filer, vol):
+ """
+ TODO: Document me ...
+ """
+ regex = """Volume .* is now online."""
+ cmd_opts = ['vol', 'online', vol]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def status(self, filer, vol=None):
+ """
+ TODO: Document me ...
+ """
+ cmd_opts = ['vol', 'status']
+ output = ssh(filer, cmd_opts)
+
+ output = output.replace(',', ' ')
+ lines = output.split('\n')[1:]
+
+ vols = []
+ current_vol = {}
+ for line in lines:
+ tokens = line.split()
+ if len(tokens) >= 2 and tokens[1] in ('online', 'offline', 'restricted'):
+ if current_vol: vols.append(current_vol)
+ current_vol = {'name': tokens[0],
+ 'state': tokens[1],
+ 'status': [foo for foo in tokens[2:] if '=' not in foo],
+ 'options': [foo for foo in tokens[2:] if '=' in foo]}
+ else:
+ current_vol['status'].extend([foo for foo in tokens if '=' not in foo])
+ current_vol['options'].extend([foo for foo in tokens if '=' in foo])
+ vols.append(current_vol)
+
+ if vol:
+ try:
+ return [foo for foo in vols if foo['name'] == vol][0]
+ except:
+ raise NetappCommandError, "No such volume: %s" % vol
+ else:
+ return vols
+
+ def size(self, filer, vol, delta=None):
+ """
+ TODO: Document me ...
+ """
+ stat_regex = """vol size: Flexible volume .* has size .*."""
+ resize_regex = """vol size: Flexible volume .* size set to .*."""
+ cmd_opts = ['vol', 'size', vol]
+
+ if delta:
+ cmd_opts.append(delta)
+ output = ssh(filer, cmd_opts)
+ return check_output(resize_regex, output)
+ else:
+ output = ssh(filer, cmd_opts)
+ check_output(stat_regex, output)
+ return output.split()[-1][:-1]
+
+ def options(self, filer, args):
+ """
+ TODO: Document me ...
+ """
+ pass
+
+ def rename(self, filer, args):
+ """
+ TODO: Document me ...
+ """
+ pass
+
+ def restrict(self, filer, args):
+ """
+ TODO: Document me ...
+ """
+ pass
diff --git a/func/minion/modules/netapp/vol/clone.py b/func/minion/modules/netapp/vol/clone.py
new file mode 100644
index 0000000..715d8a8
--- /dev/null
+++ b/func/minion/modules/netapp/vol/clone.py
@@ -0,0 +1,46 @@
+##
+## NetApp Filer 'vol.clone' Module
+##
+## Copyright 2008, Red Hat, Inc
+## John Eckersberg <jeckersb@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+import re
+from func.minion.modules import func_module
+from func.minion.modules.netapp.common import *
+
+class Clone(func_module.FuncModule):
+
+ # Update these if need be.
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Interface to the 'vol' command"
+
+ def create(self, filer, vol, parent, snap):
+ """
+ TODO: Document me ...
+ """
+ regex = """Creation of clone volume .* has completed."""
+ cmd_opts = ['vol', 'clone', 'create', vol, '-b', parent, snap]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+ def split(self, filer, vol):
+ """
+ TODO: Document me ...
+ """
+ # only worry about 'start' now, I don't terribly care to automate the rest
+ regex = """Clone volume .* will be split from its parent."""
+ cmd_opts = ['vol', 'clone', 'split', 'start', vol]
+ output = ssh(filer, cmd_opts)
+ return check_output(regex, output)
+
+
+
diff --git a/func/minion/modules/networktest.py b/func/minion/modules/networktest.py
new file mode 100644
index 0000000..0e6fbb2
--- /dev/null
+++ b/func/minion/modules/networktest.py
@@ -0,0 +1,64 @@
+# Copyright 2008, Red Hat, Inc
+# Steve 'Ashcrow' Milner <smilner@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+
+import func_module
+from codes import FuncException
+
+import sub_process
+
+class NetworkTest(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Defines various network testing tools."
+
+ def ping(self, *args):
+ if '-c' not in args:
+ raise(FuncException("You must define a count with -c!"))
+ return self.__run_command('/bin/ping', self.__args_to_list(args))
+
+ def netstat(self, *args):
+ return self.__run_command('/bin/netstat',
+ self.__args_to_list(args))
+
+ def traceroute(self, *args):
+ return self.__run_command('/bin/traceroute',
+ self.__args_to_list(args))
+
+ def dig(self, *args):
+ return self.__run_command('/usr/bin/dig',
+ self.__args_to_list(args))
+
+ def isportopen(self, host, port):
+ # FIXME: the return api here needs some work... -akl
+ import socket
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ timeout = 3.0
+ sock.settimeout(timeout)
+ try:
+ sock.connect((host, int(port)))
+ except socket.error, e:
+ sock.close()
+ return [1, ("connection to %s:%s failed" % (host, port), "%s" % e)]
+ except socket.timeout:
+ sock.close()
+ return [2, ("connection to %s:%s timed out after %s seconds" % (host, port, timeout))]
+
+ sock.close()
+ return [0, "connection to %s:%s succeeded" % (host, port)]
+
+ def __args_to_list(self, args):
+ return [arg for arg in args]
+
+ def __run_command(self, command, opts=[]):
+ full_cmd = [command] + opts
+ cmd = sub_process.Popen(full_cmd, stdout=sub_process.PIPE)
+ return [line for line in cmd.communicate()[0].split('\n')]
diff --git a/func/minion/modules/process.py b/func/minion/modules/process.py
new file mode 100644
index 0000000..848e847
--- /dev/null
+++ b/func/minion/modules/process.py
@@ -0,0 +1,216 @@
+## -*- coding: utf-8 -*-
+##
+## Process lister (control TBA)
+##
+## Copyright 2007, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+# other modules
+import sub_process
+import codes
+
+# our modules
+import func_module
+
+# =================================
+
+class ProcessModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Process related reporting and control."
+
+ def info(self, flags="-auxh"):
+ """
+ Returns a struct of hardware information. By default, this pulls down
+ all of the devices. If you don't care about them, set with_devices to
+ False.
+ """
+
+ flags.replace(";", "") # prevent stupidity
+
+ cmd = sub_process.Popen(["/bin/ps", flags], executable="/bin/ps",
+ stdout=sub_process.PIPE,
+ stderr=sub_process.PIPE,
+ shell=False)
+
+ data, error = cmd.communicate()
+
+ # We can get warnings for odd formatting. warnings != errors.
+ if error and error[:7] != "Warning":
+ raise codes.FuncException(error.split('\n')[0])
+
+ results = []
+ for x in data.split("\n"):
+ tokens = x.split()
+ results.append(tokens)
+
+ return results
+
+ def mem(self):
+ """
+ Returns a list of per-program memory usage.
+
+ Private + Shared = RAM used Program
+
+ [["39.4 MiB", "10.3 MiB", "49.8 MiB", "Xorg"],
+ ["42.2 MiB", "12.4 MiB", "54.6 MiB", "nautilus"],
+ ["52.3 MiB", "10.8 MiB", "63.0 MiB", "liferea-bin"]
+ ["171.6 MiB", "11.9 MiB", "183.5 MiB", "firefox-bin"]]
+
+ Taken from the ps_mem.py script written by Pádraig Brady.
+ http://www.pixelbeat.org/scripts/ps_mem.py
+ """
+ import os
+ our_pid=os.getpid()
+ results = []
+ have_smaps=0
+ have_pss=0
+
+ def kernel_ver():
+ """ (major,minor,release) """
+ kv=open("/proc/sys/kernel/osrelease").readline().split(".")[:3]
+ for char in "-_":
+ kv[2]=kv[2].split(char)[0]
+ return (int(kv[0]), int(kv[1]), int(kv[2]))
+
+ kv=kernel_ver()
+
+ def getMemStats(pid):
+ """ return Rss,Pss,Shared (note Private = Rss-Shared) """
+ Shared_lines=[]
+ Pss_lines=[]
+ pagesize=os.sysconf("SC_PAGE_SIZE")/1024 #KiB
+ Rss=int(open("/proc/"+str(pid)+"/statm").readline().split()[1])*pagesize
+ if os.path.exists("/proc/"+str(pid)+"/smaps"): #stat
+ global have_smaps
+ have_smaps=1
+ for line in open("/proc/"+str(pid)+"/smaps").readlines(): #open
+ #Note in smaps Shared+Private = Rss above
+ #The Rss in smaps includes video card mem etc.
+ if line.startswith("Shared"):
+ Shared_lines.append(line)
+ elif line.startswith("Pss"):
+ global have_pss
+ have_pss=1
+ Pss_lines.append(line)
+ Shared=sum([int(line.split()[1]) for line in Shared_lines])
+ Pss=sum([int(line.split()[1]) for line in Pss_lines])
+ elif (2,6,1) <= kv <= (2,6,9):
+ Pss=0
+ Shared=0 #lots of overestimation, but what can we do?
+ else:
+ Pss=0
+ Shared=int(open("/proc/"+str(pid)+"/statm").readline().split()[2])*pagesize
+ return (Rss, Pss, Shared)
+
+ cmds={}
+ shareds={}
+ count={}
+ for pid in os.listdir("/proc/"):
+ try:
+ pid = int(pid) #note Thread IDs not listed in /proc/
+ if pid ==our_pid: continue
+ except:
+ continue
+ cmd = file("/proc/%d/status" % pid).readline()[6:-1]
+ try:
+ exe = os.path.basename(os.path.realpath("/proc/%d/exe" % pid))
+ if exe.startswith(cmd):
+ cmd=exe #show non truncated version
+ #Note because we show the non truncated name
+ #one can have separated programs as follows:
+ #584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash)
+ #56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin
+ except:
+ #permission denied or
+ #kernel threads don't have exe links or
+ #process gone
+ continue
+ try:
+ rss, pss, shared = getMemStats(pid)
+ private = rss-shared
+ #Note shared is always a subset of rss (trs is not always)
+ except:
+ continue #process gone
+ if shareds.get(cmd):
+ if pss: #add shared portion of PSS together
+ shareds[cmd]+=pss-private
+ elif shareds[cmd] < shared: #just take largest shared val
+ shareds[cmd]=shared
+ else:
+ if pss:
+ shareds[cmd]=pss-private
+ else:
+ shareds[cmd]=shared
+ cmds[cmd]=cmds.setdefault(cmd,0)+private
+ if count.has_key(cmd):
+ count[cmd] += 1
+ else:
+ count[cmd] = 1
+
+ #Add max shared mem for each program
+ total=0
+ for cmd in cmds.keys():
+ cmds[cmd]=cmds[cmd]+shareds[cmd]
+ total+=cmds[cmd] #valid if PSS available
+
+ sort_list = cmds.items()
+ sort_list.sort(lambda x,y:cmp(x[1],y[1]))
+ sort_list=filter(lambda x:x[1],sort_list) #get rid of zero sized processes
+
+ #The following matches "du -h" output
+ def human(num, power="Ki"):
+ powers=["Ki","Mi","Gi","Ti"]
+ while num >= 1000: #4 digits
+ num /= 1024.0
+ power=powers[powers.index(power)+1]
+ return "%.1f %s" % (num,power)
+
+ def cmd_with_count(cmd, count):
+ if count>1:
+ return "%s (%u)" % (cmd, count)
+ else:
+ return cmd
+
+ for cmd in sort_list:
+ results.append([
+ "%sB" % human(cmd[1]-shareds[cmd[0]]),
+ "%sB" % human(shareds[cmd[0]]),
+ "%sB" % human(cmd[1]),
+ "%s" % cmd_with_count(cmd[0], count[cmd[0]])
+ ])
+ if have_pss:
+ results.append(["", "", "", "%sB" % human(total)])
+
+ return results
+
+ memory = mem
+
+ def kill(self,pid,signal="TERM"):
+ if pid == "0":
+ raise codes.FuncException("Killing pid group 0 not permitted")
+ if signal == "":
+ # this is default /bin/kill behaviour,
+ # it claims, but enfore it anyway
+ signal = "-TERM"
+ if signal[0] != "-":
+ signal = "-%s" % signal
+ rc = sub_process.call(["/bin/kill",signal, pid],
+ executable="/bin/kill", shell=False)
+ print rc
+ return rc
+
+ def pkill(self,name,level=""):
+ # example killall("thunderbird","-9")
+ rc = sub_process.call(["/usr/bin/pkill", name, level],
+ executable="/usr/bin/pkill", shell=False)
+ return rc
diff --git a/func/minion/modules/process.py.orig b/func/minion/modules/process.py.orig
new file mode 100644
index 0000000..bdd5193
--- /dev/null
+++ b/func/minion/modules/process.py.orig
@@ -0,0 +1,221 @@
+## -*- coding: utf-8 -*-
+##
+## Process lister (control TBA)
+##
+## Copyright 2007, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+# other modules
+import sub_process
+import codes
+
+# our modules
+from modules 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"):
+ """
+ Returns a struct of hardware information. By default, this pulls down
+ all of the devices. If you don't care about them, set with_devices to
+ False.
+ """
+
+ flags.replace(";", "") # prevent stupidity
+
+ cmd = sub_process.Popen(["/bin/ps", flags], executable="/bin/ps",
+ stdout=sub_process.PIPE,
+ stderr=sub_process.PIPE,
+ shell=False)
+
+ data, error = cmd.communicate()
+
+ # We can get warnings for odd formatting. warnings != errors.
+ if error and error[:7] != "Warning":
+ raise codes.FuncException(error.split('\n')[0])
+
+ results = []
+ for x in data.split("\n"):
+ tokens = x.split()
+ results.append(tokens)
+
+ return results
+
+ def mem(self):
+ """
+ Returns a list of per-program memory usage.
+
+ Private + Shared = RAM used Program
+
+ [["39.4 MiB", "10.3 MiB", "49.8 MiB", "Xorg"],
+ ["42.2 MiB", "12.4 MiB", "54.6 MiB", "nautilus"],
+ ["52.3 MiB", "10.8 MiB", "63.0 MiB", "liferea-bin"]
+ ["171.6 MiB", "11.9 MiB", "183.5 MiB", "firefox-bin"]]
+
+ Taken from the ps_mem.py script written by Pádraig Brady.
+ http://www.pixelbeat.org/scripts/ps_mem.py
+ """
+ import os
+ our_pid=os.getpid()
+ results = []
+ have_smaps=0
+ have_pss=0
+
+ def kernel_ver():
+ """ (major,minor,release) """
+ kv=open("/proc/sys/kernel/osrelease").readline().split(".")[:3]
+ for char in "-_":
+ kv[2]=kv[2].split(char)[0]
+ return (int(kv[0]), int(kv[1]), int(kv[2]))
+
+ kv=kernel_ver()
+
+ def getMemStats(pid):
+ """ return Rss,Pss,Shared (note Private = Rss-Shared) """
+ Shared_lines=[]
+ Pss_lines=[]
+ pagesize=os.sysconf("SC_PAGE_SIZE")/1024 #KiB
+ Rss=int(open("/proc/"+str(pid)+"/statm").readline().split()[1])*pagesize
+ if os.path.exists("/proc/"+str(pid)+"/smaps"): #stat
+ global have_smaps
+ have_smaps=1
+ for line in open("/proc/"+str(pid)+"/smaps").readlines(): #open
+ #Note in smaps Shared+Private = Rss above
+ #The Rss in smaps includes video card mem etc.
+ if line.startswith("Shared"):
+ Shared_lines.append(line)
+ elif line.startswith("Pss"):
+ global have_pss
+ have_pss=1
+ Pss_lines.append(line)
+ Shared=sum([int(line.split()[1]) for line in Shared_lines])
+ Pss=sum([int(line.split()[1]) for line in Pss_lines])
+ elif (2,6,1) <= kv <= (2,6,9):
+ Pss=0
+ Shared=0 #lots of overestimation, but what can we do?
+ else:
+ Pss=0
+ Shared=int(open("/proc/"+str(pid)+"/statm").readline().split()[2])*pagesize
+ return (Rss, Pss, Shared)
+
+ cmds={}
+ shareds={}
+ count={}
+ for pid in os.listdir("/proc/"):
+ try:
+ pid = int(pid) #note Thread IDs not listed in /proc/
+ if pid ==our_pid: continue
+ except:
+ continue
+ cmd = file("/proc/%d/status" % pid).readline()[6:-1]
+ try:
+ exe = os.path.basename(os.path.realpath("/proc/%d/exe" % pid))
+ if exe.startswith(cmd):
+ cmd=exe #show non truncated version
+ #Note because we show the non truncated name
+ #one can have separated programs as follows:
+ #584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash)
+ #56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin
+ except:
+ #permission denied or
+ #kernel threads don't have exe links or
+ #process gone
+ continue
+ try:
+ rss, pss, shared = getMemStats(pid)
+ private = rss-shared
+ #Note shared is always a subset of rss (trs is not always)
+ except:
+ continue #process gone
+ if shareds.get(cmd):
+ if pss: #add shared portion of PSS together
+ shareds[cmd]+=pss-private
+ elif shareds[cmd] < shared: #just take largest shared val
+ shareds[cmd]=shared
+ else:
+ if pss:
+ shareds[cmd]=pss-private
+ else:
+ shareds[cmd]=shared
+ cmds[cmd]=cmds.setdefault(cmd,0)+private
+ if count.has_key(cmd):
+ count[cmd] += 1
+ else:
+ count[cmd] = 1
+
+ #Add max shared mem for each program
+ total=0
+ for cmd in cmds.keys():
+ cmds[cmd]=cmds[cmd]+shareds[cmd]
+ total+=cmds[cmd] #valid if PSS available
+
+ sort_list = cmds.items()
+ sort_list.sort(lambda x,y:cmp(x[1],y[1]))
+ sort_list=filter(lambda x:x[1],sort_list) #get rid of zero sized processes
+
+ #The following matches "du -h" output
+ def human(num, power="Ki"):
+ powers=["Ki","Mi","Gi","Ti"]
+ while num >= 1000: #4 digits
+ num /= 1024.0
+ power=powers[powers.index(power)+1]
+ return "%.1f %s" % (num,power)
+
+ def cmd_with_count(cmd, count):
+ if count>1:
+ return "%s (%u)" % (cmd, count)
+ else:
+ return cmd
+
+ for cmd in sort_list:
+ results.append([
+ "%sB" % human(cmd[1]-shareds[cmd[0]]),
+ "%sB" % human(shareds[cmd[0]]),
+ "%sB" % human(cmd[1]),
+ "%s" % cmd_with_count(cmd[0], count[cmd[0]])
+ ])
+ if have_pss:
+ results.append(["", "", "", "%sB" % human(total)])
+
+ return results
+
+ def kill(self,pid,signal="TERM"):
+ if pid == "0":
+ raise codes.FuncException("Killing pid group 0 not permitted")
+ if signal == "":
+ # this is default /bin/kill behaviour,
+ # it claims, but enfore it anyway
+ signal = "-TERM"
+ if signal[0] != "-":
+ signal = "-%s" % signal
+ rc = sub_process.call(["/bin/kill",signal, pid],
+ executable="/bin/kill", shell=False)
+ print rc
+ return rc
+
+ def pkill(self,name,level=""):
+ # 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
new file mode 100644
index 0000000..c4fb275
--- /dev/null
+++ b/func/minion/modules/reboot.py
@@ -0,0 +1,21 @@
+# Copyright 2007, Red Hat, Inc
+# James Bowes <jbowes@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+import func_module
+import sub_process
+
+class Reboot(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Reboots a machine."
+
+ def reboot(self, when='now', message=''):
+ return sub_process.call(["/sbin/shutdown", '-r', when, message])
diff --git a/func/minion/modules/rpms.py b/func/minion/modules/rpms.py
new file mode 100644
index 0000000..ae26cb4
--- /dev/null
+++ b/func/minion/modules/rpms.py
@@ -0,0 +1,44 @@
+# Copyright 2007, Red Hat, Inc
+# Michael DeHaan <mdehaan@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+import func_module
+import rpm
+
+class RpmModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "RPM related commands."
+
+ def inventory(self, flatten=True):
+ """
+ Returns information on all installed packages.
+ By default, 'flatten' is passed in as True, which makes printouts very
+ clean in diffs for use by func-inventory. If you are writting another
+ software application, using flatten=False will prevent the need to
+ parse the returns.
+ """
+ # I have not been able to get flatten=False to work if there
+ # is more than 491 entries in the dict -- ashcrow
+ ts = rpm.TransactionSet()
+ mi = ts.dbMatch()
+ results = []
+ for hdr in mi:
+ name = hdr['name']
+ epoch = (hdr['epoch'] or 0)
+ version = hdr['version']
+ release = hdr['release']
+ arch = hdr['arch']
+ if flatten:
+ results.append("%s %s %s %s %s" % (name, epoch, version,
+ release, arch))
+ else:
+ results.append([name, epoch, version, release, arch])
+ return results
diff --git a/func/minion/modules/service.py b/func/minion/modules/service.py
new file mode 100644
index 0000000..062aea5
--- /dev/null
+++ b/func/minion/modules/service.py
@@ -0,0 +1,88 @@
+## func
+##
+## Copyright 2007, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+##
+
+import codes
+import func_module
+
+import sub_process
+import os
+
+class Service(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Allows for service control via func."
+
+ def __command(self, service_name, command):
+
+ filename = os.path.join("/etc/rc.d/init.d/",service_name)
+ if os.path.exists(filename):
+ return sub_process.call(["/sbin/service", service_name, command])
+ else:
+ raise codes.FuncException("Service not installed: %s" % service_name)
+
+ def start(self, service_name):
+ return self.__command(service_name, "start")
+
+ def stop(self, service_name):
+ return self.__command(service_name, "stop")
+
+ def restart(self, service_name):
+ return self.__command(service_name, "restart")
+
+ def reload(self, service_name):
+ return self.__command(service_name, "reload")
+
+ def status(self, service_name):
+ return self.__command(service_name, "status")
+
+ def inventory(self):
+ return {
+ "running" : self.get_running(),
+ "enabled" : self.get_enabled()
+ }
+
+ def get_enabled(self):
+ """
+ Get the list of services that are enabled at the various runlevels. Xinetd services
+ only provide whether or not they are running, not specific runlevel info.
+ """
+
+ chkconfig = sub_process.Popen(["/sbin/chkconfig", "--list"], stdout=sub_process.PIPE)
+ data = chkconfig.communicate()[0]
+ results = []
+ for line in data.split("\n"):
+ if line.find("0:") != -1:
+ # regular services
+ tokens = line.split()
+ results.append((tokens[0],tokens[1:]))
+ elif line.find(":") != -1 and not line.endswith(":"):
+ # xinetd.d based services
+ tokens = line.split()
+ tokens[0] = tokens[0].replace(":","")
+ results.append((tokens[0],tokens[1]))
+ return results
+
+ def get_running(self):
+ """
+ Get a list of which services are running, stopped, or disabled.
+ """
+ chkconfig = sub_process.Popen(["/sbin/service", "--status-all"], stdout=sub_process.PIPE)
+ data = chkconfig.communicate()[0]
+ results = []
+ for line in data.split("\n"):
+ if line.find(" is ") != -1:
+ tokens = line.split()
+ results.append((tokens[0], tokens[-1].replace("...","")))
+ return results
diff --git a/func/minion/modules/smart.py b/func/minion/modules/smart.py
new file mode 100644
index 0000000..f410f09
--- /dev/null
+++ b/func/minion/modules/smart.py
@@ -0,0 +1,47 @@
+##
+## Grabs status from SMART to see if your hard drives are ok
+## Returns in the format of (return code, [line1, line2, line3,...])
+##
+## Copyright 2007, Red Hat, Inc
+## Michael DeHaan <mdehaan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## 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.
+##
+
+# other modules
+import sub_process
+
+# our modules
+import func_module
+
+# =================================
+
+class SmartModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Grabs status from SMART to see if your hard drives are ok."
+
+ def info(self,flags="-q onecheck"):
+ """
+ Returns a struct of hardware information. By default, this pulls down
+ all of the devices. If you don't care about them, set with_devices to
+ False.
+ """
+
+ flags.replace(";","") # prevent stupidity
+
+ cmd = sub_process.Popen("/usr/sbin/smartd %s" % flags,stdout=sub_process.PIPE,shell=True)
+ data = cmd.communicate()[0]
+
+ results = []
+
+ for x in data.split("\n"):
+ results.append(x)
+
+ return (cmd.returncode, results)
diff --git a/func/minion/modules/snmp.py b/func/minion/modules/snmp.py
new file mode 100644
index 0000000..c992db1
--- /dev/null
+++ b/func/minion/modules/snmp.py
@@ -0,0 +1,38 @@
+# Copyright 2007, Red Hat, Inc
+# James Bowes <jbowes@redhat.com>
+# Seth Vidal modified command.py to be snmp.py
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+"""
+Abitrary command execution module for func.
+"""
+
+import func_module
+import sub_process
+base_snmp_command = '/usr/bin/snmpget -v2c -Ov -OQ'
+
+class Snmp(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "SNMP related calls through func."
+
+ def get(self, oid, rocommunity, hostname='localhost'):
+ """
+ Runs an snmpget on a specific oid returns the output of the call.
+ """
+ command = '%s -c %s %s %s' % (base_snmp_command, rocommunity, hostname, oid)
+
+ 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])
+
+ #def walk(self, oid, rocommunity):
+
+ #def table(self, oid, rocommunity):
diff --git a/func/minion/modules/sysctl.py b/func/minion/modules/sysctl.py
new file mode 100644
index 0000000..1f11d55
--- /dev/null
+++ b/func/minion/modules/sysctl.py
@@ -0,0 +1,31 @@
+# Copyright 2008, Red Hat, Inc
+# Luke Macken <lmacken@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+import func_module
+import sub_process
+
+class SysctlModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ description = "Configure kernel parameters at runtime"
+
+ def __run(self, cmd):
+ cmd = sub_process.Popen(cmd.split(), stdout=sub_process.PIPE,
+ stderr=sub_process.PIPE, shell=False)
+ return [line for line in cmd.communicate()[0].strip().split('\n')]
+
+ def list(self):
+ return self.__run("/sbin/sysctl -a")
+
+ def get(self, name):
+ return self.__run("/sbin/sysctl -n %s" % name)
+
+ def set(self, name, value):
+ return self.__run("/sbin/sysctl -w %s=%s" % (name, value))
diff --git a/func/minion/modules/test.py b/func/minion/modules/test.py
new file mode 100644
index 0000000..6f7c5fa
--- /dev/null
+++ b/func/minion/modules/test.py
@@ -0,0 +1,29 @@
+import func_module
+import time
+import exceptions
+
+class Test(func_module.FuncModule):
+ version = "11.11.11"
+ api_version = "0.0.1"
+ description = "Just a very simple example module"
+
+ def add(self, numb1, numb2):
+ return numb1 + numb2
+
+ def ping(self):
+ return 1
+
+ def sleep(self,t):
+ """
+ Sleeps for t seconds, and returns time of day.
+ Simply a test function for trying out async and threaded voodoo.
+ """
+ t = int(t)
+ time.sleep(t)
+ return time.time()
+
+ def explode(self):
+ """
+ Testing remote exception handling is useful
+ """
+ raise exceptions.Exception("khhhhhhaaaaaan!!!!!!")
diff --git a/func/minion/modules/virt.py b/func/minion/modules/virt.py
new file mode 100644
index 0000000..04d36bd
--- /dev/null
+++ b/func/minion/modules/virt.py
@@ -0,0 +1,277 @@
+"""
+Virt management features
+
+Copyright 2007, Red Hat, Inc
+Michael DeHaan <mdehaan@redhat.com>
+
+This software may be freely redistributed under the terms of the GNU
+general public license.
+
+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.
+"""
+
+# warning: virt management is rather complicated
+# to see a simple example of func, look at the
+# service control module. API docs on how
+# to use this to come.
+
+# other modules
+import os
+import sub_process
+import libvirt
+
+# our modules
+import codes
+import func_module
+
+VIRT_STATE_NAME_MAP = {
+ 0 : "running",
+ 1 : "running",
+ 2 : "running",
+ 3 : "paused",
+ 4 : "shutdown",
+ 5 : "shutdown",
+ 6 : "crashed"
+}
+
+class FuncLibvirtConnection(object):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Virtualization items through func."
+
+ def __init__(self):
+
+ cmd = sub_process.Popen("uname -r", shell=True, stdout=sub_process.PIPE)
+ output = cmd.communicate()[0]
+
+ if output.find("xen") != -1:
+ conn = libvirt.open(None)
+ else:
+ conn = libvirt.open("qemu:///system")
+
+ if not conn:
+ raise codes.FuncException("hypervisor connection failure")
+
+ self.conn = conn
+
+ def find_vm(self, vmid):
+ """
+ Extra bonus feature: vmid = -1 returns a list of everything
+ """
+ conn = self.conn
+
+ vms = []
+
+ # this block of code borrowed from virt-manager:
+ # get working domain's name
+ ids = conn.listDomainsID();
+ for id in ids:
+ vm = conn.lookupByID(id)
+ vms.append(vm)
+ # get defined domain
+ names = conn.listDefinedDomains()
+ for name in names:
+ vm = conn.lookupByName(name)
+ vms.append(vm)
+
+ if vmid == -1:
+ return vms
+
+ for vm in vms:
+ if vm.name() == vmid:
+ return vm
+
+ raise codes.FuncException("virtual machine %s not found" % vmid)
+
+ def shutdown(self, vmid):
+ return self.find_vm(vmid).shutdown()
+
+ def pause(self, vmid):
+ return self.suspend(self.conn,vmid)
+
+ def unpause(self, vmid):
+ return self.resume(self.conn,vmid)
+
+ def suspend(self, vmid):
+ return self.find_vm(vmid).suspend()
+
+ def resume(self, vmid):
+ return self.find_vm(vmid).resume()
+
+ def create(self, vmid):
+ return self.find_vm(vmid).create()
+
+ def destroy(self, vmid):
+ return self.find_vm(vmid).destroy()
+
+ def undefine(self, vmid):
+ return self.find_vm(vmid).undefine()
+
+ def get_status2(self, vm):
+ state = vm.info()[0]
+ # print "DEBUG: state: %s" % state
+ return VIRT_STATE_NAME_MAP.get(state,"unknown")
+
+ def get_status(self, vmid):
+ state = self.find_vm(vmid).info()[0]
+ return VIRT_STATE_NAME_MAP.get(state,"unknown")
+
+
+
+class Virt(func_module.FuncModule):
+
+ def __get_conn(self):
+ self.conn = FuncLibvirtConnection()
+ return self.conn
+
+ def state(self):
+ vms = self.list_vms()
+ state = []
+ for vm in vms:
+ state_blurb = self.conn.get_status(vm)
+ state.append("%s %s" % (vm,state_blurb))
+ return state
+
+
+ def info(self):
+ vms = self.list_vms()
+ info = dict()
+ for vm in vms:
+ data = self.conn.find_vm(vm).info()
+ # libvirt returns maxMem, memory, and cpuTime as long()'s, which
+ # xmlrpclib tries to convert to regular int's during serialization.
+ # This throws exceptions, so convert them to strings here and
+ # assume the other end of the xmlrpc connection can figure things
+ # out or doesn't care.
+ info[vm] = {
+ "state" : VIRT_STATE_NAME_MAP.get(data[0],"unknown"),
+ "maxMem" : str(data[1]),
+ "memory" : str(data[2]),
+ "nrVirtCpu" : data[3],
+ "cpuTime" : str(data[4])
+ }
+ return info
+
+
+ def list_vms(self):
+ self.conn = self.__get_conn()
+ vms = self.conn.find_vm(-1)
+ results = []
+ for x in vms:
+ try:
+ results.append(x.name())
+ except:
+ pass
+ return results
+
+ def install(self, server_name, target_name, system=False):
+
+ """
+ Install a new virt system by way of a named cobbler profile.
+ """
+
+ # Example:
+ # install("bootserver.example.org", "fc7webserver", True)
+
+ conn = self.__get_conn()
+
+ if conn is None:
+ raise codes.FuncException("no connection")
+
+ if not os.path.exists("/usr/bin/koan"):
+ raise codes.FuncException("no /usr/bin/koan")
+ target = "profile"
+ if system:
+ target = "system"
+
+ # TODO: FUTURE: set --virt-path in cobbler or here
+ koan_args = [
+ "/usr/bin/koan",
+ "--virt",
+ "--virt-graphics", # enable VNC
+ "--%s=%s" % (target, target_name),
+ "--server=%s" % server_name
+ ]
+
+ rc = sub_process.call(koan_args,shell=False)
+ if rc == 0:
+ return 0
+ else:
+ raise codes.FuncException("koan returned %d" % rc)
+
+
+ def shutdown(self, vmid):
+ """
+ Make the machine with the given vmid stop running.
+ Whatever that takes.
+ """
+ self.__get_conn()
+ self.conn.shutdown(vmid)
+ return 0
+
+
+ def pause(self, vmid):
+
+ """
+ Pause the machine with the given vmid.
+ """
+ self.__get_conn()
+ self.conn.suspend(vmid)
+ return 0
+
+
+ def unpause(self, vmid):
+
+ """
+ Unpause the machine with the given vmid.
+ """
+
+ self.__get_conn()
+ self.conn.resume(vmid)
+ return 0
+
+
+ def create(self, vmid):
+
+ """
+ Start the machine via the given mac address.
+ """
+ self.__get_conn()
+ self.conn.create(vmid)
+ return 0
+
+
+ def destroy(self, vmid):
+
+ """
+ Pull the virtual power from the virtual domain, giving it virtually no
+ time to virtually shut down.
+ """
+ self.__get_conn()
+ self.conn.destroy(vmid)
+ return 0
+
+
+ def undefine(self, vmid):
+
+ """
+ Stop a domain, and then wipe it from the face of the earth.
+ by deleting the disk image and it's configuration file.
+ """
+
+ self.__get_conn()
+ self.conn.undefine(vmid)
+ return 0
+
+
+ def get_status(self, vmid):
+
+ """
+ Return a state suitable for server consumption. Aka, codes.py values, not XM output.
+ """
+
+ self.__get_conn()
+ return self.conn.get_status(vmid)
diff --git a/func/minion/modules/yumcmd.py b/func/minion/modules/yumcmd.py
new file mode 100644
index 0000000..f952372
--- /dev/null
+++ b/func/minion/modules/yumcmd.py
@@ -0,0 +1,50 @@
+# Copyright 2007, Red Hat, Inc
+# James Bowes <jbowes@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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.
+
+import func_module
+
+import yum
+
+# XXX Use internal yum callback or write a useful one.
+class DummyCallback(object):
+
+ def event(self, state, data=None):
+ pass
+
+class Yum(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Package updates through yum."
+
+ def update(self):
+ # XXX support updating specific rpms
+ ayum = yum.YumBase()
+ ayum.doGenericSetup()
+ ayum.doRepoSetup()
+ try:
+ ayum.doLock()
+ ayum.update()
+ ayum.buildTransaction()
+ ayum.processTransaction(
+ callback=DummyCallback())
+ finally:
+ ayum.closeRpmDB()
+ ayum.doUnlock()
+ return True
+
+ def check_update(self, repo=None):
+ """Returns a list of packages due to be updated"""
+ ayum = yum.YumBase()
+ ayum.doConfigSetup()
+ ayum.doTsSetup()
+ if repo is not None:
+ ayum.repos.enableRepo(repo)
+ return map(str, ayum.doPackageLists('updates').updates)