Added method to return OpenStackVmInstance from Heat.
[snaps.git] / snaps / openstack / tests / create_instance_tests.py
index 66b83cb..9c872bc 100644 (file)
@@ -23,14 +23,16 @@ import os
 from neutronclient.common.exceptions import InvalidIpForSubnetClient
 
 from snaps import file_utils
+from snaps.openstack import create_network, create_router
 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
 from snaps.openstack.create_image import OpenStackImage, ImageSettings
 from snaps.openstack.create_instance import (
     VmInstanceSettings, OpenStackVmInstance, FloatingIpSettings,
     VmInstanceSettingsError, FloatingIpSettingsError)
 from snaps.openstack.create_keypairs import OpenStackKeypair, KeypairSettings
-from snaps.openstack.create_network import OpenStackNetwork, PortSettings
-from snaps.openstack.create_router import OpenStackRouter
+from snaps.openstack.create_network import (
+    OpenStackNetwork, PortSettings, NetworkSettings)
+from snaps.openstack.create_router import OpenStackRouter, RouterSettings
 from snaps.openstack.create_security_group import (
     SecurityGroupSettings, OpenStackSecurityGroup, SecurityGroupRuleSettings,
     Direction, Protocol)
@@ -208,11 +210,22 @@ class FloatingIpSettingsUnitTests(unittest.TestCase):
         with self.assertRaises(FloatingIpSettingsError):
             FloatingIpSettings(**{'name': 'foo', 'router_name': 'bar'})
 
-    def test_name_port_router_only(self):
+    def test_name_port_router_name_only(self):
         settings = FloatingIpSettings(name='foo', port_name='foo-port',
                                       router_name='bar-router')
         self.assertEqual('foo', settings.name)
         self.assertEqual('foo-port', settings.port_name)
+        self.assertIsNone(settings.port_id)
+        self.assertEqual('bar-router', settings.router_name)
+        self.assertIsNone(settings.subnet_name)
+        self.assertTrue(settings.provisioning)
+
+    def test_name_port_router_id_only(self):
+        settings = FloatingIpSettings(name='foo', port_id='foo-port',
+                                      router_name='bar-router')
+        self.assertEqual('foo', settings.name)
+        self.assertEqual('foo-port', settings.port_id)
+        self.assertIsNone(settings.port_name)
         self.assertEqual('bar-router', settings.router_name)
         self.assertIsNone(settings.subnet_name)
         self.assertTrue(settings.provisioning)
@@ -223,6 +236,7 @@ class FloatingIpSettingsUnitTests(unittest.TestCase):
                'router_name': 'bar-router'})
         self.assertEqual('foo', settings.name)
         self.assertEqual('foo-port', settings.port_name)
+        self.assertIsNone(settings.port_id)
         self.assertEqual('bar-router', settings.router_name)
         self.assertIsNone(settings.subnet_name)
         self.assertTrue(settings.provisioning)
@@ -234,6 +248,7 @@ class FloatingIpSettingsUnitTests(unittest.TestCase):
                                       provisioning=False)
         self.assertEqual('foo', settings.name)
         self.assertEqual('foo-port', settings.port_name)
+        self.assertIsNone(settings.port_id)
         self.assertEqual('bar-router', settings.router_name)
         self.assertEqual('bar-subnet', settings.subnet_name)
         self.assertFalse(settings.provisioning)
@@ -245,6 +260,7 @@ class FloatingIpSettingsUnitTests(unittest.TestCase):
                'provisioning': False})
         self.assertEqual('foo', settings.name)
         self.assertEqual('foo-port', settings.port_name)
+        self.assertIsNone(settings.port_id)
         self.assertEqual('bar-router', settings.router_name)
         self.assertEqual('bar-subnet', settings.subnet_name)
         self.assertFalse(settings.provisioning)
@@ -299,7 +315,7 @@ class SimpleHealthCheck(OSIntegrationTestCase):
             # Create Flavor
             self.flavor_creator = OpenStackFlavor(
                 self.admin_os_creds,
-                FlavorSettings(name=guid + '-flavor-name', ram=128, disk=10,
+                FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
                                vcpus=1, metadata=self.flavor_metadata))
             self.flavor_creator.create()
         except Exception as e:
@@ -406,7 +422,7 @@ class CreateInstanceSimpleTests(OSIntegrationTestCase):
             # Create Flavor
             self.flavor_creator = OpenStackFlavor(
                 self.admin_os_creds,
-                FlavorSettings(name=guid + '-flavor-name', ram=128, disk=10,
+                FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
                                vcpus=2, metadata=self.flavor_metadata))
             self.flavor_creator.create()
 
