aboutsummaryrefslogtreecommitdiffstats
path: root/exmachina.py
blob: a72ac551187c9955cf6372f5e4f3698c89e2ad98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python

# Author: bnewbold <bnewbold@robocracy.org>
# Date: July 2012
# License: GPLv3 (see http://www.gnu.org/licenses/gpl-3.0.html)

import os
import sys
import optparse
import logging
import socket
import subprocess
import stat
import time

import bjsonrpc
import bjsonrpc.handlers
import bjsonrpc.server
import augeas


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 _setup(self):
        self.augeas = augeas.Augeas()

    def test_whattime(self):
        log.debug("whattime")
        return time.time()

    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 EmptyClass():
    pass

class ExMachinaClient():

    def __init__(self, socket_path = "/tmp/exmachina.sock"):
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.sock.connect(socket_path)
        self.conn = bjsonrpc.connection.Connection(self.sock)

        self.augeas = EmptyClass()
        self.initd = EmptyClass()

        self.augeas.save = self.conn.call.augeas_save
        self.augeas.set = self.conn.call.augeas_set
        self.augeas.setm = self.conn.call.augeas_setm
        self.augeas.get = self.conn.call.augeas_get
        self.augeas.match = self.conn.call.augeas_match
        self.augeas.insert = self.conn.call.augeas_insert
        self.augeas.move = self.conn.call.augeas_move
        self.augeas.remove = self.conn.call.augeas_remove
        self.initd.status = self.conn.call.initd_status
        self.initd.start = self.conn.call.initd_start
        self.initd.stop = self.conn.call.initd_stop
        self.initd.restart = self.conn.call.initd_restart

def run_server(socket_path="/tmp/exmachina.sock"):
    # TODO: check for root permissions, warn if not root

    if not 0 == os.geteuid():
        log.warn("Expected to be running as root!")

    if os.path.exists(socket_path):
        os.unlink(socket_path)
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind(socket_path)
    sock.listen(1)

    serv = bjsonrpc.server.Server(sock, handler_factory=ExMachinaHandler)

    # TODO: group permissions only?
    os.chmod(socket_path, 0777)

    serv.serve()

# =============================================================================
# Command line handling
def main():

    global log
    parser = optparse.OptionParser(usage=
        "usage: %prog [options]\n"
        "%prog --help for more info."
    )
    parser.add_option("-v", "--verbose", 
        default=False,
        help="Show more debugging statements", 
        action="store_true")

    (options, args) = parser.parse_args()

    if len(args) != 0:
        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:
        log.setLevel(logging.INFO)

    run_server()

if __name__ == '__main__':
    main()