From 70f233e2aed245423d6fcb23697fabc992cec94f Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Thu, 9 Apr 2009 01:09:00 +0000 Subject: [PATCH] added some more methods --- geni/methods/__init__.py | 5 ++ geni/methods/create_gid.py | 46 +++++++++++++++ geni/methods/get_credential.py | 6 +- geni/methods/get_ticket.py | 72 +++++++++++++++++++++++ geni/methods/list.py | 2 +- geni/methods/register.py | 104 +++++++++++++++++++++++++++++++++ geni/methods/remove.py | 67 +++++++++++++++++++++ geni/methods/resolve.py | 2 +- geni/methods/update.py | 90 ++++++++++++++++++++++++++++ 9 files changed, 388 insertions(+), 6 deletions(-) create mode 100644 geni/methods/create_gid.py create mode 100644 geni/methods/get_ticket.py create mode 100644 geni/methods/register.py create mode 100644 geni/methods/remove.py create mode 100644 geni/methods/update.py diff --git a/geni/methods/__init__.py b/geni/methods/__init__.py index ba307207..d6e2da91 100644 --- a/geni/methods/__init__.py +++ b/geni/methods/__init__.py @@ -1,5 +1,10 @@ methods=""" +create_gid get_credential +get_ticket list +register +remove resolve +update """.split() diff --git a/geni/methods/create_gid.py b/geni/methods/create_gid.py new file mode 100644 index 00000000..7e8b6fda --- /dev/null +++ b/geni/methods/create_gid.py @@ -0,0 +1,46 @@ +from geni.util.faults import * +from geni.util.excep import * +from geni.util.method import Method +from geni.util.parameter import Parameter, Mixed +from geni.util.auth import Auth +from geni.util.cert import Keypair + +class create_gid(Method): + """ + Create a new GID. For MAs and SAs that are physically located on the + registry, this allows a owner/operator/PI to create a new GID and have it + signed by his respective authority. + + @param cred credential of caller + @param name hrn for new GID + @param uuid unique identifier for new GID + @param pkey_string public-key string (TODO: why is this a string and not a keypair object?) + + @return the string representation of a GID object + """ + + interfaces = ['registry'] + + accepts = [ + Parameter(str, "Credential string"), + Parameter(str, "Human readable name (hrn)"), + Mixed(Parameter(str, "Unique identifier for new GID (uuid)"), + Parameter(None, "Unique identifier (uuid) not specified")), + Parameter(str, "public-key string") + ] + + returns = Parameter(str, "String represeneation of a GID object") + + def call(self, cred, hrn, uuid, pubkey_str): + self.api.auth.decode_authentication(cred, "getcredential") + self.api.auth.verify_object_belongs_to_me(hrn) + self.api.auth.verify_object_permission(hrn) + + if uuid == None: + uuid = create_uuid() + + pkey = Keypair() + pkey.load_pubkey_from_string(pubkey_str) + gid = self.api.auth.hierarchy.create_gid(name, uuid, pkey) + + return gid.save_to_string(save_parents=True) diff --git a/geni/methods/get_credential.py b/geni/methods/get_credential.py index 125bd125..2ad9f3ae 100644 --- a/geni/methods/get_credential.py +++ b/geni/methods/get_credential.py @@ -28,23 +28,21 @@ class get_credential(Method): Parameter(str, "Human readable name (hrn)") ] - returns = [GeniRecord] + returns = Parameter(str, "String representation of a credential object") def call(self, cred, type, hrn): if not cred: return self.get_self_credential(type, hrn) self.api.auth.check(cred, 'getcredential') - self.api.auth.verify_object_belongs_to_me(name) - auth_hrn = self.api.auth.get_authority(hrn) if not auth_hrn: auth_hrn = hrn auth_info = self.api.auth.get_auth_info(auth_hrn) record = None table = self.api.auth.get_auth_table(auth_hrn) - records = table.resolve('*', auth_hrn) + records = table.resolve('*', hrn) # verify_cancreate_credential requires that the member lists # (researchers, pis, etc) be filled in diff --git a/geni/methods/get_ticket.py b/geni/methods/get_ticket.py new file mode 100644 index 00000000..2f53c374 --- /dev/null +++ b/geni/methods/get_ticket.py @@ -0,0 +1,72 @@ +from geni.util.faults import * +from geni.util.excep import * +from geni.util.method import Method +from geni.util.parameter import Parameter, Mixed +from geni.util.auth import Auth +from geni.util.cert import Keypair +from geni.util.geniticket import * + +class get_ticket(Method): + """ + Retrieve a ticket. This operation is currently implemented on PLC + only (see SFA, engineering decisions); it is not implemented on + components. + + The ticket is filled in with information from the PLC database. This + information includes resources, and attributes such as user keys and + initscripts. + + @param cred credential string + @param name name of the slice to retrieve a ticket for + @param rspec resource specification dictionary + + @return the string representation of a ticket object + """ + + interfaces = ['registry'] + + accepts = [ + Parameter(str, "Credential string"), + Parameter(str, "Human readable name of slice to retrive a ticket for (hrn)"), + Parameter(str, "Resource specification (rspec)") + ] + + returns = Parameter(str, "String represeneation of a ticket object") + + def call(self, cred, hrn, rspec): + self.api.auth.decode_authentication(cred, "getticket") + self.api.auth.verify_object_belongs_to_me(hrn) + self.api.auth.verify_object_permission(name) + + # XXX much of this code looks like get_credential... are they so similar + # that they should be combined? + + auth_hrn = self.api.auth.get_authority(hrn) + if not auth_hrn: + auth_hrn = hrn + auth_info = self.api.auth.get_auth_info(auth_hrn) + record = None + table = self.api.auth.get_auth_table(auth_hrn) + records = table.resolve('slice', hrn) + + object_gid = record.get_gid_object() + new_ticket = Ticket(subject = object_gid.get_subject()) + new_ticket.set_gid_caller(self.client_gid) + new_ticket.set_gid_object(object_gid) + new_ticket.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn) + new_ticket.set_pubkey(object_gid.get_pubkey()) + + self.api.fill_record_info(record) + + (attributes, rspec) = self.api.record_to_slice_info(record) + + new_ticket.set_attributes(attributes) + new_ticket.set_rspec(rspec) + + new_ticket.set_parent(self.api.auth.hierarchy.get_auth_ticket(auth_hrn)) + + new_ticket.encode() + new_ticket.sign() + + return new_ticket.save_to_string(save_parents=True) + diff --git a/geni/methods/list.py b/geni/methods/list.py index a136abfc..641e4db3 100644 --- a/geni/methods/list.py +++ b/geni/methods/list.py @@ -17,7 +17,7 @@ class list(Method): interfaces = ['registry'] accepts = [ - Parameter(str, "credential"), + Parameter(str, "Credential string"), Parameter(str, "Human readable name (hrn)") ] diff --git a/geni/methods/register.py b/geni/methods/register.py new file mode 100644 index 00000000..e103fea3 --- /dev/null +++ b/geni/methods/register.py @@ -0,0 +1,104 @@ +from geni.util.faults import * +from geni.util.excep import * +from geni.util.method import Method +from geni.util.parameter import Parameter, Mixed +from geni.util.auth import Auth +from geni.util.record import GeniRecord +from geni.util.debug import log + +class register(Method): + """ + Register an object with the registry. In addition to being stored in the + Geni database, the appropriate records will also be created in the + PLC databases + + @param cred credential string + @param record_dict dictionary containing record fields + + @return gid string representation + """ + + interfaces = ['registry'] + + accepts = [ + Parameter(str, "Credential string"), + Parameter(dict, "Record dictionary containing record fields") + ] + + returns = Parameter(int, "String representation of gid object") + + def call(self, cred, record_dict): + self.decode_authentication(cred, "register") + record = GeniRecord(dict = record_dict) + type = record.get_type() + name = record.get_name() + auth_name = self.api.auth.get_authority(name) + self.api.auth.verify_object_permission(auth_name) + auth_info = self.api.auth.get_auth_info(auth_name) + table = self.api.auth.get_auth_table(auth_name) + pkey = None + + # check if record already exists + existing_records = table.resolve(type, name) + if existing_records: + raise ExistingRecord(name) + + geni_fields = record.get_geni_info() + pl_fields = record.get_pl_info() + + if (type == "sa") or (type=="ma"): + # update the tree + if not self.api.auth.hierarchy.auth_exists(name): + self.api.auth.hierarchy.create_auth(name) + + # authorities are special since they are managed by the registry + # rather than by the caller. We create our own GID for the + # authority rather than relying on the caller to supply one. + + # get the GID from the newly created authority + child_auth_info = self.api.auth.get_auth_info(name) + gid = auth_info.get_gid_object() + record.set_gid(gid.save_to_string(save_parents=True)) + + # if registering a sa, see if a ma already exists + # if registering a ma, see if a sa already exists + if (type == "sa"): + other_rec = table.resolve("ma", record.get_name()) + elif (type == "ma"): + other_rec = table.resolve("sa", record.get_name()) + + if other_rec: + print >> log, "linking ma and sa to the same plc site" + pointer = other_rec[0].get_pointer() + else: + self.api.geni_fields_to_pl_fields(type, name, geni_fields, pl_fields) + print >> log, "adding site with fields", pl_fields + pointer = self.api.plshell.AddSite(self.api.plauth, pl_fields) + + record.set_pointer(pointer) + + elif (type == "slice"): + self.api.geni_fields_to_pl_fields(type, name, geni_fields, pl_fields) + pointer = self.api.plshell.AddSlice(self.api.plauth, pl_fields) + record.set_pointer(pointer) + + elif (type == "user"): + self.api.geni_fields_to_pl_fields(type, name, geni_fields, pl_fields) + pointer = self.api.plshell.AddPerson(self.api.plauth, pl_fields) + record.set_pointer(pointer) + + elif (type == "node"): + self.api.geni_fields_to_pl_fields(type, name, geni_fields, pl_fields) + login_base = self.api.hrn_to_pl_login_base(auth_name) + pointer = self.api.plshell.AddNode(self.api.plauth, login_base, pl_fields) + record.set_pointer(pointer) + + else: + raise UnknownGeniType(type) + + table.insert(record) + + # update membership for researchers, pis, owners, operators + self.api.update_membership(None, record) + + return record.get_gid_object().save_to_string(save_parents=True) diff --git a/geni/methods/remove.py b/geni/methods/remove.py new file mode 100644 index 00000000..bf22d71a --- /dev/null +++ b/geni/methods/remove.py @@ -0,0 +1,67 @@ +from geni.util.faults import * +from geni.util.excep import * +from geni.util.method import Method +from geni.util.parameter import Parameter, Mixed +from geni.util.auth import Auth +from geni.util.record import GeniRecord +from geni.util.debug import log + +class remove(Method): + """ + Remove an object from the registry. If the object represents a PLC object, + then the PLC records will also be removed. + + @param cred credential string + @param type record type + @param hrn human readable name of record to remove + + @return 1 if successful, faults otherwise + """ + + interfaces = ['registry'] + + accepts = [ + Parameter(str, "Credential string"), + Parameter(str, "Record type") + Parameter(str, "Human readable name (hrn) of record to be removed") + ] + + returns = Parameter(int, "1 if successful") + + def call(self, cred, type, hrn): + self.api.auth.decode_authentication(cred, "remove") + self.api.auth.verify_object_permission(hrn) + auth_name = self.api.auth.get_authority(hrn) + table = self.api.auth.get_auth_table(auth_name) + record_list = table.resolve(type, hrn) + if not record_list: + raise RecordNotFound(hrn) + record = record_list[0] + + # TODO: sa, ma + if type == "user": + self.api.plshell.DeletePerson(self.api.plauth, record.get_pointer()) + elif type == "slice": + self.api.plshell.DeleteSlice(self.api.plauth, record.get_pointer()) + elif type == "node": + self.api.plshell.DeleteNode(self.api.plauth, record.get_pointer()) + elif (type == "sa") or (type == "ma"): + if (type == "sa"): + other_rec = table.resolve("ma", record.get_name()) + elif (type == "ma"): + other_rec = table.resolve("sa", record.get_name()) + + if other_rec: + # sa and ma both map to a site, so if we are deleting one + # but the other still exists, then do not delete the site + print >> log, "not removing site", record.get_name(), "because either sa or ma still exists" + pass + else: + print >> log, "removing site", record.get_name() + self.api.plshell.DeleteSite(self.api.plauth, record.get_pointer()) + else: + raise UnknownGeniType(type) + + table.remove(record) + + return 1 diff --git a/geni/methods/resolve.py b/geni/methods/resolve.py index 3daee240..a8acd247 100644 --- a/geni/methods/resolve.py +++ b/geni/methods/resolve.py @@ -18,7 +18,7 @@ class resolve(Method): interfaces = ['registry'] accepts = [ - Parameter(str, "credential"), + Parameter(str, "Credential string"), Parameter(str, "Human readable name (hrn)") ] diff --git a/geni/methods/update.py b/geni/methods/update.py new file mode 100644 index 00000000..5a0bfa67 --- /dev/null +++ b/geni/methods/update.py @@ -0,0 +1,90 @@ +from geni.util.faults import * +from geni.util.excep import * +from geni.util.method import Method +from geni.util.parameter import Parameter, Mixed +from geni.util.auth import Auth +from geni.util.record import GeniRecord +from geni.util.debug import log + +class update(Method): + """ + Update an object in the registry. Currently, this only updates the + PLC information associated with the record. The Geni fields (name, type, + GID) are fixed. + + The record is expected to have the pl_info field filled in with the data + that should be updated. + + TODO: The geni_info member of the record should be parsed and the pl_info + adjusted as necessary (add/remove users from a slice, etc) + + @param cred credential string specifying rights of the caller + @param record a record dictionary to be updated + + @return 1 if successful, faults otherwise + """ + + interfaces = ['registry'] + + accepts = [ + Parameter(str, "Credential string"), + Parameter(dict, "Record dictionary to be updated") + ] + + returns = Parameter(int, "1 if successful") + + def call(self, cred, record_dict): + self.api.auth.decode_authentication(cred, "update") + record = GeniRecord(dict = record_dict) + type = record.get_type() + self.api.auth.verify_object_permission(record.get_name()) + auth_name = self.api.auth.get_authority(record.get_name()) + if not auth_name: + auth_name = record.get_name() + table = self.api.auth.get_auth_table(auth_name) + + # make sure the record exists + existing_record_list = table.resolve(type, record.get_name()) + if not existing_record_list: + raise RecordNotFound(record.get_name()) + existing_record = existing_record_list[0] + + # Update_membership needs the membership lists in the existing record + # filled in, so it can see if members were added or removed + self.api.fill_record_info(existing_record) + + # Use the pointer from the existing record, not the one that the user + # gave us. This prevents the user from inserting a forged pointer + pointer = existing_record.get_pointer() + + # update the PLC information that was specified with the record + + if (type == "sa") or (type == "ma"): + self.api.plshell.UpdateSite(self.api.plauth, pointer, record.get_pl_info()) + + elif type == "slice": + self.api.plshell.UpdateSlice(self.api.plauth, pointer, record.get_pl_info()) + + elif type == "user": + # SMBAKER: UpdatePerson only allows a limited set of fields to be + # updated. Ideally we should have a more generic way of doing + # this. I copied the field names from UpdatePerson.py... + update_fields = {} + all_fields = record.get_pl_info() + for key in all_fields.keys(): + if key in ['first_name', 'last_name', 'title', 'email', + 'password', 'phone', 'url', 'bio', 'accepted_aup', + 'enabled']: + update_fields[key] = all_fields[key] + self.api.plshell.UpdatePerson(self.api.plauth, pointer, update_fields) + + elif type == "node": + self.api.plshell.UpdateNode(self.api.plauth, pointer, record.get_pl_info()) + + else: + raise UnknownGeniType(type) + + # update membership for researchers, pis, owners, operators^M + self.api.update_membership(existing_record, record) + + return 1 -- 2.43.0