Merge Master in geni-v3 conflict resolution
[sfa.git] / sfa / openstack / osaggregate.py
index d603b08..de34995 100644 (file)
@@ -4,11 +4,12 @@ import socket
 import base64
 import string
 import random
-import time
+import time    
 from collections import defaultdict
 from nova.exception import ImageNotFound
 from nova.api.ec2.cloud import CloudController
-from sfa.util.faults import SfaAPIError, InvalidRSpec
+from sfa.util.faults import SfaAPIError, SliverDoesNotExist
+from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch
 from sfa.rspecs.rspec import RSpec
 from sfa.rspecs.elements.hardware_type import HardwareType
 from sfa.rspecs.elements.node import Node
@@ -36,15 +37,6 @@ def pubkeys_to_user_data(pubkeys):
         user_data += "\n"
     return user_data
 
-def instance_to_sliver(instance, slice_xrn=None):
-    sliver_id = None
-    sliver = Sliver({'name': instance.name,
-                     'type': instance.name,
-                     'cpus': str(instance.vcpus),
-                     'memory': str(instance.ram),
-                     'storage':  str(instance.disk)})
-    return sliver
-
 def image_to_rspec_disk_image(image):
     img = DiskImage()
     img['name'] = image['name']
@@ -58,30 +50,78 @@ class OSAggregate:
     def __init__(self, driver):
         self.driver = driver
 
-    def get_rspec(self, slice_xrn=None, version=None, options={}):
-        version_manager = VersionManager()
-        version = version_manager.get_version(version)
-        if not slice_xrn:
-            rspec_version = version_manager._get_version(version.type, version.version, 'ad')
-            nodes = self.get_aggregate_nodes()
-        else:
-            rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
-            nodes = self.get_slice_nodes(slice_xrn)
-        rspec = RSpec(version=rspec_version, user_options=options)
-        rspec.version.add_nodes(nodes)
-        return rspec.toxml()
-
     def get_availability_zones(self):
-        # essex release
         zones = self.driver.shell.nova_manager.dns_domains.domains()
-
         if not zones:
             zones = ['cloud']
         else:
             zones = [zone.name for zone in zones]
         return zones
 
-    def instance_to_rspec_node(self, slice_xrn, instance):
+    def list_resources(self, version=None, options={}):
+        version_manager = VersionManager()
+        version = version_manager.get_version(version)
+        rspec_version = version_manager._get_version(version.type, version.version, 'ad')
+        rspec = RSpec(version=version, user_options=options)
+        nodes = self.get_aggregate_nodes()
+        rspec.version.add_nodes(nodes)
+        return rspec.toxml()
+
+    def describe(self, urns, version=None, options={}):
+        # update nova connection
+        tenant_name = OSXrn(xrn=urns[0], type='slice').get_tenant_name()
+        self.driver.shell.nova_manager.connect(tenant=tenant_name)
+        instances = self.get_instances(urns)
+        # lookup the sliver allocations
+        sliver_ids = [sliver['sliver_id'] for sliver in slivers]
+        constraint = SliverAllocation.sliver_id.in_(sliver_ids)
+        sliver_allocations = dbsession.query(SliverAllocation).filter(constraint)
+        sliver_allocation_dict = {}
+        for sliver_allocation in sliver_allocations:
+            sliver_allocation_dict[sliver_allocation.sliver_id] = sliver_allocation
+
+        geni_slivers = []
+        rspec_nodes = []
+        for instance in instances:
+            rspec_nodes.append(self.instance_to_rspec_node(instance))
+            geni_sliver = self.instance_to_geni_sliver(instance, sliver_sllocation_dict)
+            geni_slivers.append(geni_sliver)
+        version_manager = VersionManager()
+        version = version_manager.get_version(version)
+        rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
+        rspec = RSpec(version=rspec_version, user_options=options)
+        rspec.xml.set('expires',  datetime_to_string(utcparse(time.time())))
+        rspec.version.add_nodes(rspec_nodes)
+        result = {'geni_urn': Xrn(urns[0]).get_urn(),
+                  'geni_rspec': rspec.toxml(), 
+                  'geni_slivers': geni_slivers}
+        
+        return result
+
+    def get_instances(self, urns):
+        # parse slice names and sliver ids
+        names = set()
+        ids = set()
+        for urn in urns:
+            xrn = OSXrn(xrn=urn)
+            if xrn.type == 'slice':
+                names.add(xrn.get_slice_name())
+            elif xrn.type == 'sliver':
+                ids.add(xrn.leaf)
+
+        # look up instances
+        instances = []
+        filter = {}
+        if names:
+            filter['name'] = names
+        if ids:
+            filter['id'] = ids   
+        servers = self.driver.shell.nova_manager.servers.findall(**filter)
+        instances.extend(servers)
+
+        return instances
+
+    def instance_to_rspec_node(self, instance):
         # determine node urn
         node_xrn = instance.metadata.get('component_id')
         if not node_xrn:
