Merge "Added method to return OpenStackVmInstance from Heat."
[snaps.git] / snaps / openstack / tests / create_stack_tests.py
index bee1340..d2b138e 100644 (file)
 # 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.
-import pkg_resources
 import time
 
+import pkg_resources
 from heatclient.exc import HTTPBadRequest
 from snaps import file_utils
-
 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
-
 from snaps.openstack.create_image import OpenStackImage
 
 try:
@@ -33,9 +31,9 @@ import uuid
 
 from snaps.openstack import create_stack
 from snaps.openstack.create_stack import StackSettings, StackSettingsError
-from snaps.openstack.tests import openstack_tests
+from snaps.openstack.tests import openstack_tests, create_instance_tests
 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
-from snaps.openstack.utils import heat_utils
+from snaps.openstack.utils import heat_utils, neutron_utils, nova_utils
 
 __author__ = 'spisarski'
 
@@ -46,13 +44,14 @@ class StackSettingsUnitTests(unittest.TestCase):
     """
     Tests the construction of the StackSettings class
     """
+
     def test_no_params(self):
         with self.assertRaises(StackSettingsError):
             StackSettings()
 
     def test_empty_config(self):
         with self.assertRaises(StackSettingsError):
-            StackSettings(config=dict())
+            StackSettings(**dict())
 
     def test_name_only(self):
         with self.assertRaises(StackSettingsError):
@@ -60,23 +59,25 @@ class StackSettingsUnitTests(unittest.TestCase):
 
     def test_config_with_name_only(self):
         with self.assertRaises(StackSettingsError):
-            StackSettings(config={'name': 'foo'})
+            StackSettings(**{'name': 'foo'})
 
     def test_config_minimum_template(self):
-        settings = StackSettings(config={'name': 'stack', 'template': 'foo'})
+        settings = StackSettings(**{'name': 'stack', 'template': 'foo'})
         self.assertEqual('stack', settings.name)
         self.assertEqual('foo', settings.template)
         self.assertIsNone(settings.template_path)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_config_minimum_template_path(self):
-        settings = StackSettings(config={'name': 'stack', 'template_path': 'foo'})
+        settings = StackSettings(**{'name': 'stack', 'template_path': 'foo'})
         self.assertEqual('stack', settings.name)
         self.assertIsNone(settings.template)
         self.assertEqual('foo', settings.template_path)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_minimum_template(self):
         settings = StackSettings(name='stack', template='foo')
@@ -84,7 +85,8 @@ class StackSettingsUnitTests(unittest.TestCase):
         self.assertEqual('foo', settings.template)
         self.assertIsNone(settings.template_path)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_minimum_template_path(self):
         settings = StackSettings(name='stack', template_path='foo')
@@ -92,11 +94,13 @@ class StackSettingsUnitTests(unittest.TestCase):
         self.assertEqual('foo', settings.template_path)
         self.assertIsNone(settings.template)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_all(self):
         env_values = {'foo': 'bar'}
-        settings = StackSettings(name='stack', template='bar', template_path='foo', env_values=env_values,
+        settings = StackSettings(name='stack', template='bar',
+                                 template_path='foo', env_values=env_values,
                                  stack_create_timeout=999)
         self.assertEqual('stack', settings.name)
         self.assertEqual('bar', settings.template)
@@ -107,8 +111,8 @@ class StackSettingsUnitTests(unittest.TestCase):
     def test_config_all(self):
         env_values = {'foo': 'bar'}
         settings = StackSettings(
-            config={'name': 'stack', 'template': 'bar', 'template_path': 'foo',
-                    'env_values': env_values, 'stack_create_timeout': 999})
+            **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
+               'env_values': env_values, 'stack_create_timeout': 999})
         self.assertEqual('stack', settings.name)
         self.assertEqual('bar', settings.template)
         self.assertEqual('foo', settings.template_path)
