X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=rwcal%2Fplugins%2Fvala%2Frwcal_openstack%2Frwcal_openstack.py;h=8a2d275a18de5bb0283fbc32a680e022071beb2a;hb=9a6c59279b572f61301e54014b0eb1dc72407a68;hp=8e0c7103474fa7d355753c9676105230a195ef89;hpb=6f07e6f33f751ab4ffe624f6037f887b243bece2;p=osm%2FSO.git diff --git a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py index 8e0c7103..8a2d275a 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py +++ b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py @@ -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()