added some more methods
authorTony Mack <tmack@cs.princeton.edu>
Thu, 9 Apr 2009 01:09:00 +0000 (01:09 +0000)
committerTony Mack <tmack@cs.princeton.edu>
Thu, 9 Apr 2009 01:09:00 +0000 (01:09 +0000)
geni/methods/__init__.py
geni/methods/create_gid.py [new file with mode: 0644]
geni/methods/get_credential.py
geni/methods/get_ticket.py [new file with mode: 0644]
geni/methods/list.py
geni/methods/register.py [new file with mode: 0644]
geni/methods/remove.py [new file with mode: 0644]
geni/methods/resolve.py
geni/methods/update.py [new file with mode: 0644]

index ba30720..d6e2da9 100644 (file)
@@ -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 (file)
index 0000000..7e8b6fd
--- /dev/null
@@ -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)
index 125bd12..2ad9f3a 100644 (file)
@@ -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 (file)
index 0000000..2f53c37
--- /dev/null
@@ -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)
+        
index a136abf..641e4db 100644 (file)
@@ -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 (file)
index 0000000..e103fea
--- /dev/null
@@ -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 (file)
index 0000000..bf22d71
--- /dev/null
@@ -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
index 3daee24..a8acd24 100644 (file)
@@ -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 (file)
index 0000000..5a0bfa6
--- /dev/null
@@ -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