From dee7b92c2e3feb7f8004cd3e683aaf8a7781b650 Mon Sep 17 00:00:00 2001 From: Volodymyr Shypyguzov Date: Wed, 6 Apr 2016 17:56:37 +0300 Subject: [PATCH] Add test_sriov_instance_connectivity +Add corresponding test +Add _create_port +Add _cleanup_ports +Add sriov deployment tag if computes with SR-IOV enabled interface are present and unit-tests for ostf-adapter +Add sriov_enabled physnets to cluster config Change-Id: I487c0dc0f4756b893af5d5c59ea85ba0d47faa5c Closes-Bug: #1566906 Implements: blueprint test-sriov-support --- fuel_health/config.py | 19 +++- fuel_health/neutronmanager.py | 11 +++ fuel_health/nmanager.py | 39 ++++++-- .../tests/smoke/test_neutron_actions.py | 96 +++++++++++++++++++ fuel_plugin/ostf_adapter/mixins.py | 14 +++ fuel_plugin/testing/tests/base.py | 42 +++++++- .../tests/unit/test_support_utilities.py | 28 ++++++ 7 files changed, 235 insertions(+), 14 deletions(-) diff --git a/fuel_health/config.py b/fuel_health/config.py index 7665289f..19d32970 100644 --- a/fuel_health/config.py +++ b/fuel_health/config.py @@ -719,8 +719,8 @@ class NailgunConfig(object): compute_nodes = filter(lambda node: 'compute' in node['roles'], data) online_computes = filter( - lambda node: 'compute' in node['roles'] - and node['online'] is True, data) + lambda node: 'compute' in node['roles'] and + node['online'] is True, data) online_computes_ips = [] for node in online_computes: online_computes_ips.append(node['ip']) @@ -730,14 +730,25 @@ class NailgunConfig(object): for node in compute_nodes: compute_ips.append(node['ip']) LOG.info("COMPUTES IPS %s" % compute_ips) + sriov_physnets = [] + compute_ids = [node['id'] for node in online_computes] + for compute_id in compute_ids: + api_url = '/api/nodes/{}/interfaces'.format(compute_id) + ifaces_resp = self.req_session.get( + self.nailgun_url + api_url).json() + for iface in ifaces_resp: + if iface['interface_properties']['sriov']['enabled']: + sriov_physnets.append( + iface['interface_properties']['sriov']['physnet']) + self.compute.sriov_physnets = sriov_physnets self.compute.compute_nodes = compute_ips ceph_nodes = filter(lambda node: 'ceph-osd' in node['roles'], data) self.compute.ceph_nodes = ceph_nodes online_ironic = filter( - lambda node: 'ironic' in node['roles'] - and node['online'] is True, data) + lambda node: 'ironic' in node['roles'] and + node['online'] is True, data) self.ironic.online_conductors = [] for node in online_ironic: self.ironic.online_conductors.append(node['ip']) diff --git a/fuel_health/neutronmanager.py b/fuel_health/neutronmanager.py index f652a61c..bc5d4f57 100644 --- a/fuel_health/neutronmanager.py +++ b/fuel_health/neutronmanager.py @@ -31,6 +31,7 @@ class NeutronBaseTest(fuel_health.nmanager.NovaNetworkScenarioTest): cls.networks = [] cls.floating_ips = [] cls.security_groups = {} + cls.ports = [] def setUp(self): super(NeutronBaseTest, self).setUp() @@ -172,8 +173,18 @@ class NeutronBaseTest(fuel_health.nmanager.NovaNetworkScenarioTest): cls.error_msg.append(exc) LOG.debug(traceback.format_exc()) + @classmethod + def _cleanup_ports(cls): + for port in cls.ports: + try: + cls.neutron_client.delete_port(port['port']['id']) + except Exception as exc: + cls.error_msg.append(exc) + LOG.debug(traceback.format_exc()) + @classmethod def tearDownClass(cls): super(NeutronBaseTest, cls) cls._clean_floating_ips() cls._clear_networks() + cls._cleanup_ports() diff --git a/fuel_health/nmanager.py b/fuel_health/nmanager.py index 5a57ecce..b7c96963 100644 --- a/fuel_health/nmanager.py +++ b/fuel_health/nmanager.py @@ -569,6 +569,7 @@ class NovaNetworkScenarioTest(OfficialClientTest): cls.error_msg = [] cls.flavors = [] cls.images = [] + cls.ports = [] cls.private_net = cls.config.network.private_net def setUp(self): @@ -654,6 +655,22 @@ class NovaNetworkScenarioTest(OfficialClientTest): "Network creation failed") return networks + def _create_port(self, net_id, vnic_type, label='ost1_test-port-'): + n_label = rand_name(label) + port_data = { + 'name': n_label, + 'binding:vnic_type': vnic_type, + 'network_id': net_id, + } + port = self.neutron_client.create_port({'port': port_data}) + self.set_resource(n_label, port) + self.ports.append(port) + LOG.debug(port) + self.verify_response_body_content(port['port']['name'], + n_label, + "Port creation failed") + return port + @classmethod def _clear_networks(cls): try: @@ -680,7 +697,8 @@ class NovaNetworkScenarioTest(OfficialClientTest): def _create_server(self, client, name, security_groups=None, flavor_id=None, net_id=None, img_name=None, - data_file=None, az_name=None): + data_file=None, az_name=None, port=None): + create_kwargs = {} if img_name: base_image_id = self.get_image_from_name(img_name=img_name) @@ -702,6 +720,7 @@ class NovaNetworkScenarioTest(OfficialClientTest): security_groups = [self._create_security_group( self.compute_client).name] if 'neutron' in self.config.network.network_provider: + create_kwargs['nics'] = [] if net_id: network = [net_id] else: @@ -709,15 +728,17 @@ class NovaNetworkScenarioTest(OfficialClientTest): self.compute_client.networks.list() if net.label == self.private_net] - if network: - create_kwargs = {'nics': [{'net-id': network[0]}], - 'security_groups': security_groups} + if port: + create_kwargs['nics'].append({'port-id': port['port']['id']}) else: - self.fail("Default private network '{0}' isn't present. " - "Please verify it is properly created.". - format(self.private_net)) - else: - create_kwargs = {'security_groups': security_groups} + if network: + create_kwargs['nics'].append({'net-id': network[0]}) + else: + self.fail("Default private network '{0}' isn't present. " + "Please verify it is properly created.". + format(self.private_net)) + + create_kwargs['security_groups'] = security_groups server = client.servers.create(name, base_image_id, flavor_id, files=data_file, diff --git a/fuel_health/tests/smoke/test_neutron_actions.py b/fuel_health/tests/smoke/test_neutron_actions.py index dca934e7..22e715ec 100644 --- a/fuel_health/tests/smoke/test_neutron_actions.py +++ b/fuel_health/tests/smoke/test_neutron_actions.py @@ -144,3 +144,99 @@ class TestNeutron(neutronmanager.NeutronBaseTest): "Subnet deletion", subnet) self.verify(20, self._remove_network, 16, "Network can not be deleted", "Network deletion", network) + + def test_check_sriov_instance_connectivity(self): + """Check network connectivity from SRIOV instance via floating IP + Target component: Neutron + + Scenario: + 1. Create a new security group (if it doesn't exist yet). + 2. Create SR-IOV port + 3. Create an instance using new security group and SR-IOV port. + 4. Create new floating IP + 5. Assign created floating IP to the instance. + 6. Check connectivity to the floating IP using ping command. + 7. Check that public IP 8.8.8.8 can be pinged from instance. + 8. Disassociate server floating ip. + 9. Delete floating ip + 10. Delete server. + 11. Delete SR-IOV port + Duration: 300 s. + + Deployment tags: sriov + """ + if 'physnet2' not in self.config.compute.sriov_physnets: + self.skipTest('physnet2 is not configured for any interface') + self.check_image_exists() + if not self.security_groups: + self.security_groups[self.tenant_id] = self.verify( + 25, self._create_security_group, 1, + "Security group can not be created.", + 'security group creation', + self.compute_client) + + name = rand_name('ost1_test-server-sriov-') + security_groups = [self.security_groups[self.tenant_id].name] + + network = [net.id for net in + self.compute_client.networks.list() + if net.label == self.private_net] + + port = self.verify( + 20, + self._create_port, + 2, + "SRIOV port can not be created.", + 'SRIOV port creation', + net_id=network[0], vnic_type='direct') + + server = self.verify(250, self._create_server, 3, + "Server can not be created.", + "server creation", + self.compute_client, name, security_groups, + port=port, net_id=network[0]) + + floating_ip = self.verify( + 20, + self._create_floating_ip, + 4, + "Floating IP can not be created.", + 'floating IP creation') + + self.verify(20, self._assign_floating_ip_to_instance, + 5, "Floating IP can not be assigned.", + 'floating IP assignment', + self.compute_client, server, floating_ip) + + self.floating_ips.append(floating_ip) + + ip_address = floating_ip.ip + LOG.info('is address is {0}'.format(ip_address)) + LOG.debug(ip_address) + + self.verify(600, self._check_vm_connectivity, 6, + "VM connectivity doesn`t function properly.", + 'VM connectivity checking', ip_address, + 30, (5, 10)) + + self.verify(600, self._check_connectivity_from_vm, + 7, ("Connectivity to 8.8.8.8 from the VM doesn`t " + "function properly."), + 'public connectivity checking from VM', ip_address, + 30, (5, 10)) + + self.verify(20, self.compute_client.servers.remove_floating_ip, + 8, "Floating IP cannot be removed.", + "removing floating IP", server, floating_ip) + + self.verify(20, self.compute_client.floating_ips.delete, + 9, "Floating IP cannot be deleted.", + "floating IP deletion", floating_ip) + + self.verify(30, self._delete_server, 10, + "Server can not be deleted. ", + "server deletion", server) + + self.verify(30, self.neutron_client.delete_port, 11, + "Port can not be deleted. ", + "port deletion", port['port']['id']) diff --git a/fuel_plugin/ostf_adapter/mixins.py b/fuel_plugin/ostf_adapter/mixins.py index 1ef8e1f0..925e2813 100644 --- a/fuel_plugin/ostf_adapter/mixins.py +++ b/fuel_plugin/ostf_adapter/mixins.py @@ -160,7 +160,21 @@ def _get_cluster_attrs(cluster_id, token=None): enable_without_ceph = filter(lambda node: 'ceph-osd' in node['roles'], nodes_response) + sriov_compute_ids = [] + compute_ids = [node['id'] for node in nodes_response + if "compute" in node['roles']] + for compute_id in compute_ids: + ifaces_url = URL.format( + cfg.CONF.adapter.nailgun_host, cfg.CONF.adapter.nailgun_port, + 'api/nodes/{id}/interfaces'.format(id=compute_id)) + ifaces_resp = REQ_SES.get(ifaces_url).json() + for iface in ifaces_resp: + if iface['interface_properties']['sriov']['enabled']: + sriov_compute_ids.append(compute_id) + deployment_tags = set() + if sriov_compute_ids: + deployment_tags.add('sriov') if not enable_without_ceph: deployment_tags.add('enable_without_ceph') diff --git a/fuel_plugin/testing/tests/base.py b/fuel_plugin/testing/tests/base.py index 3ada04e7..adf46d3e 100644 --- a/fuel_plugin/testing/tests/base.py +++ b/fuel_plugin/testing/tests/base.py @@ -147,7 +147,47 @@ CLUSTERS = { 'common': {} } } - } + }, + 7: { + 'cluster_meta': { + 'release_id': 7, + 'mode': 'ha' + }, + 'release_data': { + 'operating_system': 'rhel', + 'version': '2015.2-1.0', + }, + 'cluster_node': [ + { + "hostname": "node-1", + 'id': "1", + 'roles': "compute" + }, + ], + 'node_interfaces': [ + { + 'interface_properties': { + 'sriov': { + 'enabled': 'true' + } + } + + } + ], + 'cluster_attributes': { + 'editable': { + 'additional_components': { + 'murano': { + 'value': True + }, + 'sahara': { + 'value': False + } + }, + 'common': {} + } + } + }, } diff --git a/fuel_plugin/testing/tests/unit/test_support_utilities.py b/fuel_plugin/testing/tests/unit/test_support_utilities.py index 73c87d53..48842a41 100644 --- a/fuel_plugin/testing/tests/unit/test_support_utilities.py +++ b/fuel_plugin/testing/tests/unit/test_support_utilities.py @@ -49,3 +49,31 @@ class TestDeplTagsGetter(base.BaseUnitTest): res = mixins._get_cluster_attrs(expected['cluster_id']) self.assertEqual(res, expected['attrs']) + + def test_sriov_deployment_tag(self): + expected = { + 'cluster_id': 7, + 'attrs': { + 'deployment_tags': set( + ['ha', 'rhel', 'additional_components', + 'murano', 'nova_network', 'public_on_all_nodes', + 'enable_without_ceph', 'sriov']), + 'release_version': '2015.2-1.0' + } + } + + with requests_mock.Mocker() as m: + cluster = base.CLUSTERS[expected['cluster_id']] + m.register_uri('GET', '/api/clusters/7', + json=cluster['cluster_meta']) + m.register_uri('GET', '/api/clusters/7/attributes', + json=cluster['cluster_attributes']) + m.register_uri('GET', '/api/releases/7', + json=cluster['release_data']) + m.register_uri('GET', '/api/nodes?cluster_id=7', + json=cluster['cluster_node']) + m.register_uri('GET', '/api/nodes/1/interfaces', + json=cluster['node_interfaces']) + res = mixins._get_cluster_attrs(expected['cluster_id']) + + self.assertEqual(res, expected['attrs'])