@@ -118,35 +122,50 @@ class StackSettingsUnitTests(unittest.TestCase):
 
 class CreateStackSuccessTests(OSIntegrationTestCase):
     """
-    Test for the CreateStack class defined in create_stack.py
+    Tests for the CreateStack class defined in create_stack.py
     """
 
     def setUp(self):
         """
-        Instantiates the CreateStack object that is responsible for downloading and creating an OS stack file
-        within OpenStack
+        Instantiates the CreateStack object that is responsible for downloading
+        and creating an OS stack file within OpenStack
         """
         super(self.__class__, self).__start__()
 
-        self.guid = str(uuid.uuid4())
-        self.heat_cli = heat_utils.heat_client(self.os_creds)
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_creds = self.admin_os_creds
+        self.heat_creds.project_name = self.admin_os_creds.project_name
+
+        self.heat_cli = heat_utils.heat_client(self.heat_creds)
         self.stack_creator = None
 
         self.image_creator = OpenStackImage(
-            self.os_creds, openstack_tests.cirros_image_settings(
-                name=self.__class__.__name__ + '-' + str(self.guid) + '-image'))
+            self.heat_creds, openstack_tests.cirros_image_settings(
+                name=self.guid + '-image',
+                image_metadata=self.image_metadata))
         self.image_creator.create()
 
         # Create Flavor
         self.flavor_creator = OpenStackFlavor(
             self.admin_os_creds,
-            FlavorSettings(name=self.guid + '-flavor-name', ram=128, disk=10, vcpus=1))
+            FlavorSettings(name=self.guid + '-flavor-name', ram=256, disk=10,
+                           vcpus=1))
         self.flavor_creator.create()
 
-        self.env_values = {'image_name': self.image_creator.image_settings.name,
-                           'flavor_name': self.flavor_creator.flavor_settings.name}
+        self.network_name = self.guid + '-net'
+        self.subnet_name = self.guid + '-subnet'
+        self.vm_inst_name = self.guid + '-inst'
 
-        self.heat_tmplt_path = pkg_resources.resource_filename('examples.heat', 'test_heat_template.yaml')
+        self.env_values = {
+            'image_name': self.image_creator.image_settings.name,
+            'flavor_name': self.flavor_creator.flavor_settings.name,
+            'net_name': self.network_name,
+            'subnet_name': self.subnet_name,
+            'inst_name': self.vm_inst_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'test_heat_template.yaml')
 
     def tearDown(self):
         """
@@ -177,64 +196,74 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
         Tests the creation of an OpenStack stack from Heat template file.
         """
         # Create Stack
