renamed get_rspec into ListResources on the managers side
[sfa.git] / sfa / managers / aggregate_manager_eucalyptus.py
index 82ddb57..8dc604b 100644 (file)
@@ -1,8 +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
+
+import sys
+import os
 
 import boto
 from boto.ec2.regioninfo import RegionInfo
@@ -12,8 +11,14 @@ 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
+from sfa.util.callids import Callids
 
 ##
 # The data structure used to represent a cloud.
@@ -27,6 +32,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.
@@ -44,8 +53,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, 
@@ -59,7 +69,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
 
@@ -102,6 +113,16 @@ def init_server():
         cloudURL = cloudURL.replace('http://', '')
     (cloud['ip'], parts) = cloudURL.split(':')
 
+    # Create image bundles
+    images = getEucaConnection().get_all_images()
+    cloud['images'] = images
+    cloud['imageBundles'] = {}
+    for i in images:
+        if i.type != 'machine' or i.kernel_id is None: continue
+        name = os.path.dirname(i.location)
+        detail = {'imageID' : i.id, 'kernelID' : i.kernel_id, 'ramdiskID' : i.ramdisk_id}
+        cloud['imageBundles'][name] = detail
+
     # Initialize sqlite3 database.
     dbPath = '/etc/sfa/db'
     dbName = 'euca_aggregate.db'
@@ -162,6 +183,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.
 #
@@ -229,8 +286,6 @@ class EucaRSpecBuilder(object):
                                 xml << str(inst[4])
                             with xml.disk_space(unit='GB'):
                                 xml << str(inst[5])
-                            if inst[0] == 'm1.small':
-                                self.__requestXML(1, 'emi-88760F45', 'eki-F26610C6', 'cortex')
                             if 'instances' in cloud and inst[0] in cloud['instances']:
                                 existingEucaInstances = cloud['instances'][inst[0]]
                                 with xml.euca_instances:
@@ -240,8 +295,13 @@ class EucaRSpecBuilder(object):
                                                 xml << eucaInst['state']
                                             with xml.public_dns:
                                                 xml << eucaInst['public_dns']
-                                            with xml.keypair:
-                                                xml << eucaInst['key']
+
+    def __imageBundleXML(self, bundles):
+        xml = self.eucaRSpec
+        with xml.bundles:
+            for bundle in bundles.keys():
+                with xml.bundle(id=bundle):
+                    xml << ''
 
     ##
     # Creates the Images stanza.
@@ -288,8 +348,9 @@ class EucaRSpecBuilder(object):
             with xml.cloud(id=cloud['name']):
                 with xml.ipv4:
                     xml << cloud['ip']
-                self.__keyPairsXML(cloud['keypairs'])
-                self.__imagesXML(cloud['images'])
+                #self.__keyPairsXML(cloud['keypairs'])
+                #self.__imagesXML(cloud['images'])
+                self.__imageBundleXML(cloud['imageBundles'])
                 self.__clustersXML(cloud['clusters'])
         return str(xml)
 
@@ -333,9 +394,18 @@ class ZoneResultParser(object):
 
         return clusterList
 
-def get_rspec(api, xrn, origin_hrn):
+def ListResources(api, creds, options, call_id): 
+    if Callids().already_handled(call_id): return ""
     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:
@@ -352,6 +422,12 @@ def get_rspec(api, xrn, origin_hrn):
         # Images
         images = conn.get_all_images()
         cloud['images'] = images
+        cloud['imageBundles'] = {}
+        for i in images:
+            if i.type != 'machine' or i.kernel_id is None: continue
+            name = os.path.dirname(i.location)
+            detail = {'imageID' : i.id, 'kernelID' : i.kernel_id, 'ramdiskID' : i.ramdisk_id}
+            cloud['imageBundles'][name] = detail
 
         # Key Pairs
         keyPairs = conn.get_all_key_pairs()
@@ -383,7 +459,7 @@ def get_rspec(api, xrn, origin_hrn):
                 for instance in reservation.instances:
                     instances.append(instance)
 
-            # Construct a dictory for the EucaRSpecBuilder
+            # Construct a dictionary for the EucaRSpecBuilder
             instancesDict = {}
             for instance in instances:
                 instList = instancesDict.setdefault(instance.instance_type, [])
@@ -414,7 +490,9 @@ def get_rspec(api, xrn, origin_hrn):
 """
 Hook called via 'sfi.py create'
 """
-def create_slice(api, xrn, xml):
+def create_slice(api, xrn, creds, xml, users, call_id):
+    if Callids().already_handled(call_id): return False
+
     global cloud
     hrn = urn_to_hrn(xrn)[0]
 
@@ -456,20 +534,24 @@ 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
+        sys.stderr.flush()
     for req in requests:
         vmTypeElement = req.getparent()
         instType = vmTypeElement.get('name')
         numInst  = int(req.find('instances').text)
-        instKernel  = req.find('kernel_image').get('id')
-        instDiskImg = req.find('disk_image').get('id')
-        instKey     = req.find('keypair').text
         
-        ramDiskElement = req.find('ramdisk')
-        ramDiskAttr    = ramDiskElement.attrib
-        if 'id' in ramDiskAttr:
-            instRamDisk = ramDiskAttr['id']
-        else:
-            instRamDisk = None
+        bundleName = req.find('bundle').text
+        if not cloud['imageBundles'][bundleName]:
+            print >>sys.stderr, 'Cannot find bundle %s' % bundleName
+        bundleInfo = cloud['imageBundles'][bundleName]
+        instKernel  = bundleInfo['kernelID']
+        instDiskImg = bundleInfo['imageID']
+        instRamDisk = bundleInfo['ramdiskID']
+        instKey     = None
 
         # Create the instances
         for i in range(0, numInst):
@@ -479,20 +561,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, 'call-id-cloudtest')
 
-    #rspec = get_rspec('euca', 'planetcloud.pc.test', 'planetcloud.pc.marcoy')
+    #rspec = ListResources('euca', 'planetcloud.pc.test', 'planetcloud.pc.marcoy', 'test_euca')
     #print rspec
+    print getKeysForSlice('gc.gc.test1')
 
 if __name__ == "__main__":
     main()