@@ -475,15 +491,15 @@ class CreateInstanceSimpleTests(OSIntegrationTestCase):
             self.image_creator.image_settings)
 
         vm_inst = self.inst_creator.create()
-        self.assertEqual(1, len(
-            nova_utils.get_servers_by_name(self.nova, instance_settings.name)))
+        self.assertIsNotNone(nova_utils.get_server(
+            self.nova, vm_inst_settings=instance_settings))
 
         # Delete instance
         nova_utils.delete_vm_instance(self.nova, vm_inst)
 
         self.assertTrue(self.inst_creator.vm_deleted(block=True))
-        self.assertEqual(0, len(
-            nova_utils.get_servers_by_name(self.nova, instance_settings.name)))
+        self.assertIsNone(nova_utils.get_server(
+            self.nova, vm_inst_settings=instance_settings))
 
         # Exception should not be thrown
         self.inst_creator.clean()
@@ -544,7 +560,7 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase):
             # Create Flavor
             self.flavor_creator = OpenStackFlavor(
                 self.admin_os_creds,
-                FlavorSettings(name=guid + '-flavor-name', ram=128, disk=10,
+                FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
                                vcpus=2, metadata=self.flavor_metadata))
             self.flavor_creator.create()
 
@@ -670,7 +686,7 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase):
 
         self.assertEqual(ip_1, inst_creator.get_port_ip(self.port_1_name))
         self.assertTrue(inst_creator.vm_active(block=True))
-        self.assertEqual(vm_inst, inst_creator.get_vm_inst())
+        self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
 
     def test_ssh_client_fip_before_active(self):
         """
@@ -704,7 +720,7 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase):
 
         inst_creator.add_security_group(
             self.sec_grp_creator.get_security_group())
-        self.assertEqual(vm_inst, inst_creator.get_vm_inst())
+        self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
 
         self.assertTrue(validate_ssh_client(inst_creator))
 
@@ -742,10 +758,55 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase):
 
         inst_creator.add_security_group(
             self.sec_grp_creator.get_security_group())
-        self.assertEqual(vm_inst, inst_creator.get_vm_inst())
+        self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
 
         self.assertTrue(validate_ssh_client(inst_creator))
 
+    def test_ssh_client_fip_second_creator(self):
+        """
+        Tests the ability to access a VM via SSH and a floating IP via a
+        creator that is identical to the original creator.
+        """
+        port_settings = PortSettings(
+            name=self.port_1_name,
+            network_name=self.pub_net_config.network_settings.name)
+
+        instance_settings = VmInstanceSettings(
+            name=self.vm_inst_name,
+            flavor=self.flavor_creator.flavor_settings.name,
+            port_settings=[port_settings],
+            floating_ip_settings=[FloatingIpSettings(
+                name=self.floating_ip_name, port_name=self.port_1_name,
+                router_name=self.pub_net_config.router_settings.name)])
+
+        inst_creator = OpenStackVmInstance(
+            self.os_creds, instance_settings,
+            self.image_creator.image_settings,
+            keypair_settings=self.keypair_creator.keypair_settings)
+        self.inst_creators.append(inst_creator)
+
+        # block=True will force the create() method to block until the
+        vm_inst = inst_creator.create(block=True)
+        self.assertIsNotNone(vm_inst)
+
+        self.assertTrue(inst_creator.vm_active(block=True))
+
+        ip = inst_creator.get_port_ip(port_settings.name)
+        self.assertTrue(check_dhcp_lease(inst_creator, ip))
+
+        inst_creator.add_security_group(
+            self.sec_grp_creator.get_security_group())
+        self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
+
+        self.assertTrue(validate_ssh_client(inst_creator))
+
+        inst_creator2 = OpenStackVmInstance(
+            self.os_creds, instance_settings,
+            self.image_creator.image_settings,
+            keypair_settings=self.keypair_creator.keypair_settings)
+        inst_creator2.create()
+        self.assertTrue(validate_ssh_client(inst_creator2))
+
 
 class CreateInstancePortManipulationTests(OSIntegrationTestCase):
     """
@@ -792,7 +853,7 @@ class CreateInstancePortManipulationTests(OSIntegrationTestCase):
             # Create Flavor
             self.flavor_creator = OpenStackFlavor(
                 self.admin_os_creds,
-                FlavorSettings(name=guid + '-flavor-name', ram=128, disk=10,
+                FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
                                vcpus=2, metadata=self.flavor_metadata))
             self.flavor_creator.create()
         except Exception as e:
