RIFT-14308 Meaningful logs while initalizing Openstack driver
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rwcal_openstack.py
index 8e0c710..8a2d275 100644 (file)
@@ -1,5 +1,5 @@
 
-# 
+#
 #   Copyright 2016 RIFT.IO Inc
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,6 +26,7 @@ import rw_status
 import rift.cal.rwcal_status as rwcal_status
 import rwlogger
 import neutronclient.common.exceptions as NeutronException
+import keystoneclient.exceptions as KeystoneExceptions
 
 from gi.repository import (
     GObject,
@@ -88,6 +89,9 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                                          tenant_name   = account.openstack.tenant,
                                          mgmt_network  = account.openstack.mgmt_network,
                                          cert_validate = account.openstack.cert_validate )
+            except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure,
+                        NeutronException.NotFound) as e:
+                raise
             except Exception as e:
                 self.log.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e)))
                 raise
@@ -118,11 +122,27 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             Validation Code and Details String
         """
         status = RwcalYang.CloudConnectionStatus()
-
         try:
             with self._use_driver(account) as drv:
                 drv.validate_account_creds()
 
+        except KeystoneExceptions.Unauthorized as e:
+            self.log.error("Invalid credentials given for VIM account %s" %account.name)
+            status.status = "failure"
+            status.details = "Invalid Credentials: %s" % str(e)
+
+        except KeystoneExceptions.AuthorizationFailure as e:
+            self.log.error("Bad authentication URL given for VIM account %s. Given auth url: %s" % (
+            account.name, account.openstack.auth_url))
+            status.status = "failure"
+            status.details = "Invalid auth url: %s" % str(e)
+
+        except NeutronException.NotFound as e:
+            self.log.error("Given management network %s could not be found for VIM account %s" % (
+                        account.openstack.mgmt_network, account.name))
+            status.status = "failure"
+            status.details = "mgmt network does not exist: %s" % str(e)
+
         except openstack_drv.ValidationError as e:
             self.log.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e))
             status.status = "failure"
@@ -581,7 +601,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             if guest_epa.numa_node_policy.has_field('node'):
                 for node in guest_epa.numa_node_policy.node:
                     if node.has_field('vcpu') and node.vcpu:
-                        epa_specs['hw:numa_cpus.'+str(node.id)] = ','.join([str(j) for j in node.vcpu])
+                        epa_specs['hw:numa_cpus.'+str(node.id)] = ','.join([str(j.id) for j in node.vcpu])
                     if node.memory_mb:
                         epa_specs['hw:numa_mem.'+str(node.id)] = str(node.memory_mb)
 
@@ -635,7 +655,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             cpu_features = []
             espec_cpu_features = []
             for feature in host_epa.cpu_feature:
-                cpu_features.append(feature)
+                cpu_features.append(feature.feature)
             espec_cpu_features = espec_utils.host.mano_to_extra_spec_cpu_features(cpu_features)
             if espec_cpu_features is not None:
                 epa_specs['capabilities:cpu_info:features'] = espec_cpu_features
@@ -771,7 +791,9 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                     numa_node = getattr(flavor,'guest_epa').numa_node_policy.node.add()
                     numa_node.id = int(node_id)
 
-                numa_node.vcpu = [ int(x) for x in flavor_info['extra_specs'][attr].split(',') ]
+                for x in flavor_info['extra_specs'][attr].split(','):
+                   numa_node_vcpu = numa_node.vcpu.add()
+                   numa_node_vcpu.id = int(x)
 
             elif attr.startswith('hw:numa_mem.'):
                 node_id = attr.split('.')[1]
@@ -1296,11 +1318,11 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             except Exception as e:
                 self.log.error("Encountered exceptions during network creation. Exception: %s", str(e))
                 raise
-            
+
             kwargs = {'network_id' : network_id,
                       'dhcp_params': {'enable_dhcp': True},
                       'gateway_ip' : None,}
-            
+
             if link_params.ip_profile_params.has_field('ip_version'):
                 kwargs['ip_version'] = 6 if link_params.ip_profile_params.ip_version == 'ipv6' else 4
             else:
@@ -1315,7 +1337,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                                    link_params.ip_profile_params.subnet_prefix_pool,
                                    link_params.name)
                     raise NeutronException.NotFound("SubnetPool with name %s not found"%(link_params.ip_profile_params.subnet_prefix_pool))
-                
+
                 kwargs['subnetpool_id'] = subnet_pool['id']
             elif link_params.has_field('subnet'):
                 kwargs['cidr'] = link_params.subnet
@@ -1329,17 +1351,17 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                     kwargs['dhcp_params']['start_address']  = link_params.ip_profile_params.dhcp_params.start_address
                 if link_params.ip_profile_params.dhcp_params.has_field('count'):
                     kwargs['dhcp_params']['count']  = link_params.ip_profile_params.dhcp_params.count
-    
+
             if link_params.ip_profile_params.has_field('dns_server'):
                 kwargs['dns_server'] = []
                 for server in link_params.ip_profile_params.dns_server:
-                    kwargs['dns_server'].append(server)
+                    kwargs['dns_server'].append(server.address)
 
             if link_params.ip_profile_params.has_field('gateway_address'):
                 kwargs['gateway_ip'] = link_params.ip_profile_params.gateway_address
-                
+
             drv.neutron_subnet_create(**kwargs)
-            
+
         return network_id
 
 
@@ -1429,12 +1451,12 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         kwargs['network_id'] = c_point.virtual_link_id
         kwargs['admin_state_up'] = True
 
-        if c_point.type_yang == 'VIRTIO':
+        if c_point.type_yang == 'VIRTIO' or c_point.type_yang == 'E1000':
             kwargs['port_type'] = 'normal'
         elif c_point.type_yang == 'SR_IOV':
             kwargs['port_type'] = 'direct'
         else:
-            raise NotImplementedError("Port Type: %s not supported" %(c_point.port_type))
+            raise NotImplementedError("Port Type: %s not supported" %(c_point.type_yang))
 
         with self._use_driver(account) as drv:
             if c_point.has_field('security_group'):
@@ -1504,8 +1526,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('pcie_device'):
             self.log.debug("Rejecting available flavor because pcie_device not required but available")
             return False
-                        
-                    
+
+
         if required.has_field('mempage_size'):
             self.log.debug("Matching mempage_size")
             if available.has_field('mempage_size') == False:
@@ -1518,7 +1540,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('mempage_size'):
             self.log.debug("Rejecting available flavor because mempage_size not required but available")
             return False
-        
+
         if required.has_field('cpu_pinning_policy'):
             self.log.debug("Matching cpu_pinning_policy")
             if required.cpu_pinning_policy != 'ANY':
@@ -1532,7 +1554,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_pinning_policy'):
             self.log.debug("Rejecting available flavor because cpu_pinning_policy not required but available")
             return False
-        
+
         if required.has_field('cpu_thread_pinning_policy'):
             self.log.debug("Matching cpu_thread_pinning_policy")
             if available.has_field('cpu_thread_pinning_policy') == False:
@@ -1559,7 +1581,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('trusted_execution'):
             self.log.debug("Rejecting available flavor because trusted_execution not required but available")
             return False
-        
+
         if required.has_field('numa_node_policy'):
             self.log.debug("Matching numa_node_policy")
             if available.has_field('numa_node_policy') == False:
@@ -1578,7 +1600,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                 elif available.numa_node_policy.has_field('node_cnt'):
                     self.log.debug("Rejecting available flavor because numa node count not required but available")
                     return False
-                
+
                 if required.numa_node_policy.has_field('mem_policy'):
                     self.log.debug("Matching numa_node_policy mem_policy")
                     if available.numa_node_policy.has_field('mem_policy') == False:
@@ -1645,7 +1667,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_model'):
             self.log.debug("Rejecting available flavor because cpu_model not required but available")
             return False
-        
+
         if required.has_field('cpu_arch'):
             self.log.debug("Matching CPU architecture")
             if available.has_field('cpu_arch') == False:
@@ -1659,7 +1681,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_arch'):
             self.log.debug("Rejecting available flavor because cpu_arch not required but available")
             return False
-        
+
         if required.has_field('cpu_vendor'):
             self.log.debug("Matching CPU vendor")
             if available.has_field('cpu_vendor') == False:
@@ -1686,7 +1708,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_socket_count'):
             self.log.debug("Rejecting available flavor because cpu_socket_count not required but available")
             return False
-        
+
         if required.has_field('cpu_core_count'):
             self.log.debug("Matching CPU core count")
             if available.has_field('cpu_core_count') == False:
@@ -1699,7 +1721,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_core_count'):
             self.log.debug("Rejecting available flavor because cpu_core_count not required but available")
             return False
-        
+
         if required.has_field('cpu_core_thread_count'):
             self.log.debug("Matching CPU core thread count")
             if available.has_field('cpu_core_thread_count') == False:
@@ -1712,7 +1734,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_core_thread_count'):
             self.log.debug("Rejecting available flavor because cpu_core_thread_count not required but available")
             return False
-    
+
         if required.has_field('cpu_feature'):
             self.log.debug("Matching CPU feature list")
             if available.has_field('cpu_feature') == False:
@@ -1726,13 +1748,13 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         elif available.has_field('cpu_feature'):
             self.log.debug("Rejecting available flavor because cpu_feature not required but available")
             return False
-        self.log.info("Successful match for Host EPA attributes")            
+        self.log.info("Successful match for Host EPA attributes")
         return True
 
 
     def _match_placement_group_inputs(self, required, available):
         self.log.info("Matching Host aggregate attributes")
-        
+
         if not required and not available:
             # Host aggregate not required and not available => success
             self.log.info("Successful match for Host Aggregate attributes")
@@ -1753,7 +1775,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             #  - Host aggregate not required but available
             self.log.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s ", required, available)
             return False
-                    
+
     def match_epa_params(self, resource_info, request_params):
         result = self._match_vm_flavor(getattr(request_params, 'vm_flavor'),
                                        getattr(resource_info, 'vm_flavor'))
@@ -1791,11 +1813,11 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         if result == False:
             self.log.debug("Host Aggregate mismatched")
             return False
-        
+
         return True
 
     def _select_resource_flavor(self, account, vdu_init):
-        """ 
+        """
             Select a existing flavor if it matches the request or create new flavor
         """
         flavor = RwcalYang.FlavorInfoItem()
