From 86e851b0d1a19fe5c0ace2bf3e32b5bafa4cbbbb Mon Sep 17 00:00:00 2001 From: Devang Doshi Date: Fri, 2 Dec 2016 16:17:53 -0800 Subject: [PATCH] Tempest: Added MDProxy scenario test cases {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_delete_when_ls_bounded [42.348919s] ... ok {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_isolated_network [33.465001s] ... ok {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_ping [70.032833s] ... ok {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_verify_backend [69.797982s] ... ok {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_with_incorrect_password [62.458667s] ... FAILED {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_with_multiple_metadata_ports [131.592047s] ... ok {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_with_multiple_ports_on_network [114.483229s] ... ok {0} vmware_nsx_tempest.tests.nsxv3.scenario.test_mdproxy.TestMDProxy.test_mdproxy_with_server_on_two_ls [102.261764s] ... ok 1 failed test case is expected to run on other VDNet set up. Change-Id: I5871563d46641abb62311389c2f1e4a55c63cc18 --- vmware_nsx_tempest/common/constants.py | 6 + vmware_nsx_tempest/config.py | 3 + vmware_nsx_tempest/services/nsxv3_client.py | 21 ++ .../tests/nsxv3/scenario/test_mdproxy.py | 332 ++++++++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 vmware_nsx_tempest/tests/nsxv3/scenario/test_mdproxy.py diff --git a/vmware_nsx_tempest/common/constants.py b/vmware_nsx_tempest/common/constants.py index 67a34e02fa..bdc4f86268 100644 --- a/vmware_nsx_tempest/common/constants.py +++ b/vmware_nsx_tempest/common/constants.py @@ -30,3 +30,9 @@ L2GWC = "l2_gateway_connection" MAC_SW_PROFILE = "MacManagementSwitchingProfile" PORT_SEC_PROFILE = "SpoofGuardSwitchingProfile" SEC_GRPS_PROFILE = "SwitchSecuritySwitchingProfile" + +# NSXV3 MDProxy constants. +MD_ERROR_CODE_WHEN_LS_BOUNDED = "10026" +INTERVAL_BETWEEN_EXEC_RETRY_ON_SSH = 5 +MAX_NO_OF_TIMES_EXECUTION_OVER_SSH = 30 +MD_BASE_URL = "http://169.254.169.254/" diff --git a/vmware_nsx_tempest/config.py b/vmware_nsx_tempest/config.py index db7f21d5a8..b579072539 100644 --- a/vmware_nsx_tempest/config.py +++ b/vmware_nsx_tempest/config.py @@ -181,4 +181,7 @@ NSXv3Group = [ cfg.StrOpt('nsx_password', default='default', help="NSX manager password"), + cfg.BoolOpt('native_dhcp_metadata', + default=False, + help="Enable or disable Native DHCP and MDProxy for nsxv3"), ] diff --git a/vmware_nsx_tempest/services/nsxv3_client.py b/vmware_nsx_tempest/services/nsxv3_client.py index 619ef1f8e8..4fdbe79756 100644 --- a/vmware_nsx_tempest/services/nsxv3_client.py +++ b/vmware_nsx_tempest/services/nsxv3_client.py @@ -447,3 +447,24 @@ class NSXV3Client(object): """ endpoint = "/dhcp/servers/%s/static-bindings" % dhcp_server return self.get_logical_resources(endpoint) + + def get_md_proxies(self): + """ + Get md proxies. + + :return: returns list of md proxies information. + """ + return self.get_logical_resources("/md-proxies") + + def delete_md_proxy(self, uuid): + """ + Delete md proxies. + """ + return self.delete_logical_resources("/md-proxies/%s" % uuid) + + def delete_logical_resources(self, endpoint): + """ + Delete logical resources based on the endpoint. + """ + response = self.delete(endpoint=endpoint) + return response.json() diff --git a/vmware_nsx_tempest/tests/nsxv3/scenario/test_mdproxy.py b/vmware_nsx_tempest/tests/nsxv3/scenario/test_mdproxy.py new file mode 100644 index 0000000000..2a839d8538 --- /dev/null +++ b/vmware_nsx_tempest/tests/nsxv3/scenario/test_mdproxy.py @@ -0,0 +1,332 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_log import log as logging + +from tempest import config +from tempest import exceptions +from tempest.lib.common.utils import data_utils +from tempest.lib.common.utils import test_utils +from tempest.scenario import manager +from tempest import test + +from vmware_nsx_tempest._i18n import _LI +from vmware_nsx_tempest.common import constants +from vmware_nsx_tempest.services import nsxv3_client + +CONF = config.CONF + +LOG = logging.getLogger(__name__) + + +class TestMDProxy(manager.NetworkScenarioTest): + """Test MDProxy. + + Adding test cases to test MDProxy in different scenarios such as + testing it over multiple created networks, verify MDProxy realization + with nsxv3 backend, test MDProxy with isolated network and so on. + """ + + def setUp(self): + super(TestMDProxy, self).setUp() + self.image_ref = CONF.compute.image_ref + self.flavor_ref = CONF.compute.flavor_ref + self.run_ssh = CONF.validation.run_validation + self.ssh_user = CONF.validation.image_ssh_user + self.nsx = nsxv3_client.NSXV3Client(CONF.nsxv3.nsx_manager, + CONF.nsxv3.nsx_user, + CONF.nsxv3.nsx_password) + + @classmethod + def skip_checks(cls): + """Class level skip checks. + + Class level check. Skip all teh MDproxy tests, if native_dhcp_metadata + is not True under nsxv3 section of the config + """ + if not CONF.nsxv3.native_dhcp_metadata: + msg = " native_dhcp_metadata is not enabled under nsxv3 config" \ + ", skipping all the MDProxy tests!!!" + raise cls.skipException(msg) + + def verify_ssh(self, keypair, instance, port_id=None): + created_floating_ip = self.create_floating_ip(instance, + port_id=port_id) + self.fip = str(created_floating_ip["floating_ip_address"]) + self.assertIsNotNone(self.fip) + # Check ssh + self.ssh_client = self.get_remote_client( + ip_address=self.fip, username=self.ssh_user, + private_key=keypair["private_key"]) + + def _delete_router(self, router): + body = self.ports_client.list_ports(device_id=router["id"]) + interfaces = body["ports"] + for interface in interfaces: + test_utils.call_and_ignore_notfound_exc( + self.routers_client.remove_router_interface, router["id"], + subnet_id=interface["fixed_ips"][0]["subnet_id"]) + self.routers_client.delete_router(router["id"]) + + def _create_router(self, router_name=None, admin_state_up=True, + external_network_id=None, enable_snat=None, **kwargs): + ext_gw_info = {} + if external_network_id: + ext_gw_info["network_id"] = external_network_id + if enable_snat is not None: + ext_gw_info["enable_snat"] = enable_snat + body = self.routers_client.create_router( + name=router_name, external_gateway_info=ext_gw_info, + admin_state_up=admin_state_up, **kwargs) + router = body["router"] + self.addCleanup(self._delete_router, router) + return router + + def _create_net_subnet_router_interface(self, router=None): + network = self._create_network(namestart="network-mdproxy") + subnet = self._create_subnet(network) + if router: + self.routers_client.add_router_interface( + router["id"], subnet_id=subnet["id"]) + self.addCleanup(self.routers_client.remove_router_interface, + router["id"], subnet_id=subnet["id"]) + return network["id"], subnet["id"] + + def _create_server_on_networks(self, networks): + security_group = self._create_security_group() + name = data_utils.rand_name("server-mdproxy") + keypair = self.create_keypair() + security_groups = [{"name": security_group["name"]}] + instance = self.create_server( + image_id=self.image_ref, + flavor=self.flavor_ref, + config_drive=CONF.compute_feature_enabled.config_drive, name=name, + networks=networks, key_name=keypair["name"], + security_groups=security_groups, wait_until="ACTIVE") + self.addCleanup(self.servers_client.delete_server, instance["id"]) + return instance, keypair + + def _get_port_id(self, network_id, subnet_id, instance): + _, instance_addr = instance["addresses"].items()[0] + instance_fixed_ip = instance_addr[0]["addr"] + for port in self._list_ports(): + port_fixed_ip = port["fixed_ips"][0]["ip_address"] + if port["network_id"] == network_id and port["fixed_ips"][0][ + "subnet_id"] == subnet_id and "compute:" in port[ + "device_owner"] and port_fixed_ip == instance_fixed_ip: + port_id = port["id"] + self.assertIsNotNone(port_id, "Failed to find Instance's port id!!!") + return port_id + + def _verify_md(self, md_url, expected_value="", check_exist_only=False, + sub_result=None): + def exec_cmd_and_verify_output(): + cmd = "curl " + md_url + exec_cmd_retried = 0 + import time + while exec_cmd_retried < \ + constants.MAX_NO_OF_TIMES_EXECUTION_OVER_SSH: + result = self.ssh_client.exec_command(cmd) + self.assertIsNotNone(result) + if not result == "": + break + exec_cmd_retried += 1 + time.sleep(constants.INTERVAL_BETWEEN_EXEC_RETRY_ON_SSH) + LOG.info(_LI("Tried %s times!!!") % exec_cmd_retried) + if check_exist_only: + return "Verification is successful!" + msg = ("Failed while verifying metadata on server. Result " + "of command %r is NOT %r." % (cmd, expected_value)) + if sub_result: + msg2 = ("Failed to verify incorrect passowrd on metadata" + "server. Result %r is NOT in %r." % ( + sub_result, result)) + self.assertIn(sub_result, result, msg2) + return "Verification is successful!" + self.assertEqual(expected_value, result, msg) + return "Verification is successful!" + + if not test_utils.call_until_true(exec_cmd_and_verify_output, + CONF.compute.build_timeout, + CONF.compute.build_interval): + raise exceptions.TimeoutException("Timed out while waiting to " + "verify metadata on server. " + "%s is empty." % md_url) + + def verify_metadata_in_detail(self, instance): + # Check floating IPv4 in Metadata. + md_url_pubic_ipv4 = constants.MD_BASE_URL + \ + "latest/meta-data/public-ipv4" + self._verify_md(md_url=md_url_pubic_ipv4, expected_value=self.fip) + # Check hostname in Metadata. + md_url_hostname = constants.MD_BASE_URL + "latest/meta-data/hostname" + self._verify_md(md_url=md_url_hostname, + expected_value=instance["name"] + ".novalocal") + # Check local IPv4 in Metadata. + md_url_local_ipv4 = constants.MD_BASE_URL + \ + "latest/meta-data/local-ipv4" + self._verify_md(md_url=md_url_local_ipv4, check_exist_only=True) + # Check hostname in Metadata of 2009-04-04 folder. + md_url_hostname = constants.MD_BASE_URL + \ + "2009-04-04/meta-data/hostname" + self._verify_md(md_url=md_url_hostname, + expected_value=instance["name"] + ".novalocal") + # Check hostname in Metadata of 1.0 folder. + md_url_hostname = constants.MD_BASE_URL + "1.0/meta-data/hostname" + self._verify_md(md_url=md_url_hostname, + expected_value=instance["name"] + ".novalocal") + + def verify_md_proxy_logical_ports_on_backend(self): + md_counter = 0 + logical_ports = self.nsx.get_os_logical_ports() + for port_index in range(len(logical_ports)): + if logical_ports[port_index]["attachment"][ + "attachment_type"] == "METADATA_PROXY": + md_counter += 1 + msg = "Admin state of MDProxy logical port is DOWN!!!" + msg2 = "LS name does not start with mdproxy!!!" + msg3 = "MDproxy logical port does not have any auto tag!!!" + self.assertEqual( + "UP", logical_ports[port_index]["admin_state"], msg) + self.assertIn("mdproxy-", + logical_ports[port_index]["display_name"], msg2) + self.assertNotEqual(0, len(logical_ports[port_index]["tags"]), + msg3) + self.assertNotEqual(0, md_counter, "No logical port found for MD " + "proxy!!!") + + @test.idempotent_id("e9a93161-d852-414d-aa55-36d465ea45df") + @test.services("compute", "network") + def test_mdproxy_ping(self): + router = self._create_router( + router_name=data_utils.rand_name("router-MDProxy"), + external_network_id=CONF.network.public_network_id) + (network_id, subnet_id) = self._create_net_subnet_router_interface( + router) + networks_ids = {"uuid": network_id} + instance, keypair = self._create_server_on_networks([networks_ids]) + port_id = self._get_port_id(network_id, subnet_id, instance) + self.verify_ssh(keypair=keypair, instance=instance, port_id=port_id) + md_url_pubic_ipv4 = constants.MD_BASE_URL + \ + "latest/meta-data/public-ipv4" + self._verify_md(md_url=md_url_pubic_ipv4, expected_value=self.fip) + + @test.idempotent_id("743f34a6-58b8-4288-a07f-7bee21c55051") + @test.services("compute", "network") + def test_mdproxy_verify_backend(self): + router = self._create_router( + router_name=data_utils.rand_name("router-MDProxy"), + external_network_id=CONF.network.public_network_id) + (network_id, subnet_id) = self._create_net_subnet_router_interface( + router) + networks_ids = {"uuid": network_id} + instance, keypair = self._create_server_on_networks([networks_ids]) + port_id = self._get_port_id(network_id, subnet_id, instance) + self.verify_ssh(keypair=keypair, instance=instance, port_id=port_id) + self.verify_metadata_in_detail(instance=instance) + self.verify_md_proxy_logical_ports_on_backend() + + @test.idempotent_id("fce2acc8-b850-40fe-bf02-958dd3cd4343") + @test.services("compute", "network") + def test_mdproxy_with_server_on_two_ls(self): + router = self._create_router( + router_name=data_utils.rand_name("router-MDProxy"), + external_network_id=CONF.network.public_network_id) + (network_id1, subnet_id1) = self._create_net_subnet_router_interface( + router) + (network_id2, subnet_id2) = self._create_net_subnet_router_interface( + router) + net1 = {"uuid": network_id1} + net2 = {"uuid": network_id2} + instance, keypair = self._create_server_on_networks([net1, net2]) + port_id = self._get_port_id(network_id1, subnet_id1, instance) + self.verify_ssh(keypair=keypair, instance=instance, port_id=port_id) + self.verify_metadata_in_detail(instance=instance) + + @test.idempotent_id("67332752-1295-42cb-a8c3-99210fb6b00b") + @test.services("compute", "network") + def test_mdproxy_isolated_network(self): + (network_id, _) = self._create_net_subnet_router_interface() + networks_ids = {"uuid": network_id} + self._create_server_on_networks([networks_ids]) + self.verify_md_proxy_logical_ports_on_backend() + + @test.idempotent_id("cc8d2ab8-0bea-4e32-bf80-c9c46a7612b7") + @test.attr(type=["negative"]) + @test.services("compute", "network") + def test_mdproxy_delete_when_ls_bounded(self): + (network_id, _) = self._create_net_subnet_router_interface() + networks_ids = {"uuid": network_id} + self._create_server_on_networks([networks_ids]) + md_proxy_uuid = self.nsx.get_md_proxies()[0]["id"] + result = self.nsx.delete_md_proxy(md_proxy_uuid) + self.assertEqual(str(result["error_code"]), + constants.MD_ERROR_CODE_WHEN_LS_BOUNDED) + + @test.idempotent_id("501fc3ea-696b-4e9e-b383-293ab94e2545") + @test.services("compute", "network") + def test_mdproxy_with_multiple_ports_on_network(self): + router = self._create_router( + router_name=data_utils.rand_name("router-MDProxy"), + external_network_id=CONF.network.public_network_id) + (network_id, subnet_id) = self._create_net_subnet_router_interface( + router) + networks_ids = {"uuid": network_id} + instance, keypair = self._create_server_on_networks([networks_ids]) + instance2, keypair2 = self._create_server_on_networks([networks_ids]) + port_id = self._get_port_id(network_id, subnet_id, instance) + # Verify 1st instance. + self.verify_ssh(keypair=keypair, instance=instance, port_id=port_id) + self.verify_metadata_in_detail(instance=instance) + # Verify 2nd instance. + port_id2 = self._get_port_id(network_id, subnet_id, instance2) + self.verify_ssh(keypair=keypair2, instance=instance2, port_id=port_id2) + self.verify_metadata_in_detail(instance=instance2) + + @test.idempotent_id("eae21afc-50ea-42e5-9c49-2ee38cee9f06") + @test.services("compute", "network") + def test_mdproxy_with_multiple_metadata_ports(self): + router = self._create_router( + router_name=data_utils.rand_name("router-MDProxy"), + external_network_id=CONF.network.public_network_id) + (network_id1, subnet_id1) = self._create_net_subnet_router_interface( + router) + (network_id2, subnet_id2) = self._create_net_subnet_router_interface( + router) + net1 = {"uuid": network_id1} + net2 = {"uuid": network_id2} + instance, keypair = self._create_server_on_networks([net1]) + instance2, keypair2 = self._create_server_on_networks([net2]) + port_id1 = self._get_port_id(network_id1, subnet_id1, instance) + port_id2 = self._get_port_id(network_id2, subnet_id2, instance2) + self.verify_ssh(keypair=keypair, instance=instance, port_id=port_id1) + self.verify_metadata_in_detail(instance=instance) + self.verify_ssh(keypair=keypair2, instance=instance2, port_id=port_id2) + self.verify_metadata_in_detail(instance=instance2) + + @test.idempotent_id("29d44d7c-6ea1-4b30-a6c3-a2695c2486fe") + @test.attr(type=["negative"]) + @test.services("compute", "network") + def test_mdproxy_with_incorrect_password(self): + router = self._create_router( + router_name=data_utils.rand_name("router-MDProxy"), + external_network_id=CONF.network.public_network_id) + (network_id, subnet_id) = self._create_net_subnet_router_interface( + router) + networks_ids = {"uuid": network_id} + instance, keypair = self._create_server_on_networks([networks_ids]) + port_id = self._get_port_id(network_id, subnet_id, instance) + self.verify_ssh(keypair=keypair, instance=instance, port_id=port_id) + md_url = constants.MD_BASE_URL + "latest/meta-data/public-ipv4" + self._verify_md(md_url, sub_result="403 Forbidden")