diff options
-rw-r--r-- | README | 3 | ||||
-rwxr-xr-x | exmachina.py | 111 | ||||
-rwxr-xr-x | test.py | 11 |
3 files changed, 110 insertions, 15 deletions
@@ -11,14 +11,11 @@ Just a first commit... TODO: -* export python-augeas API calls in server -* add /etc/init.d start/stop/status/reset API methods * re-implement python-augeas methods using API client-side * use /var/lib/exmachina/<something> as socket instead of /tmp/exmachina.sock? * check to make sure server is running as root * check/set permissions on socket after server opens it * tests and demonstrations -* fix/remove logging ### Dependancies (server) diff --git a/exmachina.py b/exmachina.py index fca499e..cb909ef 100755 --- a/exmachina.py +++ b/exmachina.py @@ -9,6 +9,8 @@ import sys import optparse import logging import socket +import subprocess +import stat import bjsonrpc import bjsonrpc.handlers @@ -19,26 +21,106 @@ import time # TODO log = logging.getLogger(__name__) +def run_service(servicename, action, timeout=10): + """This function mostly ripped from StackOverflow: + http://stackoverflow.com/questions/1556348/python-run-a-process-with-timeout-and-capture-stdout-stderr-and-exit-status + """ + # ensure service name isn't tricky trick + script = "/etc/init.d/" + os.path.split(servicename)[1] + + if not os.path.exists(script): + return "ERROR: so such service" + + command_list = [script, action] + log.info("running: %s" % command_list) + proc = subprocess.Popen(command_list, + bufsize=0, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + poll_seconds = .250 + deadline = time.time() + timeout + while time.time() < deadline and proc.poll() == None: + time.sleep(poll_seconds) + + if proc.poll() == None: + if float(sys.version[:3]) >= 2.6: + proc.terminate() + raise Exception("Timeout: %s" % command_list) + + stdout, stderr = proc.communicate() + return stdout, stderr, proc.returncode class ExMachinaHandler(bjsonrpc.handlers.BaseHandler): - def whattime(self): - print "hup!" + + def _setup(self): + self.augeas = augeas.Augeas() + + def test_whattime(self): + log.debug("whattime") return time.time() - def listfiles(self): - a = augeas.Augeas() - return a.match("/files/etc/*") + def test_listfiles(self): + log.debug("listfiles") + return self.augeas.match("/files/etc/*") + + # ------------- Augeas API Passthrough ----------------- + def augeas_save(self): + log.info("augeas: saving config") + return self.augeas.save() + + def augeas_set(self, path, value): + log.info("augeas: set %s=%s" % (path, value)) + return self.augeas.set(path.encode('utf-8'), + value.encode('utf-8')) + + def augeas_setm(self, base, sub, value): + log.info("augeas: setm %s %s = %s" % (base, sub, value)) + return self.augeas.setm(base.encode('utf-8'), + sub.encode('utf-8'), + value.encode('utf-8')) + + def augeas_get(self, path): + # reduce verbosity + log.debug("augeas: get %s" % path) + return self.augeas.get(path.encode('utf-8')) + + def augeas_match(self, path): + # reduce verbosity + log.debug("augeas: match %s" % path) + return self.augeas.match("%s" % path.encode('utf-8')) + + def augeas_insert(self, path, label, before=True): + log.info("augeas: insert %s=%s" % (path, value)) + return self.augeas.insert(path.encode('utf-8'), + label.encode('utf-8'), + before=before) + + def augeas_move(self, src, dst): + log.info("augeas: move %s -> %s" % (src, dst)) + return self.augeas.move(src.encode('utf-8'), dst.encode('utf-8')) + + def augeas_remove(self, path): + log.info("augeas: remove %s" % path) + return self.augeas.remove(path.encode('utf-8')) + + # ------------- Service Control ----------------- + def initd_status(self, servicename): + return run_service(servicename, "status") + + def initd_start(self, servicename): + return run_service(servicename, "start") + + def initd_stop(self, servicename): + return run_service(servicename, "stop") + + def initd_restart(self, servicename): + return run_service(servicename, "restart") class ExMachinaClient(): pass #TODO def run_server(socket_path="/tmp/exmachina.sock"): # TODO: check for root permissions, warn if not root - log.info('This is an INFO level message.') - log.debug('This is a DEBUG level message.') - log.warn('This is a WARN level message.') - - # TODO: if socket file exists, try to delete it if os.path.exists(socket_path): os.unlink(socket_path) @@ -47,6 +129,10 @@ def run_server(socket_path="/tmp/exmachina.sock"): sock.listen(1) serv = bjsonrpc.server.Server(sock, handler_factory=ExMachinaHandler) + + # TODO: group permissions only? + os.chmod(socket_path, 0777) + serv.serve() # ============================================================================= @@ -69,6 +155,11 @@ def main(): parser.error("Incorrect number of arguments") log = logging.getLogger() + hdlr = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + log.addHandler(hdlr) + if options.verbose: log.setLevel(logging.DEBUG) else: @@ -18,8 +18,15 @@ def main(): sock.connect(socket_path) c = bjsonrpc.connection.Connection(sock) - print c.call.whattime() - print c.call.listfiles() + print "time: %s" % c.call.test_whattime() + print "files: %s" % c.call.test_listfiles() + print c.call.initd_status("bluetooth") + print "/*: %s" % c.call.augeas_match("/*") + print "/files/*: %s" % c.call.augeas_match("/files/*") + print "/files/etc/*: %s" % c.call.augeas_match("/files/etc/*") + print "/augeas/*: %s" % c.call.augeas_match("/augeas/*") + print "hostname: %s" % c.call.augeas_get("/files/etc/hostname/*") + print "localhost: %s" % c.call.augeas_get("/files/etc/hosts/1/canonical") if __name__ == '__main__': main() |