@@ -1803,7 +1825,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         epa_types = ['vm_flavor', 'guest_epa', 'host_epa', 'host_aggregate', 'hypervisor_epa', 'vswitch_epa']
         epa_dict = {k: v for k, v in vdu_init.as_dict().items() if k in epa_types}
         flavor.from_dict(epa_dict)
+
         rc, response = self.do_get_flavor_list(account)
         if rc != RwTypes.RwStatus.SUCCESS:
             self.log.error("Get-flavor-info-list operation failed for cloud account: %s",
@@ -1868,6 +1890,36 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         if not vdu_init.has_field('flavor_id'):
             vdu_init.flavor_id = self._select_resource_flavor(account,vdu_init)
 
+        ### Check VDU Virtual Interface type and make sure VM with property exists
+        if vdu_init.connection_points is not None:
+                ### All virtual interfaces need to be of the same type for Openstack Accounts
+                if not all(cp.type_yang == vdu_init.connection_points[0].type_yang for cp in vdu_init.connection_points):
+                    ### We have a mix of E1000 & VIRTIO virtual interface types in the VDU, abort instantiation.
+                    assert False, "Only one type of Virtual Intefaces supported for Openstack accounts. Found a mix of VIRTIO & E1000."
+
+                with self._use_driver(account) as drv:
+                    img_info = drv.glance_image_get(vdu_init.image_id)
+
+                virt_intf_type = vdu_init.connection_points[0].type_yang
+                if virt_intf_type == 'E1000':
+                    if 'hw_vif_model' in img_info and img_info.hw_vif_model == 'e1000':
+                        self.log.debug("VDU has Virtual Interface E1000, found matching image with property hw_vif_model=e1000")
+                    else:
+                        err_str = ("VDU has Virtual Interface E1000, but image '%s' does not have property hw_vif_model=e1000" % img_info.name)
+                        self.log.error(err_str)
+                        raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str)
+                elif virt_intf_type == 'VIRTIO':
+                    if 'hw_vif_model' in img_info:
+                        err_str = ("VDU has Virtual Interface VIRTIO, but image '%s' has hw_vif_model mismatch" % img_info.name)
+                        self.log.error(err_str)
+                        raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str)
+                    else:
+                        self.log.debug("VDU has Virtual Interface VIRTIO, found matching image")
+                else:
+                    err_str = ("VDU Virtual Interface '%s' not supported yet" % virt_intf_type)
+                    self.log.error(err_str)
+                    raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str)
+
         with self._use_driver(account) as drv:
             ### Now Create VM
             vm           = RwcalYang.VMInfoItem()