X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fplc%2Fapi.py;h=b5c9ce4986b50356a2ff02486a3687e58b157b98;hb=3d7237fa0b5f2b4a60cb97c7fb3b6aecfd94558a;hp=9ca932bbc58a16fbda7a2f0c4b6e124230b57c20;hpb=357a9bfcce9d8d6edd4c4e33354c8eb2e0522072;p=sfa.git diff --git a/sfa/plc/api.py b/sfa/plc/api.py index 9ca932bb..b5c9ce49 100644 --- a/sfa/plc/api.py +++ b/sfa/plc/api.py @@ -1,5 +1,5 @@ # -# Geniwrapper XML-RPC and SOAP interfaces +# SFA XML-RPC and SOAP interfaces # ### $Id$ ### $URL$ @@ -10,100 +10,30 @@ import os import traceback import string import xmlrpclib - from sfa.trust.auth import Auth from sfa.util.config import * from sfa.util.faults import * from sfa.util.debug import * from sfa.trust.rights import * from sfa.trust.credential import * -from sfa.util.misc import * +from sfa.trust.certificate import * +from sfa.util.namespace import * +from sfa.util.api import * +from sfa.util.nodemanager import NodeManager from sfa.util.sfalogging import * -from sfa.util.genitable import * +from sfa.util.table import SfaTable -# See "2.2 Characters" in the XML specification: -# -# #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] -# avoiding -# [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF] - -invalid_xml_ascii = map(chr, range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F)) -xml_escape_table = string.maketrans("".join(invalid_xml_ascii), "?" * len(invalid_xml_ascii)) - -def xmlrpclib_escape(s, replace = string.replace): - """ - xmlrpclib does not handle invalid 7-bit control characters. This - function augments xmlrpclib.escape, which by default only replaces - '&', '<', and '>' with entities. - """ - - # This is the standard xmlrpclib.escape function - s = replace(s, "&", "&") - s = replace(s, "<", "<") - s = replace(s, ">", ">",) - - # Replace invalid 7-bit control characters with '?' - return s.translate(xml_escape_table) - -def xmlrpclib_dump(self, value, write): - """ - xmlrpclib cannot marshal instances of subclasses of built-in - types. This function overrides xmlrpclib.Marshaller.__dump so that - any value that is an instance of one of its acceptable types is - marshalled as that type. - - xmlrpclib also cannot handle invalid 7-bit control characters. See - above. - """ - - # Use our escape function - args = [self, value, write] - if isinstance(value, (str, unicode)): - args.append(xmlrpclib_escape) - - try: - # Try for an exact match first - f = self.dispatch[type(value)] - except KeyError: - raise - # Try for an isinstance() match - for Type, f in self.dispatch.iteritems(): - if isinstance(value, Type): - f(*args) - return - raise TypeError, "cannot marshal %s objects" % type(value) - else: - f(*args) - -# You can't hide from me! -xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump - -# SOAP support is optional -try: - import SOAPpy - from SOAPpy.Parser import parseSOAPRPC - from SOAPpy.Types import faultType - from SOAPpy.NS import NS - from SOAPpy.SOAPBuilder import buildSOAP -except ImportError: - SOAPpy = None - - -def import_deep(name): - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - -class GeniAPI: +class SfaAPI(BaseAPI): # flat list of method names import sfa.methods methods = sfa.methods.all - def __init__(self, config = "/etc/sfa/sfa_config", encoding = "utf-8", + def __init__(self, config = "/etc/sfa/sfa_config", encoding = "utf-8", methods='sfa.methods', peer_cert = None, interface = None, key_file = None, cert_file = None): + BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, peer_cert=peer_cert, + interface=interface, key_file=key_file, cert_file=cert_file) + self.encoding = encoding # Better just be documenting the API @@ -115,10 +45,17 @@ class GeniAPI: self.auth = Auth(peer_cert) self.interface = interface self.key_file = key_file + self.key = Keypair(filename=self.key_file) self.cert_file = cert_file + self.cert = Certificate(filename=self.cert_file) self.credential = None - self.plshell = self.getPLCShell() - self.plshell_version = self.getPLCShellVersion() + + # Initialize the PLC shell only if SFA wraps a myPLC + rspec_type = self.config.get_aggregate_rspec_type() + if (rspec_type == 'pl' or rspec_type == 'vini'): + self.plshell = self.getPLCShell() + self.plshell_version = self.getPLCShellVersion() + self.hrn = self.config.SFA_INTERFACE_HRN self.time_format = "%Y-%m-%d %H:%M:%S" self.logger=get_sfa_logger() @@ -128,14 +65,15 @@ class GeniAPI: 'AuthMethod': 'password', 'AuthString': self.config.SFA_PLC_PASSWORD} try: + self.plshell_type = 'direct' import PLC.Shell shell = PLC.Shell.Shell(globals = globals()) shell.AuthCheck(self.plauth) return shell except ImportError: + self.plshell_type = 'xmlrpc' # connect via xmlrpc url = self.config.SFA_PLC_URL - shell = xmlrpclib.Server(url, verbose = 0, allow_none = True) shell.AuthCheck(self.plauth) return shell @@ -161,25 +99,29 @@ class GeniAPI: else: return self.getCredentialFromRegistry() - def getCredentialFromRegistry(self): """ - Get our credential from a remote registry using a geniclient connection + Get our credential from a remote registry """ type = 'authority' - path = self.config.SFA_BASE_DIR + path = self.config.SFA_DATA_DIR filename = ".".join([self.interface, self.hrn, type, "cred"]) cred_filename = path + os.sep + filename try: credential = Credential(filename = cred_filename) - return credential + return credential.save_to_string(save_parents=True) except IOError: from sfa.server.registry import Registries registries = Registries(self) registry = registries[self.hrn] - self_cred = registry.get_credential(None, type, self.hrn) + cert_string=self.cert.save_to_string(save_parents=True) + # get self credential + self_cred = registry.get_self_credential(cert_string, type, self.hrn) + # get credential cred = registry.get_credential(self_cred, type, self.hrn) - cred.save_to_file(cred_filename, save_parents=True) + + # save cred to file + Credential(string=cred).save_to_file(cred_filename, save_parents=True) return cred def getCredentialFromLocalRegistry(self): @@ -194,8 +136,8 @@ class GeniAPI: if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN: auth_hrn = hrn auth_info = self.auth.get_auth_info(auth_hrn) - table = GeniTable() - records = table.find(hrn) + table = SfaTable() + records = table.findObjects(hrn) if not records: raise RecordNotFound record = records[0] @@ -216,7 +158,7 @@ class GeniAPI: new_cred.encode() new_cred.sign() - return new_cred + return new_cred.save_to_string(save_parents=True) def loadCredential (self): @@ -228,22 +170,22 @@ class GeniAPI: # see if this file exists # XX This is really the aggregate's credential. Using this is easier than getting # the registry's credential from iteslf (ssl errors). - ma_cred_filename = self.config.SFA_BASE_DIR + os.sep + self.interface + self.hrn + ".ma.cred" + ma_cred_filename = self.config.SFA_DATA_DIR + os.sep + self.interface + self.hrn + ".ma.cred" try: self.credential = Credential(filename = ma_cred_filename) except IOError: self.credential = self.getCredentialFromRegistry() ## - # Convert geni fields to PLC fields for use when registering up updating + # Convert SFA fields to PLC fields for use when registering up updating # registry record in the PLC database # # @param type type of record (user, slice, ...) # @param hrn human readable name - # @param geni_fields dictionary of geni fields + # @param sfa_fields dictionary of SFA fields # @param pl_fields dictionary of PLC fields (output) - def geni_fields_to_pl_fields(self, type, hrn, record): + def sfa_fields_to_pl_fields(self, type, hrn, record): def convert_ints(tmpdict, int_fields): for field in int_fields: @@ -262,11 +204,13 @@ class GeniAPI: pl_record["url"] = record["url"] if "description" in record: pl_record["description"] = record["description"] + if "expires" in record: + pl_record["expires"] = int(record["expires"]) elif type == "node": if not "hostname" in pl_record: if not "hostname" in record: - raise MissingGeniInfo("hostname") + raise MissingSfaInfo("hostname") pl_record["hostname"] = record["hostname"] if not "model" in pl_record: pl_record["model"] = "geni" @@ -290,7 +234,7 @@ class GeniAPI: def fill_record_pl_info(self, record): """ - Fill in the planetlab specific fields of a Geni record. This + Fill in the planetlab specific fields of a SFA record. This involves calling the appropriate PLC method to retrieve the database record for the object. @@ -309,7 +253,7 @@ class GeniAPI: record.update({}) return - if (type in ["authority", "sa", "ma"]): + if (type in ["authority"]): pl_res = self.plshell.GetSites(self.plauth, [pointer]) elif (type == "slice"): pl_res = self.plshell.GetSlices(self.plauth, [pointer]) @@ -318,7 +262,7 @@ class GeniAPI: elif (type == "node"): pl_res = self.plshell.GetNodes(self.plauth, [pointer]) else: - raise UnknownGeniType(type) + raise UnknownSfaType(type) if not pl_res: raise PlanetLabRecordDoesNotExist(record['hrn']) @@ -361,53 +305,61 @@ class GeniAPI: record.update(pl_record) - def lookup_users(self, user_id_list, role="*"): - table = GeniTable() - record_list = [] - for person_id in user_id_list: - user_records = table.find({'type': 'user', 'pointer': person_id}) - for user_record in user_records: - self.fill_record_info(user_record) - user_roles = user_record.get("roles") - if (role=="*") or (role in user_roles): - record_list.append(user_record['hrn']) - return record_list - - def fill_record_geni_info(self, record): - geni_info = {} + + def fill_record_sfa_info(self, record): + sfa_info = {} type = record['type'] + table = SfaTable() if (type == "slice"): person_ids = record.get("person_ids", []) - researchers = self.lookup_users(person_ids) - geni_info['researcher'] = researchers + persons = table.find({'type': 'user', 'pointer': person_ids}) + researchers = [person['hrn'] for person in persons] + sfa_info['researcher'] = researchers elif (type == "authority"): person_ids = record.get("person_ids", []) - pis = self.lookup_users(person_ids, "pi") - operators = self.lookup_users(person_ids, "tech") - owners = self.lookup_users(person_ids, "admin") - geni_info['pi'] = pis - geni_info['operator'] = operators - geni_info['owner'] = owners + persons = table.find({'type': 'user', 'pointer': person_ids}) + persons_dict = {} + for person in persons: + persons_dict[person['pointer']] = person + pl_persons = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'roles']) + pis, techs, admins = [], [], [] + for person in pl_persons: + pointer = person['person_id'] + + if pointer not in persons_dict: + # this means there is not sfa record for this user + continue + hrn = persons_dict[pointer]['hrn'] + if 'pi' in person['roles']: + pis.append(hrn) + if 'tech' in person['roles']: + techs.append(hrn) + if 'admin' in person['roles']: + admins.append(hrn) + + sfa_info['PI'] = pis + sfa_info['operator'] = techs + sfa_info['owner'] = admins # xxx TODO: OrganizationName elif (type == "node"): - geni_info['dns'] = record.get("hostname", "") + sfa_info['dns'] = record.get("hostname", "") # xxx TODO: URI, LatLong, IP, DNS elif (type == "user"): - geni_info['email'] = record.get("email", "") + sfa_info['email'] = record.get("email", "") # xxx TODO: PostalAddress, Phone - record.update(geni_info) + record.update(sfa_info) def fill_record_info(self, record): """ - Given a geni record, fill in the PLC specific and Geni specific + Given a SFA record, fill in the PLC specific and SFA specific fields in the record. """ self.fill_record_pl_info(record) - self.fill_record_geni_info(record) + self.fill_record_sfa_info(record) def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc): # get a list of the HRNs tht are members of the old and new records @@ -424,7 +376,7 @@ class GeniAPI: # build a list of the new person ids, by looking up each person to get # their pointer newIdList = [] - table = GeniTable() + table = SfaTable() records = table.find({'type': 'user', 'hrn': newList}) for rec in records: newIdList.append(rec['pointer']) @@ -442,13 +394,11 @@ class GeniAPI: # add people who are in the new list, but not the oldList for personId in newIdList: if not (personId in oldIdList): - print "adding id", personId, "to", record.get_name() addFunc(self.plauth, personId, containerId) # remove people who are in the old list, but not the new list for personId in oldIdList: if not (personId in newIdList): - print "removing id", personId, "from", record.get_name() delFunc(self.plauth, personId, containerId) def update_membership(self, oldRecord, record): @@ -461,74 +411,25 @@ class GeniAPI: pass - def callable(self, method): - """ - Return a new instance of the specified method. - """ - # Look up method - if method not in self.methods: - raise GeniInvalidAPIMethod, method - - # Get new instance of method - try: - classname = method.split(".")[-1] - module = __import__("sfa.methods." + method, globals(), locals(), [classname]) - callablemethod = getattr(module, classname)(self) - return getattr(module, classname)(self) - except ImportError, AttributeError: - raise - raise GeniInvalidAPIMethod, method - - def call(self, source, method, *args): - """ - Call the named method from the specified source with the - specified arguments. - """ - function = self.callable(method) - function.source = source - return function(*args) - def handle(self, source, data): - """ - Handle an XML-RPC or SOAP request from the specified source. - """ - # Parse request into method name and arguments - try: - interface = xmlrpclib - (args, method) = xmlrpclib.loads(data) - methodresponse = True - except Exception, e: - if SOAPpy is not None: - interface = SOAPpy - (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1) - method = r._name - args = r._aslist() - # XXX Support named arguments - else: - raise e +class ComponentAPI(BaseAPI): - try: - result = self.call(source, method, *args) - except Exception, fault: - traceback.print_exc(file = log) - # Handle expected faults - if interface == xmlrpclib: - result = fault - methodresponse = None - elif interface == SOAPpy: - result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method) - result._setDetail("Fault %d: %s" % (fault.faultCode, fault.faultString)) - else: - raise - - # Return result - if interface == xmlrpclib: - if not isinstance(result, GeniFault): - result = (result,) - - data = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1) - elif interface == SOAPpy: - data = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding) - - return data + def __init__(self, config = "/etc/sfa/sfa_config", encoding = "utf-8", methods='sfa.methods', + peer_cert = None, interface = None, key_file = None, cert_file = None): + + BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, peer_cert=peer_cert, + interface=interface, key_file=key_file, cert_file=cert_file) + self.encoding = encoding + # Better just be documenting the API + if config is None: + return + + self.nodemanager = NodeManager() + + def sliver_exists(self): + sliver_dict = self.nodemanager.GetXIDs() + if slicename in sliver_dict.keys(): + return True + else: + return False