| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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 marvin.cloudstackTestCase import cloudstackTestCase, unittest |
| from marvin.lib.utils import (cleanup_resources, |
| validateList, |
| get_hypervisor_type, get_process_status) |
| from marvin.lib.base import (Account, |
| Cluster, |
| Configurations, |
| Host, |
| VPC, |
| VirtualMachine, |
| Network, |
| Router, |
| ServiceOffering, |
| NetworkOffering) |
| from marvin.lib.common import (get_zone, |
| get_template, |
| verifyNetworkState, |
| wait_for_cleanup, list_routers, list_hosts) |
| from nose.plugins.attrib import attr |
| from marvin.sshClient import SshClient |
| from distutils.util import strtobool |
| from pyVmomi import vim, vmodl |
| from marvin.lib.vcenter import Vcenter |
| import logging |
| |
| logger = logging.getLogger('TestPesistentNetwork') |
| stream_handler = logging.StreamHandler() |
| logger.setLevel(logging.DEBUG) |
| logger.addHandler(stream_handler) |
| |
| |
| class TestL2PersistentNetworks(cloudstackTestCase): |
| @classmethod |
| def setUpClass(cls): |
| cls.testClient = super(TestL2PersistentNetworks, cls).getClsTestClient() |
| cls.api_client = cls.testClient.getApiClient() |
| cls.hypervisor = cls.testClient.getHypervisorInfo() |
| |
| isKVM = cls.hypervisor.lower() in ["kvm"] |
| isOVSEnabled = False |
| hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__ |
| if isKVM : |
| # Test only if all the hosts use OVS |
| grepCmd = 'grep "network.bridge.type=openvswitch" /etc/cloudstack/agent/agent.properties' |
| hosts = list_hosts(cls.api_client, type='Routing', hypervisor='kvm') |
| for host in hosts : |
| if len(SshClient(host.ipaddress, port=22, user=hostConfig["username"], |
| passwd=hostConfig["password"]).execute(grepCmd)) != 0 : |
| isOVSEnabled = True |
| break |
| if isKVM and isOVSEnabled : |
| cls.skipTest(cls, "KVM with OVS doesn't support persistent networks, skipping") |
| |
| # Fill services from the external config file |
| cls.services = cls.testClient.getParsedTestDataConfig() |
| cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][ |
| 0].__dict__ |
| # Get Zone and templates |
| cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) |
| cls.template = get_template( |
| cls.api_client, |
| cls.zone.id, |
| cls.services["ostype"] |
| ) |
| |
| cls.services["virtual_machine"]["zoneid"] = cls.zone.id |
| cls.services["virtual_machine"]["template"] = cls.template.id |
| cls.service_offering = ServiceOffering.create( |
| cls.api_client, |
| cls.services["service_offering"] |
| ) |
| cls.l2_persistent_network_offering = cls.create_network_offering("nw_off_L2_persistent") |
| cls.isolated_persistent_network_offering = cls.create_network_offering("nw_off_isolated_persistent") |
| |
| # network will be deleted as part of account cleanup |
| cls._cleanup = [ |
| cls.service_offering, |
| cls.isolated_persistent_network_offering, |
| cls.l2_persistent_network_offering] |
| return |
| |
| @classmethod |
| def tearDownClass(cls): |
| try: |
| # Cleanup resources used |
| cleanup_resources(cls.api_client, cls._cleanup) |
| except Exception as e: |
| raise Exception("Warning: Exception during cleanup : %s" % e) |
| return |
| |
| def setUp(self): |
| self.apiclient = self.testClient.getApiClient() |
| self.dbclient = self.testClient.getDbConnection() |
| self.hypervisor = self.testClient.getHypervisorInfo() |
| self.cleanup = [] |
| return |
| |
| def tearDown(self): |
| try: |
| # Clean up, terminate the resources created |
| cleanup_resources(self.apiclient, self.cleanup) |
| self.cleanup[:] = [] |
| except Exception as e: |
| raise Exception("Warning: Exception during cleanup : %s" % e) |
| return |
| |
| @classmethod |
| def is_ssh_enabled(cls): |
| conf = Configurations.list(cls.apiclient, name="kvm.ssh.to.agent") |
| if not conf: |
| return False |
| else: |
| return bool(strtobool(conf[0].value)) if conf[0].value else False |
| |
| @classmethod |
| def create_network_offering(cls, network_offering_type): |
| network_offering = NetworkOffering.create( |
| cls.api_client, |
| cls.services[network_offering_type], |
| conservemode=False |
| ) |
| |
| # Update network offering state from disabled to enabled. |
| NetworkOffering.update( |
| network_offering, |
| cls.api_client, |
| id=network_offering.id, |
| state="enabled") |
| return network_offering |
| |
| def get_ssh_client(self, ip, username, password, retries=10): |
| """ Setup ssh client connection and return connection """ |
| try: |
| ssh_client = SshClient(ip, 22, username, password, retries) |
| except Exception as e: |
| raise unittest.SkipTest("Unable to create ssh connection: " % e) |
| |
| self.assertIsNotNone( |
| ssh_client, "Failed to setup ssh connection to ip=%s" % ip) |
| |
| return ssh_client |
| |
| def list_all_hosts_in_zone(self, zone_id): |
| hosts = Host.list( |
| self.apiclient, |
| type='Routing', |
| resourcestate='Enabled', |
| state='Up', |
| zoneid=zone_id |
| ) |
| return hosts |
| |
| ''' |
| Verifies creation of bridge on KVM host |
| ''' |
| def verify_bridge_creation(self, host, vlan_id): |
| username = self.hostConfig["username"] |
| password = self.hostConfig["password"] |
| try: |
| ssh_client = self.get_ssh_client(host.ipaddress, username, password) |
| res = ssh_client.execute("ip addr | grep breth1-" + str(vlan_id) + " > /dev/null 2>&1; echo $?") |
| return res[0] |
| except Exception as e: |
| self.fail(e) |
| |
| ''' |
| Gets all port groups on the host |
| ''' |
| def capture_host_portgroups(self, host): |
| host_portgroups = [] |
| for portgroup in host.config.network.portgroup: |
| host_portgroups.append(portgroup.spec.name) |
| return host_portgroups |
| |
| ''' |
| Fetches port group names based on VMware switch type - Distributed Virtual Switch(DVS) and |
| Standard Virtual Switch(SVS) |
| ''' |
| def get_port_group_name(self, switch_type, vlan_id, network_rate): |
| if switch_type == 'DVS': |
| return 'cloud.guest.' + str(vlan_id) + '.' + str(network_rate) + '.1-dvSwitch1' |
| elif switch_type == 'SVS': |
| return 'cloud.guest.' + str(vlan_id) + '.' + str(network_rate) + '.1-vSwitch1' |
| else: |
| return None |
| |
| ''' |
| Verifies creation of port group on the Distributed vSwitch or a host in a cluster connected to |
| a Standard vSwitch |
| ''' |
| def verify_port_group_creation(self, vlan_id): |
| config = self.get_vmware_dc_config(self.zone.id) |
| vc_object = Vcenter(config[0][0], config[0][1], 'P@ssword123') |
| dvs = vc_object.get_dvswitches() |
| port_group_present = False |
| if dvs is not None: |
| port_group_name = self.get_port_group_name('DVS', vlan_id, |
| self.isolated_persistent_network_offering.networkrate) |
| port_group_present = port_group_name in dvs[0]['dvswitch']['portgroupNameList'] |
| |
| else: |
| port_group_name = self.get_port_group_name('SVS', vlan_id, |
| self.isolated_persistent_network_offering.networkrate) |
| hosts = vc_object._get_obj([vim.HostSystem]) |
| host = hosts[0]['host'] |
| host_pg = self.capture_host_portgroups(host) |
| port_group_present = port_group_name in host_pg |
| return port_group_present |
| |
| ''' |
| Fetch vmware datacenter login details |
| ''' |
| def get_vmware_dc_config(self, zone_id): |
| zid = self.dbclient.execute("select id from data_center where uuid='%s';" % |
| zone_id) |
| vmware_dc_id = self.dbclient.execute( |
| "select vmware_data_center_id from vmware_data_center_zone_map where zone_id='%s';" % |
| zid[0]) |
| vmware_dc_config = self.dbclient.execute( |
| "select vcenter_host, username, password from vmware_data_center where id = '%s';" % vmware_dc_id[0]) |
| |
| return vmware_dc_config |
| |
| ''' |
| Verify VLAN creation on specific host in a cluster |
| ''' |
| def verify_vlan_network_creation(self, host, vlan_id): |
| username = self.hostConfig["username"] |
| password = self.hostConfig["password"] |
| try: |
| ssh_client = self.get_ssh_client(host.ipaddress, username, password) |
| res = ssh_client.execute( |
| "xe vlan-list | grep -x \"^\s*tag ( RO): \"" + str(vlan_id) + "> /dev/null 2>&1; echo $?") |
| return res[0] |
| except Exception as e: |
| self.fail(e) |
| |
| def verify_network_setup_on_host_per_cluster(self, hypervisor, vlan_id): |
| clusters = Cluster.list( |
| self.apiclient, |
| zoneid=self.zone.id, |
| allocationstate="Enabled", |
| listall=True |
| ) |
| for cluster in clusters: |
| hosts = Host.list(self.apiclient, |
| clusterid=cluster.id, |
| type="Routing", |
| state="Up", |
| resourcestate="Enabled") |
| host = hosts[0] |
| if hypervisor == "xenserver": |
| result = self.verify_vlan_network_creation(host, vlan_id) |
| self.assertEqual( |
| int(result), |
| 0, |
| "Failed to find vlan on host: " + host.name + " in cluster: " + cluster.name) |
| if hypervisor == "vmware": |
| result = self.verify_port_group_creation(vlan_id) |
| self.assertEqual( |
| result, |
| True, |
| "Failed to find port group on hosts of cluster: " + cluster.name) |
| |
| ''' |
| This test verifies that on creation of an Isolated network with network offering with isPersistent flag |
| set to true the corresponding network resources are created without having to deploy a VM - VR created |
| ''' |
| @attr(tags=["advanced", "isolated", "persistent", "network"], required_hardware="false") |
| def test_01_isolated_persistent_network(self): |
| network = Network.create( |
| self.apiclient, |
| self.services["isolated_network"], |
| networkofferingid=self.isolated_persistent_network_offering.id, |
| zoneid=self.zone.id) |
| self.cleanup.append(network) |
| networkVlan = network.vlan |
| response = verifyNetworkState( |
| self.apiclient, |
| network.id, |
| "implemented") |
| exceptionOccurred = response[0] |
| isNetworkInDesiredState = response[1] |
| exceptionMessage = response[2] |
| |
| if (exceptionOccurred or (not isNetworkInDesiredState)): |
| self.fail(exceptionMessage) |
| self.assertIsNotNone( |
| networkVlan, |
| "vlan must not be null for persistent network") |
| |
| router = Router.list(self.apiclient, networkid=network.id)[0] |
| router_host_id = router.hostid |
| host = Host.list(self.apiclient, id=router_host_id)[0] |
| if host.hypervisor.lower() in "kvm": |
| result = self.verify_bridge_creation(host, networkVlan) |
| self.assertEqual( |
| int(result), |
| 0, |
| "Failed to find bridge on the breth1$-{networkVlan}") |
| elif host.hypervisor.lower() in ["xenserver", "vmware"]: |
| self.verify_network_setup_on_host_per_cluster(host.hypervisor.lower(), networkVlan) |
| |
| ''' |
| This test verifies that on creation of an L2 network with network offering with isPersistent flag |
| set to true the corresponding network resources are created without having to deploy a VM - VR created |
| ''' |
| @attr(tags=["advanced", "l2", "persistent", "network"], required_hardware="false") |
| def test_02_L2_persistent_network(self): |
| network_vlan = 90 |
| network = Network.create( |
| self.apiclient, |
| self.services["l2_network"], |
| networkofferingid=self.l2_persistent_network_offering.id, |
| zoneid=self.zone.id, |
| vlan=network_vlan) |
| self.cleanup.append(network) |
| response = verifyNetworkState( |
| self.apiclient, |
| network.id, |
| "implemented") |
| exceptionOccurred = response[0] |
| isNetworkInDesiredState = response[1] |
| exceptionMessage = response[2] |
| |
| if (exceptionOccurred or (not isNetworkInDesiredState)): |
| self.fail(exceptionMessage) |
| self.assertIsNotNone( |
| network_vlan, |
| "vlan must not be null for persistent network") |
| |
| self.validate_persistent_network_resources_created_on_host(network_vlan) |
| |
| @attr(tags=["advanced", "l2", "persistent", "network"], required_hardware="false") |
| def test_03_deploy_and_destroy_VM_and_verify_network_resources_persist(self): |
| network_vlan = 99 |
| network = Network.create( |
| self.apiclient, |
| self.services["l2_network"], |
| networkofferingid=self.l2_persistent_network_offering.id, |
| zoneid=self.zone.id, |
| vlan=network_vlan) |
| self.cleanup.append(network) |
| response = verifyNetworkState( |
| self.apiclient, |
| network.id, |
| "implemented") |
| logger.debug(response) |
| exceptionOccurred = response[0] |
| isNetworkInDesiredState = response[1] |
| exceptionMessage = response[2] |
| |
| if (exceptionOccurred or (not isNetworkInDesiredState)): |
| self.fail(exceptionMessage) |
| self.assertIsNotNone( |
| network_vlan, |
| "vlan must not be null for persistent network") |
| try: |
| virtual_machine = VirtualMachine.create( |
| self.apiclient, |
| self.services["virtual_machine"], |
| networkids=[ |
| network.id], |
| serviceofferingid=self.service_offering.id) |
| |
| VirtualMachine.delete(virtual_machine, self.apiclient, expunge=True) |
| |
| self.validate_persistent_network_resources_created_on_host(network_vlan) |
| except Exception as e: |
| self.fail("Exception occurred: %s" % e) |
| return |
| |
| |
| def validate_persistent_network_resources_created_on_host(self, network_vlan): |
| hosts = self.list_all_hosts_in_zone(self.zone.id) |
| if self.hypervisor.lower() in "kvm": |
| for host in hosts: |
| result = self.verify_bridge_creation(host, network_vlan) |
| self.assertEqual( |
| int(result), |
| 0, |
| "Failed to find bridge on the breth1-" + str(network_vlan)) |
| elif self.hypervisor.lower() in ["xenserver", "vmware"]: |
| self.verify_network_setup_on_host_per_cluster(self.hypervisor.lower(), network_vlan) |