#!/usr/local/bin/python import os,sys import subprocess import re import string import copy import pickle import logging import random import tempfile from federation.util import * from federation.fedid import fedid, generate_fedid from federation.authorizer import authorizer from federation.service_error import service_error from federation.remote_service import xmlrpc_handler, soap_handler, service_caller import federation.topdl as topdl from federation.access import access_base from federation.legacy_access import legacy_access from federation.proof import proof from topdltok import klanguage # Make log messages disappear if noone configures a fedd logger. This is # something of an incantation, but basically it creates a logger object # registered to fedd.access if no other module above us has. It's an extra # belt for the suspenders. class nullHandler(logging.Handler): def emit(self, record): pass fl = logging.getLogger("fedd.access") fl.addHandler(nullHandler()) # The plug-in itself. class access(access_base): @staticmethod def parse_access_string(s): """ Parse a parenthesized string from the access db by removing the parens. If the string isn't in parens, we just return it with whitespace trimmed in either case. """ st = s.strip() if st.startswith("(") and st.endswith(")"): return st[1:-1] else: return st def __init__(self, config=None, auth=None): self.rmanager = config.get('definition','rmanager') self.smanager = config.get('definition','smanager') self.pmanager = config.get('definition','pmanager') self.fncp = config.get('definition','fncp') self.tftpdman =config.get('definition','tftpdman') self.wolagent = config.get('definition','wolagent') self.encd = config.get('definition','encd') self.user = config.get('definition','user') self.project= config.get('definition','project') self.rpassword = config.get('definition','rpassword') access_base.__init__(self, config, auth) # authorization information self.auth_type = config.get('access', 'auth_type') \ or 'legacy' self.auth_dir = config.get('access', 'auth_dir') accessdb = config.get("access", "accessdb") print "authentication type = %s" %(self.auth_type); if self.auth_type == 'starbed': self.log.debug("starbed authtication methond"); else: raise service_error(service_error.internal, "Unknown auth_type: %s" % self.auth_type) # These dictionaries register the plug-in's local routines for handline # these four messages with the server code above. There's a version # for SOAP and XMLRPC, depending on which interfaces the plugin # supports. There's rarely a technical reason not to support one or # the other - the plugin code almost never deals with the transport - # but if a plug-in writer wanted to disable XMLRPC, they could leave # the self.xmlrpc_services dictionary empty. self.soap_services = {\ 'RequestAccess': soap_handler("RequestAccess", self.RequestAccess), 'ReleaseAccess': soap_handler("ReleaseAccess", self.ReleaseAccess), 'StartSegment': soap_handler("StartSegment", self.StartSegment), 'TerminateSegment': soap_handler("TerminateSegment", self.TerminateSegment), } self.xmlrpc_services = {\ 'RequestAccess': xmlrpc_handler('RequestAccess', self.RequestAccess), 'ReleaseAccess': xmlrpc_handler('ReleaseAccess', self.ReleaseAccess), 'StartSegment': xmlrpc_handler("StartSegment", self.StartSegment), 'TerminateSegment': xmlrpc_handler('TerminateSegment', self.TerminateSegment), } self.log.debug("Starbed AC started!") # RequestAccess and ReleaseAccess come from the base class def RequestAccess(self, req, fid): # The dance to get into the request body if req.has_key('RequestAccessRequestBody'): req = req['RequestAccessRequestBody'] else: raise service_error(service_error.req, "No request!?") #self.log.debug("req = %s\n-------\n fid=%s\n" % (req,fid)) allocID, alloc_cert = generate_fedid(subj="alloc", log=self.log) aid = unicode(allocID) self.state_lock.acquire() self.state[aid] = { } self.state[aid]['user'] = 'alwabel' self.state[aid]['owners'] = 'alwabel' self.state[aid]['auth'] = set() self.write_state() self.state_lock.release() #self.log.debug("to_dict() = %s " % (proof("me","faber","attr",[]).to_dict())); return { 'allocID': { 'fedid': allocID }, 'proof' : proof("me", "faber","attr",[]).to_dict() } def ReleaseAccess(self, req, fid): self.log.debug("request releasing access!"); def StartSegment(self, req, fid): #self.log.debug("data = %s" %(req)); try: req = req['StartSegmentRequestBody'] #print req; # Get the request topology. If not present, a KeyError is thrown. topref = req['segmentdescription']['topdldescription'] #self.log.debug("topref = %s " % (topref)); # The fedid of the allocation we're attaching resources to auth_attr = req['allocID']['fedid'] except KeyError: raise service_error(server_error.req, "Badly formed request") # String version of the allocation ID for keying aid = "%s" % auth_attr # Authorization check access_ok, proof = self.auth.check_attribute(fid, auth_attr, with_proof=True) #self.log.debug("lllllacc = %s, proffe= %s" %( access_ok,proof.to_dict())) #CAUSIO XXXX if access_ok: raise service_error(service_error.access, "Access denied", proof=proof) else: # See if this is a replay of an earlier succeeded StartSegment - # sometimes SSL kills 'em. If so, replay the response rather than # redoing the allocation. self.state_lock.acquire() #retval = self.state[aid].get('started', None) #CAUSOION retval = 0 self.state_lock.release() if retval: self.log.warning( "[StartSegment] Duplicate StartSegment for %s: " \ % aid + \ "replaying response") return retval certfile = "%s/%s.pem" % (self.certdir, aid) # Convert the topology into topdl data structures. Again, the # skeletion doesn't do anything with it, but this is how one parses a # topology request. if topref: topo = topdl.Topology(**topref) else: raise service_error(service_error.req, "Request missing segmentdescription'") #rmanager = "192.168.1.10:1234" #smanager = "192.168.1.10:1240" #pmanager = "192.168.1.10:1242" #fncp = "192.168.1.10" #tftpdman="192.168.1.10" #wolagent="192.168.1.10:5959:192.168.0.0/16" #encd="192.168.1.21" #user = "alwabel" #project = "lace" k = klanguage(self.rmanager,self.smanager,self.pmanager,self.fncp,self.user,self.project,self.tftpdman,self.wolagent,self.encd) k.k_from_topology(topo) level, kname = tempfile.mkstemp() k.to_file(kname) pid = subprocess.Popen(['/usr/local/springos/bin/kuma', '-p',self.rpassword,kname]) #pid = subprocess.Popen([sys.executable, ['/usr/local/springos/bin/kuma', '-p',self.rpassword,kname] ]) #os.unlink(kname) # The attributes of the request. Not used by this plug-in, but that's # where they are. attrs = req.get('fedAttr', []) # Gather connection information. Used to send messages to those # waiting. connInfo = req.get('connection', []) self.state_lock.acquire() # It's possible that the StartSegment call gets retried (!). # if the 'started' key is in the allocation, we'll return it rather # than redo the setup. The integer allocation was saved when we made # it. self.state[aid]['started'] = { 'allocID': req['allocID'], 'allocationLog': "Allocatation complete", 'segmentdescription': { 'topdldescription': topo.to_dict() }, #'proof' : proof("me", "faber","attr",[]).to_dict(), 'proof': proof.to_dict(), } retval = copy.deepcopy(self.state[aid]['started']) self.write_state() self.state_lock.release() return retval def TerminateSegment(self, req, fid): self.log.debug("Terminate Segment"); """ Remove the resources associated with th eallocation and stop the music. In this example, this simply means removing the integer we allocated. """ # Gather the same access information as for Start Segment try: req = req['TerminateSegmentRequestBody'] except KeyError: raise service_error(server_error.req, "Badly formed request") auth_attr = req['allocID']['fedid'] aid = "%s" % auth_attr self.log.debug("Terminate request for %s" %aid) # Check authorization access_ok, proof = self.auth.check_attribute(fid, auth_attr, with_proof=True) if not access_ok: raise service_error(service_error.access, "Access denied", proof=proof) # Authorized: remove the integer from the allocation. A more complex # plug in would interface with the underlying facility to turn off the # experiment here. self.state_lock.acquire() if aid in self.state: assigned = self.state[aid].get('integer', None) self.available_ints.add(assigned) if 'integer' in self.state[aid]: del self.state[aid]['integer'] self.write_state() self.state_lock.release() return { 'allocID': req['allocID'], 'proof': proof.to_dict() }