@@ -93,14 +133,13 @@ class OSAggregate:
         rspec_node['component_id'] = node_xrn.urn
         rspec_node['component_name'] = node_xrn.name
         rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
+        rspec_node['sliver_id'] = OSXrn(name=instance.name, type='slice', id=instance.id).get_urn() 
         if instance.metadata.get('client_id'):
             rspec_node['client_id'] = instance.metadata.get('client_id')
 
         # get sliver details
-        sliver_xrn = OSXrn(xrn=slice_xrn, type='slice', id=instance.id)
-        rspec_node['sliver_id'] = sliver_xrn.get_urn()
         flavor = self.driver.shell.nova_manager.flavors.find(id=instance.flavor['id'])
-        sliver = instance_to_sliver(flavor)
+        sliver = self.instance_to_sliver(flavor)
         # get firewall rules
         fw_rules = []
         group_name = instance.metadata.get('security_groups')
@@ -112,8 +151,9 @@ class OSAggregate:
                                   'port_range': port_range,
                                   'cidr_ip': rule['ip_range']['cidr']})
                 fw_rules.append(fw_rule)
-        sliver['fw_rules'] = fw_rules
-        rspec_node['slivers']= [sliver]
+        sliver['fw_rules'] = fw_rules 
+        rspec_node['slivers'] = [sliver]
+
         # get disk image
         image = self.driver.shell.image_manager.get_images(id=instance.image['id'])
         if isinstance(image, list) and len(image) > 0:
@@ -121,24 +161,23 @@ class OSAggregate:
         disk_image = image_to_rspec_disk_image(image)
         sliver['disk_image'] = [disk_image]
 
-        # get interfaces
+        # get interfaces            
         rspec_node['services'] = []
         rspec_node['interfaces'] = []
         addresses = instance.addresses
-        # HACK: public ips are stored in the list of private, but
-        # this seems wrong. Assume pub ip is the last in the list of
-        # private ips until openstack bug is fixed.
+        # HACK: public ips are stored in the list of private, but 
+        # this seems wrong. Assume pub ip is the last in the list of 
+        # private ips until openstack bug is fixed.      
         if addresses.get('private'):
             login = Login({'authentication': 'ssh-keys',
                            'hostname': addresses.get('private')[-1]['addr'],
                            'port':'22', 'username': 'root'})
             service = Services({'login': login})
-            rspec_node['services'].append(service)
-
-        if_index = 0
+            rspec_node['services'].append(service)    
+        
         for private_ip in addresses.get('private', []):
-            if_xrn = PlXrn(auth=self.driver.hrn,
-                           interface='node%s' % (instance.hostId))
+            if_xrn = PlXrn(auth=self.driver.hrn, 
+                           interface='node%s' % (instance.hostId)) 
             if_client_id = Xrn(if_xrn.urn, type='interface', id="eth%s" %if_index).urn
             if_sliver_id = Xrn(rspec_node['sliver_id'], type='slice', id="eth%s" %if_index).urn
             interface = Interface({'component_id': if_xrn.urn,
@@ -146,32 +185,66 @@ class OSAggregate:
                                    'sliver_id': if_sliver_id})
             interface['ips'] =  [{'address': private_ip['addr'],
                                  #'netmask': private_ip['network'],
-                                 'type': 'ipv%s' % str(private_ip['version'])}]
-            rspec_node['interfaces'].append(interface)
-            if_index += 1
-
+                                 'type': private_ip['version']}]
+            rspec_node['interfaces'].append(interface) 
+        
         # slivers always provide the ssh service
         for public_ip in addresses.get('public', []):
