Merge branch 'master' of ssh://git.planet-lab.org/git/sfa
[sfa.git] / sfa / managers / aggregate_manager_eucalyptus.py
index a38a6e8..09715cc 100644 (file)
@@ -1,9 +1,7 @@
 from __future__ import with_statement 
-from sfa.util.faults import *
-from sfa.util.namespace import *
-from sfa.util.rspec import RSpec
-from sfa.server.registry import Registries
-from sfa.plc.nodes import *
+
+import sys
+import os
 
 import boto
 from boto.ec2.regioninfo import RegionInfo
@@ -13,8 +11,13 @@ from xmlbuilder import XMLBuilder
 from lxml import etree as ET
 from sqlobject import *
 
-import sys
-import os
+from sfa.util.faults import *
+from sfa.util.xrn import urn_to_hrn
+from sfa.util.rspec import RSpec
+from sfa.server.registry import Registries
+from sfa.trust.credential import Credential
+from sfa.plc.api import SfaAPI
+from sfa.util.plxrn import hrn_to_pl_slicename, slicename_to_hrn
 
 ##
 # The data structure used to represent a cloud.
@@ -28,6 +31,10 @@ cloud = {}
 #
 EUCALYPTUS_RSPEC_SCHEMA='/etc/sfa/eucalyptus.rng'
 
+# Quick hack
+sys.stderr = file('/var/log/euca_agg.log', 'a+')
+api = SfaAPI()
+
 ##
 # A representation of an Eucalyptus instance. This is a support class
 # for instance <-> slice mapping.
@@ -45,8 +52,9 @@ class EucaInstance(SQLObject):
     # Contacts Eucalyptus and tries to reserve this instance.
     # 
     # @param botoConn A connection to Eucalyptus.
+    # @param pubKeys A list of public keys for the instance.
     #
-    def reserveInstance(self, botoConn):
+    def reserveInstance(self, botoConn, pubKeys):
         print >>sys.stderr, 'Reserving an instance: image: %s, kernel: ' \
                             '%s, ramdisk: %s, type: %s, key: %s' % \
                             (self.image_id, self.kernel_id, self.ramdisk_id, 
@@ -60,7 +68,8 @@ class EucaInstance(SQLObject):
                                                  kernel_id = self.kernel_id,
                                                  ramdisk_id = self.ramdisk_id,
                                                  instance_type = self.inst_type,
-                                                 key_name  = self.key_pair)
+                                                 key_name  = self.key_pair,
+                                                 user_data = pubKeys)
             for instance in reservation.instances:
                 self.instance_id = instance.id
 
@@ -163,6 +172,42 @@ def getEucaConnection():
                             port=eucaPort,
                             path=srvPath)
 
+##
+# Returns a string of keys that belong to the users of the given slice.
+# @param sliceHRN The hunman readable name of the slice.
+# @return sting()
+#
+def getKeysForSlice(sliceHRN):
+    try:
+        # convert hrn to slice name
+        plSliceName = hrn_to_pl_slicename(sliceHRN)
+    except IndexError, e:
+        print >>sys.stderr, 'Invalid slice name (%s)' % sliceHRN
+        return []
+
+    # Get the slice's information
+    sliceData = api.plshell.GetSlices(api.plauth, {'name':plSliceName})
+    if not sliceData:
+        print >>sys.stderr, 'Cannot get any data for slice %s' % plSliceName
+        return []
+
+    # It should only return a list with len = 1
+    sliceData = sliceData[0]
+
+    keys = []
+    person_ids = sliceData['person_ids']
+    if not person_ids: 
+        print >>sys.stderr, 'No users in slice %s' % sliceHRN
+        return []
+
+    persons = api.plshell.GetPersons(api.plauth, person_ids)
+    for person in persons:
+        pkeys = api.plshell.GetKeys(api.plauth, person['key_ids'])
+        for key in pkeys:
+            keys.append(key['key'])
+    return ''.join(keys)
+
 ##
 # A class that builds the RSpec for Eucalyptus.
 #