-        # Set the default stack settings, then set any custom parameters sent from the app
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template_path=self.heat_tmplt_path,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        # Set the default stack settings, then set any custom parameters sent
+        # from the app
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
         created_stack = self.stack_creator.create()
         self.assertIsNotNone(created_stack)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack.name, retrieved_stack.name)
         self.assertEqual(created_stack.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
-        self.assertEquals(0, len(self.stack_creator.get_outputs()))
+        self.assertEqual(0, len(self.stack_creator.get_outputs()))
 
     def test_create_stack_template_dict(self):
         """
         Tests the creation of an OpenStack stack from a heat dict() object.
         """
         # Create Stack
-        # Set the default stack settings, then set any custom parameters sent from the app
+        # Set the default stack settings, then set any custom parameters sent
+        # from the app
         template_dict = heat_utils.parse_heat_template_str(
             file_utils.read_file(self.heat_tmplt_path))
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template=template_dict,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template=template_dict,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
         created_stack = self.stack_creator.create()
         self.assertIsNotNone(created_stack)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack.name, retrieved_stack.name)
         self.assertEqual(created_stack.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
-        self.assertEquals(0, len(self.stack_creator.get_outputs()))
+        self.assertEqual(0, len(self.stack_creator.get_outputs()))
 
     def test_create_delete_stack(self):
         """
-        Tests the creation then deletion of an OpenStack stack to ensure clean() does not raise an Exception.
+        Tests the creation then deletion of an OpenStack stack to ensure
+        clean() does not raise an Exception.
         """
         # Create Stack
         template_dict = heat_utils.parse_heat_template_str(
             file_utils.read_file(self.heat_tmplt_path))
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template=template_dict,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template=template_dict,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
         created_stack = self.stack_creator.create()
         self.assertIsNotNone(created_stack)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack.name, retrieved_stack.name)
         self.assertEqual(created_stack.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
-        self.assertEquals(0, len(self.stack_creator.get_outputs()))
-        self.assertEqual(create_stack.STATUS_CREATE_COMPLETE, self.stack_creator.get_status())
+        self.assertEqual(0, len(self.stack_creator.get_outputs()))
+        self.assertEqual(create_stack.STATUS_CREATE_COMPLETE,
+                         self.stack_creator.get_status())
 
         # Delete Stack manually
         heat_utils.delete_stack(self.heat_cli, created_stack)
@@ -242,14 +271,16 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
         end_time = time.time() + 90
         deleted = False
         while time.time() < end_time:
-            status = heat_utils.get_stack_status(self.heat_cli, retrieved_stack.id)
+            status = heat_utils.get_stack_status(self.heat_cli,
+                                                 retrieved_stack.id)
             if status == create_stack.STATUS_DELETE_COMPLETE:
                 deleted = True
                 break
 
         self.assertTrue(deleted)
 
-        # Must not throw an exception when attempting to cleanup non-existent stack
+        # Must not throw an exception when attempting to cleanup non-existent
+        # stack
         self.stack_creator.clean()
         self.assertIsNone(self.stack_creator.get_stack())
 
@@ -260,24 +291,186 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
         # Create Stack
         template_dict = heat_utils.parse_heat_template_str(
             file_utils.read_file(self.heat_tmplt_path))
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template=template_dict,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template=template_dict,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
         created_stack1 = self.stack_creator.create()
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack1.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack1.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack1.name, retrieved_stack.name)
         self.assertEqual(created_stack1.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
         self.assertEqual(0, len(self.stack_creator.get_outputs()))
 
         # Should be retrieving the instance data
-        stack_creator2 = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_creator2 = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                         stack_settings)
         stack2 = stack_creator2.create()
         self.assertEqual(created_stack1.id, stack2.id)
 