@@ -1141,11 +1202,11 @@ class CreateInstanceOnComputeHost(OSIntegrationTestCase):
         """
         from snaps.openstack.utils import nova_utils
         nova = nova_utils.nova_client(self.admin_os_creds)
-        zones = nova_utils.get_nova_availability_zones(nova)
+        zone_hosts = nova_utils.get_availability_zone_hosts(nova)
 
         # Create Instance on each server/zone
         ctr = 0
-        for zone in zones:
+        for zone in zone_hosts:
             inst_name = self.vm_inst_name + '-' + zone
             ctr += 1
             port_settings = PortSettings(
@@ -1166,7 +1227,7 @@ class CreateInstanceOnComputeHost(OSIntegrationTestCase):
         # Validate instances to ensure they've been deployed to the correct
         # server
         index = 0
-        for zone in zones:
+        for zone in zone_hosts:
             creator = self.inst_creators[index]
             self.assertTrue(creator.vm_active(block=True))
             info = creator.get_vm_info()
@@ -1387,7 +1448,7 @@ class CreateInstancePubPrivNetTests(OSIntegrationTestCase):
 
         vm_inst = self.inst_creator.create(block=True)
 
-        self.assertEqual(vm_inst, self.inst_creator.get_vm_inst())
+        self.assertEqual(vm_inst.id, self.inst_creator.get_vm_inst().id)
 
         # Effectively blocks until VM has been properly activated
         self.assertTrue(self.inst_creator.vm_active(block=True))
@@ -1456,7 +1517,7 @@ class InstanceSecurityGroupTests(OSIntegrationTestCase):
             # Create Flavor
             self.flavor_creator = OpenStackFlavor(
                 self.admin_os_creds,
-                FlavorSettings(name=self.guid + '-flavor-name', ram=128,
+                FlavorSettings(name=self.guid + '-flavor-name', ram=256,
                                disk=10, vcpus=2,
                                metadata=self.flavor_metadata))
             self.flavor_creator.create()
@@ -1717,17 +1778,19 @@ def validate_ssh_client(instance_creator):
     if ssh_active:
         ssh_client = instance_creator.ssh_client()
         if ssh_client:
-            out = ssh_client.exec_command('pwd')[1]
+            try:
+                out = ssh_client.exec_command('pwd')[1]
+                channel = out.channel
+                in_buffer = channel.in_buffer
+                pwd_out = in_buffer.read(1024)
+                if not pwd_out or len(pwd_out) < 10:
+                    return False
+                return True
+            finally:
+                ssh_client.close()
         else:
             return False
 
-        channel = out.channel
-        in_buffer = channel.in_buffer
-        pwd_out = in_buffer.read(1024)
-        if not pwd_out or len(pwd_out) < 10:
-            return False
-        return True
-
     return False
 
 
@@ -1788,7 +1851,7 @@ class CreateInstanceFromThreePartImage(OSIntegrationTestCase):
             # Create Flavor
             self.flavor_creator = OpenStackFlavor(
                 self.admin_os_creds,
-                FlavorSettings(name=guid + '-flavor-name', ram=128, disk=10,
+                FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
                                vcpus=2, metadata=self.flavor_metadata))
             self.flavor_creator.create()
 
@@ -1910,7 +1973,7 @@ class CreateInstanceMockOfflineTests(OSComponentTestCase):
             self.flavor_creator = OpenStackFlavor(
                 self.os_creds,
                 FlavorSettings(
-                    name=self.guid + '-flavor-name', ram=128, disk=10,
+                    name=self.guid + '-flavor-name', ram=256, disk=10,
                     vcpus=1))
             self.flavor_creator.create()
         except Exception as e:
@@ -2355,6 +2418,235 @@ class CreateInstanceMockOfflineTests(OSComponentTestCase):
         self.assertTrue(self.inst_creator.vm_active(block=True))
 
 
+class CreateInstanceTwoNetTests(OSIntegrationTestCase):
+    """
+    Tests the ability of two VMs to communicate when attached to separate
+    private networks that are tied together with a router.
+    """
+
+    def setUp(self):
+        """
+        Instantiates the CreateImage object that is responsible for downloading
+        and creating an OS image file within OpenStack
+        """
+        super(self.__class__, self).__start__()
+
+        cidr1 = '10.200.201.0/24'
+        cidr2 = '10.200.202.0/24'
+        static_gateway_ip1 = '10.200.201.1'
+        static_gateway_ip2 = '10.200.202.1'
+        self.ip1 = '10.200.201.5'
+        self.ip2 = '10.200.202.5'
+
+        self.nova = nova_utils.nova_client(self.os_creds)
+
+        # Initialize for tearDown()
+        self.image_creator = None
+        self.network_creators = list()
+        self.router_creator = None
+        self.flavor_creator = None
+        self.sec_grp_creator = None
+        self.inst_creators = list()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+        self.vm_inst1_name = self.guid + '-inst1'
+        self.vm_inst2_name = self.guid + '-inst2'
+        self.port_1_name = self.guid + '-vm1-port'
+        self.port_2_name = self.guid + '-vm2-port'
+        self.net_config_1 = NetworkSettings(
+            name=self.guid + '-net1',
+            subnet_settings=[
+                create_network.SubnetSettings(
+                    cidr=cidr1, name=self.guid + '-subnet1',
+                    gateway_ip=static_gateway_ip1)])
+        self.net_config_2 = NetworkSettings(
+            name=self.guid + '-net2',
+            subnet_settings=[
+                create_network.SubnetSettings(
+                    cidr=cidr2, name=self.guid + '-subnet2',
+                    gateway_ip=static_gateway_ip2)])
+
+        image_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
+        os_image_settings = openstack_tests.cirros_image_settings(
+            name=image_name, image_metadata=self.image_metadata)
+
+        try:
+            # Create Image
+            self.image_creator = OpenStackImage(self.os_creds,
+                                                os_image_settings)
+            self.image_creator.create()
+
+            # First network is public
+            self.network_creators.append(OpenStackNetwork(
+                self.os_creds, self.net_config_1))
+            # Second network is private
+            self.network_creators.append(OpenStackNetwork(
+                self.os_creds, self.net_config_2))
+            for network_creator in self.network_creators:
+                network_creator.create()
+
+            port_settings = [
+                create_network.PortSettings(
+                    name=self.guid + '-router-port1',
+                    ip_addrs=[{
+                        'subnet_name':
+                            self.net_config_1.subnet_settings[0].name,
+                        'ip': static_gateway_ip1
+                    }],
+                    network_name=self.net_config_1.name,
+                    project_name=self.os_creds.project_name),
+                create_network.PortSettings(
+                    name=self.guid + '-router-port2',
+                    ip_addrs=[{
+                        'subnet_name':
+                            self.net_config_2.subnet_settings[0].name,
+                        'ip': static_gateway_ip2
+                    }],
+                    network_name=self.net_config_2.name,
+                    project_name=self.os_creds.project_name)]
+
+            router_settings = RouterSettings(name=self.guid + '-pub-router',
+                                             port_settings=port_settings)
+            self.router_creator = create_router.OpenStackRouter(
+                self.os_creds, router_settings)
+            self.router_creator.create()
+
+            # Create Flavor
+            self.flavor_creator = OpenStackFlavor(
+                self.admin_os_creds,
+                FlavorSettings(name=self.guid + '-flavor-name', ram=512,
+                               disk=10, vcpus=2,
+                               metadata=self.flavor_metadata))
+            self.flavor_creator.create()
+
+            sec_grp_name = self.guid + '-sec-grp'
+            rule1 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
+                                              direction=Direction.ingress,
+                                              protocol=Protocol.icmp)
+            self.sec_grp_creator = OpenStackSecurityGroup(
+                self.os_creds,
+                SecurityGroupSettings(name=sec_grp_name,
+                                      rule_settings=[rule1]))
+            self.sec_grp_creator.create()
+        except:
+            self.tearDown()
+            raise
+
+    def tearDown(self):
+        """
+        Cleans the created objects
+        """
+        for inst_creator in self.inst_creators:
+            try:
+                inst_creator.clean()
+            except Exception as e:
+                logger.error(
+                    'Unexpected exception cleaning VM instance with message '
+                    '- %s', e)
+
+        if self.flavor_creator:
+            try:
+                self.flavor_creator.clean()
+            except Exception as e:
+                logger.error(
+                    'Unexpected exception cleaning flavor with message - %s',
+                    e)
+
+        if self.router_creator:
+            try:
+                self.router_creator.clean()
+            except Exception as e:
+                logger.error(
+                    'Unexpected exception cleaning router with message - %s',
+                    e)
+
+        for network_creator in self.network_creators:
+            try:
+                network_creator.clean()
+            except Exception as e:
+                logger.error(
+                    'Unexpected exception cleaning network with message - %s',
+                    e)
+
+        if self.sec_grp_creator:
+            try:
+                self.sec_grp_creator.clean()
+            except Exception as e:
+                logger.error(
+                    'Unexpected exception cleaning security group with message'
+                    ' - %s', e)
+
+        if self.image_creator and not self.image_creator.image_settings.exists:
+            try:
+                self.image_creator.clean()
+            except Exception as e:
+                logger.error(
+                    'Unexpected exception cleaning image with message - %s', e)
+
+        super(self.__class__, self).__clean__()
+
+    def test_ping_via_router(self):
+        """
+        Tests the creation of two OpenStack instances with one port on
+        different private networks wit a router in between to ensure that they
+        can ping
+        through
+        """
+        # Create ports/NICs for instance
+        ports_settings = []
+        ctr = 1
+        for network_creator in self.network_creators:
+            ports_settings.append(PortSettings(
+                name=self.guid + '-port-' + str(ctr),
+                network_name=network_creator.network_settings.name))
+            ctr += 1
+
+        # Configure instances
+        instance1_settings = VmInstanceSettings(
+            name=self.vm_inst1_name,
+            flavor=self.flavor_creator.flavor_settings.name,
+            userdata=_get_ping_userdata(self.ip2),
+            port_settings=[PortSettings(
+                name=self.port_1_name,
+                ip_addrs=[{
+                    'subnet_name':
+                        self.net_config_1.subnet_settings[0].name,
+                    'ip': self.ip1
+                }],
+                network_name=self.network_creators[0].network_settings.name)])
+        instance2_settings = VmInstanceSettings(
+            name=self.vm_inst2_name,
+            flavor=self.flavor_creator.flavor_settings.name,
+            userdata=_get_ping_userdata(self.ip1),
+            port_settings=[PortSettings(
+                name=self.port_2_name,
+                ip_addrs=[{
+                    'subnet_name':
+                        self.net_config_2.subnet_settings[0].name,
+                    'ip': self.ip2
+                }],
+                network_name=self.network_creators[1].network_settings.name)])
+
+        # Create instances
+        self.inst_creators.append(OpenStackVmInstance(
+            self.os_creds, instance1_settings,
+            self.image_creator.image_settings))
+        self.inst_creators.append(OpenStackVmInstance(
+            self.os_creds, instance2_settings,
+            self.image_creator.image_settings))
+
+        for inst_creator in self.inst_creators:
+            inst_creator.create(block=True)
+
+        # Check for DHCP lease
+        self.assertTrue(check_dhcp_lease(self.inst_creators[0], self.ip1))
+        self.assertTrue(check_dhcp_lease(self.inst_creators[1], self.ip2))
+
+        # Effectively blocks until VM has been properly activated
+        self.assertTrue(check_ping(self.inst_creators[0]))
+        self.assertTrue(check_ping(self.inst_creators[1]))
+
+
 def check_dhcp_lease(inst_creator, ip, timeout=160):
     """
     Returns true if the expected DHCP lease has been acquired