-            login = Login({'authentication': 'ssh-keys',
-                           'hostname': public_ip['addr'],
+            login = Login({'authentication': 'ssh-keys', 
+                           'hostname': public_ip['addr'], 
                            'port':'22', 'username': 'root'})
             service = Services({'login': login})
             rspec_node['services'].append(service)
-        return rspec_node 
+        return rspec_node
 
-    def get_slice_nodes(self, slice_xrn):
-        # update nova connection
-        tenant_name = OSXrn(xrn=slice_xrn, type='slice').get_tenant_name()
-        self.driver.shell.nova_manager.connect(tenant=tenant_name)    
-        
-        zones = self.get_availability_zones()
-        name = hrn_to_os_slicename(slice_xrn)
-        instances = self.driver.shell.nova_manager.servers.findall(name=name)
-        rspec_nodes = []
-        for instance in instances:
-            rspec_nodes.append(self.instance_to_rspec_node(slice_xrn, instance))
-        return rspec_nodes
 
+    def instance_to_sliver(self, instance, xrn=None):
+        if xrn:
+            sliver_hrn = '%s.%s' % (self.driver.hrn, instance.id)
+            sliver_id = Xrn(sliver_hrn, type='sliver').urn
+
+        sliver = Sliver({'sliver_id': sliver_id,
+                         'name': instance.name,
+                         'type': instance.name,
+                         'cpus': str(instance.vcpus),
+                         'memory': str(instance.ram),
+                         'storage':  str(instance.disk)})
+        return sliver   
+
+    def instance_to_geni_sliver(self, instance, sliver_allocations = {}):
+        sliver_hrn = '%s.%s' % (self.driver.hrn, instance.id)
+        sliver_id = Xrn(sliver_hrn, type='sliver').urn
+        # set sliver allocation and operational status
+        sliver_allocation = sliver_allocations[sliver_id]
+        if sliver_allocation:
+            allocation_status = sliver_allocation.allocation_state
+            if allocation_status == 'geni_allocated':
+                op_status =  'geni_pending_allocation'
+            elif allocation_status == 'geni_provisioned':
+                state = instance.state.lower()
+                if state == 'active':
+                    op_status = 'geni_ready'
+                elif state == 'building':
+                    op_status = 'geni_notready'
+                elif state == 'failed':
+                    op_status =' geni_failed'
+                else:
+                    op_status = 'geni_unknown'
+            else:
+                allocation_status = 'geni_unallocated'    
+        # required fields
+        geni_sliver = {'geni_sliver_urn': sliver_id, 
+                       'geni_expires': None,
+                       'geni_allocation_status': allocation_status,
+                       'geni_operational_status': op_status,
+                       'geni_error': None,
+                       'plos_created_at': datetime_to_string(utcparse(instance.created)),
+                       'plos_sliver_type': self.shell.nova_manager.flavors.find(id=instance.flavor['id']).name,
+                        }
+
+        return geni_sliver
+                        
     def get_aggregate_nodes(self):
         zones = self.get_availability_zones()
         # available sliver/instance/vm types
@@ -193,16 +266,15 @@ class OSAggregate:
                                                 HardwareType({'name': 'pc'})]
             slivers = []
             for instance in instances:
-                sliver = instance_to_sliver(instance)
+                sliver = self.instance_to_sliver(instance)
                 sliver['disk_image'] = disk_images
                 slivers.append(sliver)
-        
+            rspec_node['available'] = 'true'
             rspec_node['slivers'] = slivers
             rspec_nodes.append(rspec_node) 
 
         return rspec_nodes 
 
-
     def create_tenant(self, tenant_name):
         tenants = self.driver.shell.auth_manager.tenants.findall(name=tenant_name)
         if not tenants:
@@ -212,7 +284,6 @@ class OSAggregate:
             tenant = tenants[0]
         return tenant
             
-
     def create_instance_key(self, slice_hrn, user):
         slice_name = Xrn(slice_hrn).leaf
         user_name = Xrn(user['urn']).leaf
@@ -289,7 +360,7 @@ class OSAggregate:
         requested_instances = defaultdict(list)
         
         # iterate over clouds/zones/nodes
-        created_instances = []
+        slivers = []
         for node in rspec.version.get_nodes_with_slivers():
             instances = node.get('slivers', [])
             if not instances:
@@ -310,55 +381,75 @@ class OSAggregate:
                     if node.get('component_id'):
                         metadata['component_id'] = node['component_id']
                     if node.get('client_id'):
-                        metadata['client_id'] = node['client_id']
-                    server = self.driver.shell.nova_manager.servers.create(flavor=flavor_id,
+                        metadata['client_id'] = node['client_id'] 
+                    server = self.driver.shell.nova_manager.servers.create(
+                                                            flavor=flavor_id,
                                                             image=image_id,
                                                             key_name = key_name,
                                                             security_groups = [group_name],
                                                             files=files,
                                                             meta=metadata, 
                                                             name=instance_name)
-                    created_instances.append(server)
-                    
+                    slivers.append(server)
                 except Exception, err:    
                     logger.log_exc(err)                                
                            
-        return created_instances
-
-
-    def delete_instances(self, instance_name, tenant_name):
+        return slivers        
 
-        def _delete_security_group(instance):
-            security_group = instance.metadata.get('security_groups', '')
+    def delete_instance(self, instance):
+    
+        def _delete_security_group(inst):
+            security_group = inst.metadata.get('security_groups', '')
             if security_group:
                 manager = SecurityGroup(self.driver)
                 timeout = 10.0 # wait a maximum of 10 seconds before forcing the security group delete
                 start_time = time.time()
                 instance_deleted = False
                 while instance_deleted == False and (time.time() - start_time) < timeout:
-                    inst = self.driver.shell.nova_manager.servers.findall(id=instance.id)
-                    if not inst:
+                    tmp_inst = self.driver.shell.nova_manager.servers.findall(id=inst.id)
+                    if not tmp_inst:
                         instance_deleted = True
-                    time.sleep(1)
+                    time.sleep(.5)
                 manager.delete_security_group(security_group)
 
         thread_manager = ThreadManager()
-        self.driver.shell.nova_manager.connect(tenant=tenant_name)
-        instances = self.driver.shell.nova_manager.servers.findall(name=instance_name)
+        tenant = self.driver.shell.auth_manager.tenants.find(id=instance.tenant_id)  
+        self.driver.shell.nova_manager.connect(tenant=tenant.name)
+        args = {'name': instance.name,
+                'id': instance.id}
+        instances = self.driver.shell.nova_manager.servers.findall(**args)
+        security_group_manager = SecurityGroup(self.driver)
         for instance in instances:
             # destroy instance
             self.driver.shell.nova_manager.servers.delete(instance)
             # deleate this instance's security groups
             thread_manager.run(_delete_security_group, instance)
-        return True
-
+        return 1
 
-    def stop_instances(self, instance_name, tenant_name):
+    def stop_instances(self, instance_name, tenant_name, id=None):
         self.driver.shell.nova_manager.connect(tenant=tenant_name)
-        instances = self.driver.shell.nova_manager.servers.findall(name=instance_name)
+        args = {'name': instance_name}
+        if id:
+            args['id'] = id
+        instances = self.driver.shell.nova_manager.servers.findall(**args)
         for instance in instances:
             self.driver.shell.nova_manager.servers.pause(instance)
         return 1
 
+    def start_instances(self, instance_name, tenant_name, id=None):
+        self.driver.shell.nova_manager.connect(tenant=tenant_name)
+        args = {'name': instance_name}
+        if id:
+            args['id'] = id
+        instances = self.driver.shell.nova_manager.servers.findall(**args)
+        for instance in instances:
+            self.driver.shell.nova_manager.servers.resume(instance)
+        return 1
+
+    def restart_instances(self, instacne_name, tenant_name, id=None):
+        self.stop_instances(instance_name, tenant_name, id)
+        self.start_instances(instance_name, tenant_name, id)
+        return 1 
+
     def update_instances(self, project_name):
         pass