+    def test_retrieve_network_creators(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of the network creator.
+        """
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
+        created_stack = self.stack_creator.create()
+        self.assertIsNotNone(created_stack)
+
+        net_creators = self.stack_creator.get_network_creators()
+        self.assertIsNotNone(net_creators)
+        self.assertEqual(1, len(net_creators))
+        self.assertEqual(self.network_name, net_creators[0].get_network().name)
+
+        neutron = neutron_utils.neutron_client(self.os_creds)
+        net_by_name = neutron_utils.get_network(
+            neutron, network_name=net_creators[0].get_network().name)
+        self.assertEqual(net_creators[0].get_network(), net_by_name)
+        self.assertIsNotNone(neutron_utils.get_network_by_id(
+            neutron, net_creators[0].get_network().id))
+
+        self.assertEqual(1, len(net_creators[0].get_subnets()))
+        subnet = net_creators[0].get_subnets()[0]
+        subnet_by_name = neutron_utils.get_subnet(
+            neutron, subnet_name=subnet.name)
+        self.assertEqual(subnet, subnet_by_name)
+
+        subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id)
+        self.assertIsNotNone(subnet_by_id)
+        self.assertEqual(subnet_by_name, subnet_by_id)
+
+    def test_retrieve_vm_inst_creators(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of the network creator.
+        """
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
+        created_stack = self.stack_creator.create()
+        self.assertIsNotNone(created_stack)
+
+        vm_inst_creators = self.stack_creator.get_vm_inst_creators()
+        self.assertIsNotNone(vm_inst_creators)
+        self.assertEqual(1, len(vm_inst_creators))
+        self.assertEqual(self.vm_inst_name,
+                         vm_inst_creators[0].get_vm_inst().name)
+
+        nova = nova_utils.nova_client(self.admin_os_creds)
+        vm_inst_by_name = nova_utils.get_server(
+            nova, server_name=vm_inst_creators[0].get_vm_inst().name)
+        self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
+        self.assertIsNotNone(nova_utils.get_server_object_by_id(
+            nova, vm_inst_creators[0].get_vm_inst().id))
+
+
+class CreateComplexStackTests(OSIntegrationTestCase):
+    """
+    Tests for the CreateStack class defined in create_stack.py
+    """
+
+    def setUp(self):
+        """
+        Instantiates the CreateStack object that is responsible for downloading
+        and creating an OS stack file within OpenStack
+        """
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_creds = self.admin_os_creds
+        self.heat_creds.project_name = self.admin_os_creds.project_name
+
+        self.heat_cli = heat_utils.heat_client(self.heat_creds)
+        self.stack_creator = None
+
+        self.image_creator = OpenStackImage(
+            self.heat_creds, openstack_tests.cirros_image_settings(
+                name=self.guid + '-image',
+                image_metadata=self.image_metadata))
+        self.image_creator.create()
+
+        self.network_name = self.guid + '-net'
+        self.subnet_name = self.guid + '-subnet'
+        self.flavor1_name = self.guid + '-flavor1'
+        self.flavor2_name = self.guid + '-flavor2'
+        self.vm_inst1_name = self.guid + '-inst1'
+        self.vm_inst2_name = self.guid + '-inst2'
+        self.keypair_name = self.guid + '-kp'
+
+        self.env_values = {
+            'image1_name': self.image_creator.image_settings.name,
+            'image2_name': self.image_creator.image_settings.name,
+            'flavor1_name': self.flavor1_name,
+            'flavor2_name': self.flavor2_name,
+            'net_name': self.network_name,
+            'subnet_name': self.subnet_name,
+            'inst1_name': self.vm_inst1_name,
+            'inst2_name': self.vm_inst2_name,
+            'keypair_name': self.keypair_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        if self.image_creator:
+            try:
+                self.image_creator.clean()
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_connect_via_ssh_heat_vm(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of two VM instance creators and attempt to connect via
+        SSH to the first one with a floating IP.
+        """
+        stack_settings = StackSettings(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = create_stack.OpenStackHeatStack(
+            self.heat_creds, stack_settings,
+            [self.image_creator.image_settings])
+        created_stack = self.stack_creator.create()
+        self.assertIsNotNone(created_stack)
+
+        vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+            heat_keypair_option='private_key')
+        self.assertIsNotNone(vm_inst_creators)
+        self.assertEqual(2, len(vm_inst_creators))
+
+        for vm_inst_creator in vm_inst_creators:
+            if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
+                self.assertTrue(
+                    create_instance_tests.validate_ssh_client(vm_inst_creator))
+            else:
+                vm_settings = vm_inst_creator.instance_settings
+                self.assertEqual(0, len(vm_settings.floating_ip_settings))
+
 
 class CreateStackNegativeTests(OSIntegrationTestCase):
     """
@@ -287,9 +480,13 @@ class CreateStackNegativeTests(OSIntegrationTestCase):
     def setUp(self):
         super(self.__class__, self).__start__()
 
+        self.heat_creds = self.admin_os_creds
+        self.heat_creds.project_name = self.admin_os_creds.project_name
+
         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
         self.stack_creator = None
-        self.heat_tmplt_path = pkg_resources.resource_filename('examples.heat', 'test_heat_template.yaml')
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'test_heat_template.yaml')
 
     def tearDown(self):
         if self.stack_creator:
@@ -300,8 +497,10 @@ class CreateStackNegativeTests(OSIntegrationTestCase):
         """
         Expect an StackCreationError when the stack file does not exist
         """
-        stack_settings = StackSettings(name=self.stack_name, template_path=self.heat_tmplt_path)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackSettings(name=self.stack_name,
+                                       template_path=self.heat_tmplt_path)
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
         with self.assertRaises(HTTPBadRequest):
             self.stack_creator.create()
 
@@ -309,7 +508,9 @@ class CreateStackNegativeTests(OSIntegrationTestCase):
         """
         Expect an StackCreationError when the stack file does not exist
         """
-        stack_settings = StackSettings(name=self.stack_name, template_path='foo')
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackSettings(name=self.stack_name,
+                                       template_path='foo')
+        self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
+                                                             stack_settings)
         with self.assertRaises(IOError):
             self.stack_creator.create()