From d248e61a3128fe285a18c9836d1c7d18b5b946e9 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Wed, 2 Dec 2015 14:53:26 +0100 Subject: [PATCH 1/7] Add test for NSX plugin that simulates a live lock --- .../network/nicira/NiciraRestClientTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/nicira/NiciraRestClientTest.java b/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/nicira/NiciraRestClientTest.java index 7fcab8001bb..3c5160cf958 100644 --- a/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/nicira/NiciraRestClientTest.java +++ b/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/nicira/NiciraRestClientTest.java @@ -20,6 +20,7 @@ package com.cloud.network.nicira; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; @@ -169,4 +170,45 @@ public class NiciraRestClientTest { verifyPrivate(client).invoke("execute", HttpRequestMatcher.eq(loginRequest), eq(401)); } } + + @Test + public void testExecuteLiveLockWhenControllerAllowsLoginAndFollowsWithUnauthorizedButDoesNotRediect() throws Exception { + when(mockResponse.getStatusLine()) + .thenReturn(HTTP_401_STATUSLINE) + .thenReturn(HTTP_200_STATUSLINE) + .thenReturn(HTTP_401_STATUSLINE) + .thenReturn(HTTP_200_STATUSLINE) + .thenReturn(HTTP_401_STATUSLINE) + .thenReturn(HTTP_200_STATUSLINE) + .thenReturn(HTTP_401_STATUSLINE) + .thenReturn(HTTP_200_STATUSLINE) + .thenReturn(HTTP_401_STATUSLINE); + when(httpClient.execute(eq(HTTP_HOST), HttpRequestMatcher.eq(request), eq(httpClientContext))) + .thenReturn(mockResponse) + .thenReturn(mockResponse) + .thenReturn(mockResponse) + .thenReturn(mockResponse) + .thenReturn(mockResponse); + when(httpClient.execute(eq(HTTP_HOST), HttpRequestMatcher.eq(loginRequest), eq(httpClientContext))) + .thenReturn(mockResponse) + .thenReturn(mockResponse) + .thenReturn(mockResponse) + .thenReturn(mockResponse); + final NiciraRestClient client = spy(NiciraRestClient.create() + .client(httpClient) + .clientContext(httpClientContext) + .hostname(LOCALHOST) + .username(ADMIN) + .password(ADMIN_PASSWORD) + .loginUrl(LOGIN_PATH) + .executionLimit(2) + .build()); + + try { + client.execute(request); + fail("Execution count should have been maxed out"); + } catch (final CloudstackRESTException e) { + assertThat(e.getMessage(), containsString("Reached max executions limit of ")); + } + } } From cfe14462a3380c053ad174e0016632e6024e3573 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Wed, 2 Dec 2015 14:53:56 +0100 Subject: [PATCH 2/7] Fix NSX rest client to not reset execution counter after a login --- .../java/com/cloud/network/nicira/NiciraRestClient.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/nicira/NiciraRestClient.java b/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/nicira/NiciraRestClient.java index 6ade3a53777..de58a42607f 100644 --- a/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/nicira/NiciraRestClient.java +++ b/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/nicira/NiciraRestClient.java @@ -90,7 +90,7 @@ public class NiciraRestClient extends BasicRestClient { if (HttpStatusCodeHelper.isUnauthorized(statusCode)) { return handleUnauthorizedResponse(request, previousStatusCode, response, statusCode); } else if (HttpStatusCodeHelper.isSuccess(statusCode)) { - return handleSuccessResponse(response); + return handleSuccessResponse(request, response); } else { throw new CloudstackRESTException("Unexpecetd status code: " + statusCode); } @@ -110,8 +110,10 @@ public class NiciraRestClient extends BasicRestClient { return execute(request, loginStatusCode); } - private CloseableHttpResponse handleSuccessResponse(final CloseableHttpResponse response) { - counter.resetExecutionCounter(); + private CloseableHttpResponse handleSuccessResponse(final HttpUriRequest request, final CloseableHttpResponse response) { + if (!request.getURI().getPath().contains(loginUrl)) { + counter.resetExecutionCounter(); + } return response; } From 86e836196ed3374912da805f598961b3fe9c9033 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Wed, 2 Dec 2015 15:42:38 +0100 Subject: [PATCH 3/7] Ignore pmd generated files during license check This has been happening for the NSX and BigSwitch plugins. The maven build prints something like: [WARNING] Unknown file extension: .../plugins/network-elements/nicira-nvp/.pmd [WARNING] Missing header in: .../plugins/network-elements/nicira-nvp/.pmdruleset.xml --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index de7bf7d3acd..a76b5fe7dcb 100644 --- a/pom.xml +++ b/pom.xml @@ -605,6 +605,7 @@ .checkstyle .project .classpath + .pmd* From bafc2313bb872efa07c01dca2fdff4bec9b8dbb6 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Fri, 4 Dec 2015 11:40:09 +0100 Subject: [PATCH 4/7] Move NSX integrationt test to new plugins folder --- test/integration/{smoke => plugins}/test_nicira_controller.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/integration/{smoke => plugins}/test_nicira_controller.py (100%) diff --git a/test/integration/smoke/test_nicira_controller.py b/test/integration/plugins/test_nicira_controller.py similarity index 100% rename from test/integration/smoke/test_nicira_controller.py rename to test/integration/plugins/test_nicira_controller.py From 1e820e3906023ae43dbd3f01d632fbef5bb77430 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Fri, 4 Dec 2015 11:46:08 +0100 Subject: [PATCH 5/7] Use logger to print debug messages during test --- test/integration/plugins/test_nicira_controller.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/integration/plugins/test_nicira_controller.py b/test/integration/plugins/test_nicira_controller.py index 229612d451d..197c3f60cc7 100644 --- a/test/integration/plugins/test_nicira_controller.py +++ b/test/integration/plugins/test_nicira_controller.py @@ -31,6 +31,7 @@ from marvin.lib.common import (get_domain, get_zone, get_template) from nose.plugins.attrib import attr from marvin.codes import (FAILED, PASS) import time +import logging class TestNiciraContoller(cloudstackTestCase): @@ -128,6 +129,11 @@ class TestNiciraContoller(cloudstackTestCase): cls.service_offering ] + cls.logger = logging.getLogger('TestNiciraContoller') + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + @classmethod def tearDownClass(cls): @@ -170,7 +176,7 @@ class TestNiciraContoller(cloudstackTestCase): if result_count == 0: raise Exception('Nicira controller did not return any Transport Zones') elif result_count > 1: - self.debug("Nicira controller returned %s Transport Zones, picking first one" % resultCount) + self.logger.debug("Nicira controller returned %s Transport Zones, picking first one" % resultCount) transport_zone_api_url = list_transport_zone_response['results'][0]['_href'] r3 = requests.get( "https://%s%s" % (controller_host, transport_zone_api_url), @@ -239,7 +245,7 @@ class TestNiciraContoller(cloudstackTestCase): self.test_cleanup.append(virtual_machine) list_vm_response = VirtualMachine.list(self.api_client, id=virtual_machine.id) - self.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) + self.logger.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) self.assertEqual(isinstance(list_vm_response, list), True, 'Response did not return a valid list') self.assertNotEqual(len(list_vm_response), 0, 'List of VMs is empty') @@ -261,7 +267,7 @@ class TestNiciraContoller(cloudstackTestCase): should be created without issues. """ nicira_slave = self.determine_slave_conroller(self.nicira_hosts, self.nicira_master_controller) - self.debug("Nicira slave controller is: %s " % nicira_slave) + self.logger.debug("Nicira slave controller is: %s " % nicira_slave) nicira_device = NiciraNvp.add( self.api_client, @@ -299,7 +305,7 @@ class TestNiciraContoller(cloudstackTestCase): self.test_cleanup.append(virtual_machine) list_vm_response = VirtualMachine.list(self.api_client, id=virtual_machine.id) - self.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) + self.logger.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) self.assertEqual(isinstance(list_vm_response, list), True, 'Response did not return a valid list') self.assertNotEqual(len(list_vm_response), 0, 'List of VMs is empty') From a633ef811324a8dbf868c85922235c16a08cb1a0 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Fri, 4 Dec 2015 12:10:55 +0100 Subject: [PATCH 6/7] Refactor test cases to reduce duplication --- .../plugins/test_nicira_controller.py | 71 +++++++++---------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/test/integration/plugins/test_nicira_controller.py b/test/integration/plugins/test_nicira_controller.py index 197c3f60cc7..dd015c9f3d8 100644 --- a/test/integration/plugins/test_nicira_controller.py +++ b/test/integration/plugins/test_nicira_controller.py @@ -52,11 +52,13 @@ class TestNiciraContoller(cloudstackTestCase): 'name': 'NiciraEnabledNetwork', 'displaytext': 'NiciraEnabledNetwork', 'guestiptype': 'Isolated', - 'supportedservices': 'SourceNat,Firewall,PortForwarding,Connectivity', + 'supportedservices': 'SourceNat,Dhcp,Dns,Firewall,PortForwarding,Connectivity', 'traffictype': 'GUEST', 'availability': 'Optional', 'serviceProviderList': { 'SourceNat': 'VirtualRouter', + 'Dhcp': 'VirtualRouter', + 'Dns': 'VirtualRouter', 'Firewall': 'VirtualRouter', 'PortForwarding': 'VirtualRouter', 'Connectivity': 'NiciraNvp' @@ -207,18 +209,20 @@ class TestNiciraContoller(cloudstackTestCase): else: raise Exception("None of the supplied hosts (%s) is a Nicira slave" % hosts) - @attr(tags = ["advanced", "smoke", "nicira"], required_hardware="true") - def test_01_nicira_controller(self): + + def add_nicira_device(self, hostname): nicira_device = NiciraNvp.add( self.api_client, None, self.physical_network_id, - hostname=self.nicira_master_controller, + hostname=hostname, username=self.nicira_credentials['username'], password=self.nicira_credentials['password'], transportzoneuuid=self.transport_zone_uuid) self.test_cleanup.append(nicira_device) + + def create_guest_network(self): network_services = { 'name' : 'nicira_enabled_network', 'displaytext' : 'nicira_enabled_network', @@ -232,7 +236,10 @@ class TestNiciraContoller(cloudstackTestCase): domainid=self.domain.id, ) self.test_cleanup.append(network) + return network + + def create_virtual_machine(self, network): virtual_machine = VirtualMachine.create( self.api_client, self.vm_services['small'], @@ -243,6 +250,24 @@ class TestNiciraContoller(cloudstackTestCase): mode=self.vm_services['mode'] ) self.test_cleanup.append(virtual_machine) + return virtual_machine + + + def get_routers_for_network(self, network): + return list_routers( + self.apiclient, + account='admin', + domainid=self.account.domainid, + networkid=network.id + ) + + + @attr(tags = ["advanced", "smoke", "nicira"], required_hardware="true") + def test_01_nicira_controller(self): + self.add_nicira_device(self.nicira_master_controller) + + network = self.create_guest_network() + virtual_machine = self.create_virtual_machine(network) list_vm_response = VirtualMachine.list(self.api_client, id=virtual_machine.id) self.logger.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) @@ -254,6 +279,7 @@ class TestNiciraContoller(cloudstackTestCase): self.assertEqual(vm_response.id, virtual_machine.id, 'Virtual machine in response does not match request') self.assertEqual(vm_response.state, 'Running', 'VM is not in Running state') + @attr(tags = ["advanced", "smoke", "nicira"], required_hardware="true") def test_02_nicira_controller_redirect(self): """ @@ -269,40 +295,10 @@ class TestNiciraContoller(cloudstackTestCase): nicira_slave = self.determine_slave_conroller(self.nicira_hosts, self.nicira_master_controller) self.logger.debug("Nicira slave controller is: %s " % nicira_slave) - nicira_device = NiciraNvp.add( - self.api_client, - None, - self.physical_network_id, - hostname=nicira_slave, - username=self.nicira_credentials['username'], - password=self.nicira_credentials['password'], - transportzoneuuid=self.transport_zone_uuid) - self.test_cleanup.append(nicira_device) + self.add_nicira_device(nicira_slave) - network_services = { - 'name' : 'nicira_enabled_network', - 'displaytext' : 'nicira_enabled_network', - 'zoneid' : self.zone.id, - 'networkoffering' : self.network_offering.id - } - network = Network.create( - self.api_client, - network_services, - accountid='admin', - domainid=self.domain.id, - ) - self.test_cleanup.append(network) - - virtual_machine = VirtualMachine.create( - self.api_client, - self.vm_services['small'], - accountid='admin', - domainid=self.domain.id, - serviceofferingid=self.service_offering.id, - networkids=[network.id], - mode=self.vm_services['mode'] - ) - self.test_cleanup.append(virtual_machine) + network = self.create_guest_network() + virtual_machine = self.create_virtual_machine(network) list_vm_response = VirtualMachine.list(self.api_client, id=virtual_machine.id) self.logger.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) @@ -313,4 +309,3 @@ class TestNiciraContoller(cloudstackTestCase): vm_response = list_vm_response[0] self.assertEqual(vm_response.id, virtual_machine.id, 'Virtual machine in response does not match request') self.assertEqual(vm_response.state, 'Running', 'VM is not in Running state') - From 811f254e4ddcc8de0bc0375f35783eea4e04ef6d Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Sat, 5 Dec 2015 15:51:07 +0100 Subject: [PATCH 7/7] Test NSX tunnel in guest network --- .../plugins/test_nicira_controller.py | 112 +++++++++++++++++- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/test/integration/plugins/test_nicira_controller.py b/test/integration/plugins/test_nicira_controller.py index dd015c9f3d8..6c625b6297b 100644 --- a/test/integration/plugins/test_nicira_controller.py +++ b/test/integration/plugins/test_nicira_controller.py @@ -24,10 +24,19 @@ from marvin.lib.base import ( NetworkOffering, NiciraNvp, ServiceOffering, + NATRule, + PublicIPAddress, Network, VirtualMachine ) -from marvin.lib.common import (get_domain, get_zone, get_template) +from marvin.lib.common import ( + get_domain, + get_zone, + get_template, + list_routers, + list_hosts, + findSuitableHostForMigration +) from nose.plugins.attrib import attr from marvin.codes import (FAILED, PASS) import time @@ -255,13 +264,74 @@ class TestNiciraContoller(cloudstackTestCase): def get_routers_for_network(self, network): return list_routers( - self.apiclient, + self.api_client, account='admin', - domainid=self.account.domainid, + domainid=self.domain.id, networkid=network.id ) + def get_hosts(self): + return list_hosts( + self.api_client, + account='admin', + domainid=self.domain.id + ) + + + def get_master_router(self, routers): + master = filter(lambda r: r.redundantstate == 'MASTER', routers) + self.logger.debug("Found %s master router(s): %s" % (master.size(), master)) + return master[0] + + + def distribute_vm_and_routers_by_hosts(self, virtual_machine, routers): + if len(routers) > 1: + router = self.get_router(routers) + self.logger.debug("Master Router VM is %s" % router) + else: + router = routers[0] + + if router.hostid == virtual_machine.hostid: + self.logger.debug("Master Router VM is on the same host as VM") + host = findSuitableHostForMigration(self.api_client, router.id) + if host is not None: + router.migrate(self.api_client, host) + self.logger.debug("Migrated Master Router VM to host %s" % host) + else: + self.fail('No suitable host to migrate Master Router VM to') + else: + self.logger.debug("Master Router VM is not on the same host as VM: %s, %s" % (router.hostid, virtual_machine.hostid)) + + + def acquire_publicip(self, network): + self.logger.debug("Associating public IP for network: %s" % network.name) + public_ip = PublicIPAddress.create( + self.api_client, + accountid='admin', + zoneid=self.zone.id, + domainid=self.domain.id, + networkid=network.id + ) + self.logger.debug("Associated %s with network %s" % (public_ip.ipaddress.ipaddress, network.id)) + self.test_cleanup.append(public_ip) + return public_ip + + + def create_natrule(self, vm, public_ip, network): + self.logger.debug("Creating NAT rule in network for vm with public IP") + nat_rule = NATRule.create( + self.api_client, + vm, + self.vm_services['small'], + ipaddressid=public_ip.ipaddress.id, + openfirewall=True, + networkid=network.id + ) + self.test_cleanup.append(nat_rule) + return nat_rule + + @attr(tags = ["advanced", "smoke", "nicira"], required_hardware="true") def test_01_nicira_controller(self): self.add_nicira_device(self.nicira_master_controller) @@ -309,3 +379,39 @@ class TestNiciraContoller(cloudstackTestCase): vm_response = list_vm_response[0] self.assertEqual(vm_response.id, virtual_machine.id, 'Virtual machine in response does not match request') self.assertEqual(vm_response.state, 'Running', 'VM is not in Running state') + + + @attr(tags = ["advanced", "smoke", "nicira"], required_hardware="true") + def test_03_nicira_tunnel_guest_network(self): + self.add_nicira_device(self.nicira_master_controller) + network = self.create_guest_network() + virtual_machine = self.create_virtual_machine(network) + public_ip = self.acquire_publicip(network) + nat_rule = self.create_natrule(virtual_machine, public_ip, network) + + list_vm_response = VirtualMachine.list(self.api_client, id=virtual_machine.id) + self.logger.debug("Verify listVirtualMachines response for virtual machine: %s" % virtual_machine.id) + + self.assertEqual(isinstance(list_vm_response, list), True, 'Response did not return a valid list') + self.assertNotEqual(len(list_vm_response), 0, 'List of VMs is empty') + + vm_response = list_vm_response[0] + self.assertEqual(vm_response.id, virtual_machine.id, 'Virtual machine in response does not match request') + self.assertEqual(vm_response.state, 'Running', 'VM is not in Running state') + + routers = self.get_routers_for_network(network) + + self.distribute_vm_and_routers_by_hosts(virtual_machine, routers) + + ssh_command = 'ping -c 3 google.com' + result = 'failed' + try: + self.logger.debug("SSH into VM: %s" % public_ip.ipaddress.ipaddress) + ssh = virtual_machine.get_ssh_client(ipaddress=public_ip.ipaddress.ipaddress) + self.logger.debug('Ping to google.com from VM') + result = str(ssh.execute(ssh_command)) + self.logger.debug("SSH result: %s; COUNT is ==> %s" % (result, result.count("3 packets received"))) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % (vmObj.get_ip(), e)) + + self.assertEqual(result.count('3 packets received'), 1, 'Ping to outside world from VM should be successful')