@@ -2382,3 +2674,43 @@ def check_dhcp_lease(inst_creator, ip, timeout=160):
         logger.debug('Full console output -\n' + full_log)
 
     return found
+
+
+def _get_ping_userdata(test_ip):
+    """
+    Returns the post VM creation script to be added into the VM's userdata
+    :param test_ip: the IP value to substitute into the script
+    :return: the bash script contents
+    """
+    if test_ip:
+        return ("#!/bin/sh\n\n"
+                "while true; do\n"
+                " ping -c 1 %s 2>&1 >/dev/null\n"
+                " RES=$?\n"
+                " if [ \"Z$RES\" = \"Z0\" ] ; then\n"
+                "  echo 'vPing OK'\n"
+                "  break\n"
+                " else\n"
+                "  echo 'vPing KO'\n"
+                " fi\n"
+                " sleep 1\n"
+                "done\n" % test_ip)
+    return None
+
+
+def check_ping(vm_creator, timeout=160):
+    """
+    Check for VM for ping result
+    """
+    tries = 0
+
+    while tries < timeout:
+        time.sleep(1)
+        p_console = vm_creator.get_console_output()
+        if "vPing OK" in p_console:
+            return True
+        elif "failed to read iid from metadata" in p_console or tries > 5:
+            return False
+        tries += 1
+
+    return False