#!/usr/local/bin/python import os,sys from M2Crypto.SSL import SSLError from M2Crypto.m2xmlrpclib import SSL_Transport from service_error import service_error from xmlrpclib import ServerProxy, Fault, Error from protogeni_proxy import protogeni_proxy from deter import topdl import list_log class geniapi_proxy(protogeni_proxy): ''' This exports the same interface as the protogeni_proxy, but uses GENIAPI calls when avaliable. These calls are currently only available only for the component manager/aggregate manager. The keyword arguments of the ProtoGENI interface are converted into XMLRPC positional parameters on GENIAPI calls. Tables to do that are established in the constructior. ''' def __init__(self, log=None, keyfile=None, debug=False, ch_url=None, sa_url=None, cm_url=None): protogeni_proxy.__init__(self, log, keyfile, debug, ch_url, sa_url, cm_url) def get_key(k, d): ''' Return a function that takes a dict and returns the value bound to key k or the default d ''' return lambda x : x.get(k, d) def user_filter(params): ''' Take a dict assumed to hold a key 'users' that is a dict of a urn (keyed by 'urn' and a list of dicts describing ssh keys (keyed by 'key'. Construct a list of dicts with the same 'urn' and a 'keys' value holding only the 'key' value of the subdict. This converts ProtoGENI users to GENIAPI users. ''' # That conversion is a nested list comprehension. return [ { 'urn': u.get('urn', ''), 'keys': [ k.get('key', '') for k in u.get('keys', []) ] } for u in params.get('users', [])] self.ProtoGENIError = protogeni_proxy.ProtoGENIError # These ProtoGENI requests are mapped to the given GENIAPI request. self.method_map = { 'DeleteSlice': 'DeleteSliver', 'RenewSlice': 'RenewSliver', } # Conversion tables for changing the dict of keyword arguments into a # list of arguments for GENIAPI. The list of positional params is # created by calling the functions in the dict below (keyed by the # requested function) in order on the params dict, placing the results # into the arguments list. The list so constructed is the positional # parameters in order. Mostly this is just putting the right key in # the right place, but the users choice is actually reformatted some. # This dict is indexed by ProtoGENI method. self.arg_map = { 'CreateSliver': [ get_key('slice_urn', ''), get_key('credentials', []), get_key('rspec', ''), user_filter ], 'SliverStatus': [ get_key('slice_urn', ''), get_key('credentials','') ], 'DeleteSlice': [ get_key('slice_urn', ''), get_key('credentials','') ], 'RenewSlice': [ get_key('slice_urn', ''), get_key('credentials','') ], } def geniapi_call(self, url, method, params, context): ''' Make an SSL authenticated (and encrypted) GENIAPI call on the given url. ''' try: if self.log: self.log.debug("Calling %s %s" % (url, method)) transport = SSL_Transport(context) port = ServerProxy(url, transport=transport) remote_method = getattr(port, method, None) if remote_method is not None: return remote_method(*params) else: raise service_error(service_error.internal, "Bad method: %s" % method) except Fault, f: raise service_error(f.faultCode, f.faultString) except Error, e: raise service_error(service_error.protocol, "Remote XMLRPC Fault: %s" % e) except: raise self.ProtoGENIError(op=method, code=-1, output="%s" % sys.exc_info()[1]) def component_manager_call(self, method, params, context): ''' Convert the ProtoGENI call into a GENIAPI call using the translation tables above. ''' if not self.debug: gmethod = self.method_map.get(method, method) gparams = [f(params) for f in self.arg_map.get(method, [])] return self.geniapi_call(self.cm_url, gmethod, gparams, context) else: if method in self.debug_fail: raise self.ProtoGENIError(op=method, code='unknown', output='No output') elif self.debug_response.has_key(method): return self.debug_response[method] else: return "%s XML blob" % method