source: starbed_plugin/starbed.py @ adf8517

compt_changes
Last change on this file since adf8517 was adf8517, checked in by alwabel <alwabel@…>, 12 years ago

Starbed plugin

  • Property mode set to 100644
File size: 8.9 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4import subprocess
5import re
6import string
7import copy
8import pickle
9import logging
10import random
11import tempfile
12from federation.util import *
13from federation.fedid import fedid, generate_fedid
14from federation.authorizer import authorizer
15from federation.service_error import service_error
16from federation.remote_service import xmlrpc_handler, soap_handler, service_caller
17
18import federation.topdl as topdl
19
20from federation.access import access_base
21from federation.legacy_access import legacy_access
22from federation.proof import proof
23from topdltok import klanguage
24
25# Make log messages disappear if noone configures a fedd logger.  This is
26# something of an incantation, but basically it creates a logger object
27# registered to fedd.access if no other module above us has.  It's an extra
28# belt for the suspenders.
29class nullHandler(logging.Handler):
30    def emit(self, record): pass
31
32fl = logging.getLogger("fedd.access")
33fl.addHandler(nullHandler())
34
35
36# The plug-in itself.
37class access(access_base):
38
39    @staticmethod 
40    def parse_access_string(s):
41        """
42        Parse a parenthesized string from the access db by removing the parens.
43        If the string isn't in parens, we just return it with whitespace
44        trimmed in either case.
45        """
46        st = s.strip()
47        if st.startswith("(") and st.endswith(")"): return st[1:-1]
48        else: return st
49
50    def __init__(self, config=None, auth=None):
51        self.rmanager = config.get('definition','rmanager')
52        self.smanager = config.get('definition','smanager')
53        self.pmanager = config.get('definition','pmanager')
54        self.fncp = config.get('definition','fncp')
55        self.tftpdman =config.get('definition','tftpdman')
56        self.wolagent = config.get('definition','wolagent')
57        self.encd = config.get('definition','encd')
58        self.user = config.get('definition','user')
59        self.project= config.get('definition','project')
60        self.rpassword = config.get('definition','rpassword') 
61
62        access_base.__init__(self, config, auth)
63
64        # authorization information
65        self.auth_type = config.get('access', 'auth_type') \
66                or 'legacy'
67        self.auth_dir = config.get('access', 'auth_dir')
68        accessdb = config.get("access", "accessdb")
69        print "authentication type = %s" %(self.auth_type);
70        if self.auth_type == 'starbed':
71                self.log.debug("starbed authtication methond");
72        else:
73            raise service_error(service_error.internal, 
74                    "Unknown auth_type: %s" % self.auth_type)
75
76        # These dictionaries register the plug-in's local routines for handline
77        # these four messages with the server code above.  There's a version
78        # for SOAP and XMLRPC, depending on which interfaces the plugin
79        # supports.  There's rarely a technical reason not to support one or
80        # the other - the plugin code almost never deals with the transport -
81        # but if a plug-in writer wanted to disable XMLRPC, they could leave
82        # the self.xmlrpc_services dictionary empty.
83        self.soap_services = {\
84            'RequestAccess': soap_handler("RequestAccess", self.RequestAccess),
85            'ReleaseAccess': soap_handler("ReleaseAccess", self.ReleaseAccess),
86            'StartSegment': soap_handler("StartSegment", self.StartSegment),
87            'TerminateSegment': soap_handler("TerminateSegment", 
88                self.TerminateSegment),
89            }
90        self.xmlrpc_services =  {\
91            'RequestAccess': xmlrpc_handler('RequestAccess',
92                self.RequestAccess),
93            'ReleaseAccess': xmlrpc_handler('ReleaseAccess',
94                self.ReleaseAccess),
95            'StartSegment': xmlrpc_handler("StartSegment", self.StartSegment),
96            'TerminateSegment': xmlrpc_handler('TerminateSegment',
97                self.TerminateSegment),
98            }
99        self.log.debug("Starbed AC started!")
100    # RequestAccess and ReleaseAccess come from the base class
101    def RequestAccess(self, req, fid):
102        # The dance to get into the request body
103        if req.has_key('RequestAccessRequestBody'):
104            req = req['RequestAccessRequestBody']
105        else:
106            raise service_error(service_error.req, "No request!?")
107        #self.log.debug("req = %s\n-------\n fid=%s\n" % (req,fid))
108        allocID, alloc_cert = generate_fedid(subj="alloc", log=self.log)
109        aid = unicode(allocID)
110        self.state_lock.acquire()
111        self.state[aid] = { }
112        self.state[aid]['user'] = 'alwabel'
113        self.state[aid]['owners'] = 'alwabel'
114        self.state[aid]['auth'] = set()
115        self.write_state()
116        self.state_lock.release()
117        #self.log.debug("to_dict() = %s " % (proof("me","faber","attr",[]).to_dict()));
118        return { 'allocID': { 'fedid': allocID }, 'proof' : proof("me", "faber","attr",[]).to_dict() }
119    def ReleaseAccess(self, req, fid):
120        self.log.debug("request releasing access!");
121    def StartSegment(self, req, fid):
122        #self.log.debug("data = %s" %(req));
123
124        try:
125            req = req['StartSegmentRequestBody']
126            #print req;
127            # Get the request topology.  If not present, a KeyError is thrown.
128            topref = req['segmentdescription']['topdldescription']
129            #self.log.debug("topref = %s " % (topref));
130            # The fedid of the allocation we're attaching resources to
131            auth_attr = req['allocID']['fedid']
132        except KeyError:
133            raise service_error(server_error.req, "Badly formed request")
134
135        # String version of the allocation ID for keying
136        aid = "%s" % auth_attr
137        # Authorization check
138        access_ok, proof = self.auth.check_attribute(fid, auth_attr, 
139                with_proof=True)
140        #self.log.debug("lllllacc = %s, proffe= %s" %( access_ok,proof.to_dict()))
141        #CAUSIO XXXX
142        if access_ok:
143            raise service_error(service_error.access, "Access denied", 
144                    proof=proof)
145        else:
146            # See if this is a replay of an earlier succeeded StartSegment -
147            # sometimes SSL kills 'em.  If so, replay the response rather than
148            # redoing the allocation.
149            self.state_lock.acquire()
150            #retval = self.state[aid].get('started', None)
151            #CAUSOION
152            retval = 0
153            self.state_lock.release()
154            if retval:
155                self.log.warning(
156                        "[StartSegment] Duplicate StartSegment for %s: " \
157                                % aid + \
158                        "replaying response")
159                return retval
160
161        certfile = "%s/%s.pem" % (self.certdir, aid)
162
163        # Convert the topology into topdl data structures.  Again, the
164        # skeletion doesn't do anything with it, but this is how one parses a
165        # topology request.
166        if topref: topo = topdl.Topology(**topref)
167        else:
168            raise service_error(service_error.req, 
169                    "Request missing segmentdescription'")
170        #rmanager = "192.168.1.10:1234"
171        #smanager = "192.168.1.10:1240"
172        #pmanager = "192.168.1.10:1242"
173        #fncp = "192.168.1.10"
174        #tftpdman="192.168.1.10"
175        #wolagent="192.168.1.10:5959:192.168.0.0/16"
176        #encd="192.168.1.21"
177        #user = "alwabel"
178        #project = "lace"
179
180        k = klanguage(self.rmanager,self.smanager,self.pmanager,self.fncp,self.user,self.project,self.tftpdman,self.wolagent,self.encd)
181        k.k_from_topology(topo)
182        level, kname = tempfile.mkstemp()
183        k.to_file(kname)
184        pid = subprocess.Popen(['/usr/local/springos/bin/kuma', '-p',self.rpassword,kname])
185        #pid = subprocess.Popen([sys.executable, ['/usr/local/springos/bin/kuma', '-p',self.rpassword,kname] ])
186        #os.unlink(kname)
187        # The attributes of the request.  Not used by this plug-in, but that's
188        # where they are.
189        attrs = req.get('fedAttr', [])
190
191        # Gather connection information.  Used to send messages to those
192        # waiting.
193        connInfo = req.get('connection', [])
194
195        self.state_lock.acquire()
196        # It's possible that the StartSegment call gets retried (!).
197        # if the 'started' key is in the allocation, we'll return it rather
198        # than redo the setup.  The integer allocation was saved when we made
199        # it.
200        self.state[aid]['started'] = { 
201                'allocID': req['allocID'],
202                'allocationLog': "Allocatation complete",
203                'segmentdescription': { 'topdldescription': topo.to_dict() },
204                #'proof' :  proof("me", "faber","attr",[]).to_dict(),
205                'proof': proof.to_dict(),
206                }
207        retval = copy.deepcopy(self.state[aid]['started'])
208        self.write_state()
209        self.state_lock.release()
210
211        return retval
212
213    def TerminateSegment(self, req, fid):
214        self.log.debug("Terminate Segment");
215        """
216        Remove the resources associated with th eallocation and stop the music.
217        In this example, this simply means removing the integer we allocated.
218        """
219        # Gather the same access information as for Start Segment
220        try:
221            req = req['TerminateSegmentRequestBody']
222        except KeyError:
223            raise service_error(server_error.req, "Badly formed request")
224
225        auth_attr = req['allocID']['fedid']
226        aid = "%s" % auth_attr
227
228        self.log.debug("Terminate request for %s" %aid)
229        # Check authorization
230        access_ok, proof = self.auth.check_attribute(fid, auth_attr, 
231                with_proof=True)
232        if not access_ok:
233            raise service_error(service_error.access, "Access denied", 
234                    proof=proof)
235
236        # Authorized: remove the integer from the allocation.  A more complex
237        # plug in would interface with the underlying facility to turn off the
238        # experiment here.
239        self.state_lock.acquire()
240        if aid in self.state:
241            assigned = self.state[aid].get('integer', None)
242            self.available_ints.add(assigned)
243            if 'integer' in self.state[aid]:
244                del self.state[aid]['integer']
245            self.write_state()
246        self.state_lock.release()
247   
248        return { 'allocID': req['allocID'], 'proof': proof.to_dict() }
Note: See TracBrowser for help on using the repository browser.