@@ -334,9 +379,17 @@ class ZoneResultParser(object):
 
         return clusterList
 
-def get_rspec(api, xrn, origin_hrn):
+def get_rspec(api, creds, options): 
     global cloud
-    hrn = urn_to_hrn(xrn)[0]
+    # get slice's hrn from options
+    xrn = options.get('geni_slice_urn', '')
+    hrn, type = urn_to_hrn(xrn)
+
+    # get hrn of the original caller
+    origin_hrn = options.get('origin_hrn', None)
+    if not origin_hrn:
+        origin_hrn = Credential(string=creds[0]).get_gid_caller().get_hrn()
+
     conn = getEucaConnection()
 
     if not conn:
@@ -365,12 +418,21 @@ def get_rspec(api, xrn, origin_hrn):
             # Get the instances that belong to the given slice from sqlite3
             # XXX use getOne() in production because the slice's hrn is supposed
             # to be unique. For testing, uniqueness is turned off in the db.
-            theSlice = list(Slice.select(Slice.q.slice_hrn == hrn))[-1]
+            # If the slice isn't found in the database, create a record for the 
+            # slice.
+            matchedSlices = list(Slice.select(Slice.q.slice_hrn == hrn))
+            if matchedSlices:
+                theSlice = matchedSlices[-1]
+            else:
+                theSlice = Slice(slice_hrn = hrn)
             for instance in theSlice.instances:
                 instanceId.append(instance.instance_id)
 
             # Get the information about those instances using their ids.
-            reservations = conn.get_all_instances(instanceId)
+            if len(instanceId) > 0:
+                reservations = conn.get_all_instances(instanceId)
+            else:
+                reservations = []
             for reservation in reservations:
                 for instance in reservation.instances:
                     instances.append(instance)
@@ -396,12 +458,17 @@ def get_rspec(api, xrn, origin_hrn):
 
     rspec = EucaRSpecBuilder(cloud).toXML()
 
+    # Remove the instances records so next time they won't 
+    # show up.
+    if 'instances' in cloud:
+        del cloud['instances']
+
     return rspec
 
 """
 Hook called via 'sfi.py create'
 """
-def create_slice(api, xrn, xml):
+def create_slice(api, xrn, creds, xml, users):
     global cloud
     hrn = urn_to_hrn(xrn)[0]
 
@@ -443,6 +510,10 @@ def create_slice(api, xrn, xml):
 
     # Process new instance requests
     requests = rspecXML.findall('.//request')
+    if requests:
+        # Get all the public keys associate with slice.
+        pubKeys = getKeysForSlice(s.slice_hrn)
+        print sys.stderr, "Passing the following keys to the instance:\n%s" % pubKeys
     for req in requests:
         vmTypeElement = req.getparent()
         instType = vmTypeElement.get('name')
@@ -466,20 +537,21 @@ def create_slice(api, xrn, xml):
                                     ramdisk_id = instRamDisk,
                                     key_pair = instKey,
                                     inst_type = instType)
-            eucaInst.reserveInstance(conn)
+            eucaInst.reserveInstance(conn, pubKeys)
 
     return True
 
 def main():
     init_server()
 
-    theRSpec = None
-    with open(sys.argv[1]) as xml:
-        theRSpec = xml.read()
-    create_slice(None, 'planetcloud.pc.test', theRSpec)
+    #theRSpec = None
+    #with open(sys.argv[1]) as xml:
+    #    theRSpec = xml.read()
+    #create_slice(None, 'planetcloud.pc.test', theRSpec)
 
     #rspec = get_rspec('euca', 'planetcloud.pc.test', 'planetcloud.pc.marcoy')
     #print rspec
+    print getKeysForSlice('gc.gc.test1')
 
 if __name__ == "__main__":
     main()