summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rw-r--r--func.spec1
-rw-r--r--func/minion/modules/filetracker.py197
-rwxr-xr-xfunc/minion/modules/virt.py11
-rw-r--r--setup.py2
5 files changed, 212 insertions, 2 deletions
diff --git a/AUTHORS b/AUTHORS
index b4539ca..ceef6cb 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,7 +11,8 @@ func is written by (alphabetically) ...
Additional patches and contributions by ...
+ Vito Laurenza <vitolaurenza@gmail.com>
...
- [ send in patches to get your name here ]
+ [ send in patches (or modules!) to get your name here ]
diff --git a/func.spec b/func.spec
index 386e2ae..579093c 100644
--- a/func.spec
+++ b/func.spec
@@ -50,6 +50,7 @@ rm -fr $RPM_BUILD_ROOT
%dir %{_sysconfdir}/%{name}
%dir %{_sysconfdir}/%{name}/minion-acl.d/
%dir %{_sysconfdir}/pki/%{name}
+%dir /etc/func/modules/
%config(noreplace) /etc/func/minion.conf
%config(noreplace) /etc/func/certmaster.conf
%config(noreplace) /etc/logrotate.d/func_rotate
diff --git a/func/minion/modules/filetracker.py b/func/minion/modules/filetracker.py
new file mode 100644
index 0000000..efcc31c
--- /dev/null
+++ b/func/minion/modules/filetracker.py
@@ -0,0 +1,197 @@
+#!/usr/bin/python
+
+## 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>
+##
+## 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
+from modules import func_module
+
+# other modules
+# import ConfigParser
+from stat import *
+import os
+import sys
+import md5
+
+# defaults
+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.
+ """
+
+ 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()
+ print "DEBUG: deleting: %s" % file_name
+ print "DEBUG: keys: %s" % filehash
+ 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 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()
+
+methods = FileTracker()
+register_rpc = methods.register_rpc
diff --git a/func/minion/modules/virt.py b/func/minion/modules/virt.py
index 7dc5697..3e7fc2d 100755
--- a/func/minion/modules/virt.py
+++ b/func/minion/modules/virt.py
@@ -22,7 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# other modules
import os
import sub_process
-import libvirt
+# import libvirt
# our modules
import codes
@@ -42,6 +42,13 @@ class FuncLibvirtConnection(object):
def __init__(self):
+ self.loaded = False
+
+ try:
+ import libvirt
+ self.loaded = True
+ except:
+ return
cmd = sub_process.Popen("uname -r", shell=True, stdout=sub_process.PIPE)
output = cmd.communicate()[0]
@@ -147,6 +154,8 @@ class Virt(func_module.FuncModule):
def get_conn(self):
self.conn = FuncLibvirtConnection()
+ if not self.conn.loaded:
+ return False
return self.conn
def info(self):
diff --git a/setup.py b/setup.py
index 7cb1bef..7675ed1 100644
--- a/setup.py
+++ b/setup.py
@@ -15,6 +15,7 @@ if __name__ == "__main__":
manpath = "share/man/man1/"
etcpath = "/etc/%s" % NAME
+ etcmodpath = "/etc/%s/modules" % NAME
initpath = "/etc/init.d/"
logpath = "/var/log/%s/" % NAME
pkipath = "/etc/pki/%s" % NAME
@@ -45,6 +46,7 @@ if __name__ == "__main__":
(initpath, ["init-scripts/certmaster"]),
(etcpath, ["etc/minion.conf"]),
(etcpath, ["etc/certmaster.conf"]),
+ (etcmodpath, []),
(manpath, ["docs/func.1.gz"]),
(manpath, ["docs/func-inventory.1.gz"]),
(manpath, ["docs/funcd.1.gz"]),