summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormbeierl <mark.beierl@dell.com>2018-05-10 16:16:25 -0400
committerspisarski <s.pisarski@cablelabs.com>2018-06-20 15:45:46 -0600
commit0175feef9f00ea7595e4f7cc13f3b7a9c26e9a02 (patch)
tree875646d39b55059b6d3a8c6af86b162548d3a963
parentb2590d79a80dc12948d028ffee5b911b59daa777 (diff)
Adds Stack Update
Adds function to allow stack update to occur. Includes higher level object calls for stack update. Added blocking parameter to create() and update() Rebase screwed up some changes. Added new test to test_suite_builder.py and updated IntegrationTests.rst accordingly Change-Id: I4558befb3ea8ea7982faff79d1ebb54fbb3d44a7 Signed-off-by: mbeierl <mark.beierl@dell.com>
-rw-r--r--docs/how-to-use/IntegrationTests.rst11
-rw-r--r--snaps/config/stack.py3
-rw-r--r--snaps/openstack/create_stack.py47
-rw-r--r--snaps/openstack/tests/create_stack_tests.py170
-rw-r--r--snaps/openstack/tests/heat/agent-group.yaml2
-rw-r--r--snaps/openstack/utils/heat_utils.py18
-rw-r--r--snaps/openstack/utils/nova_utils.py11
-rw-r--r--snaps/test_suite_builder.py8
8 files changed, 243 insertions, 27 deletions
diff --git a/docs/how-to-use/IntegrationTests.rst b/docs/how-to-use/IntegrationTests.rst
index 98f1d1d..bc4b706 100644
--- a/docs/how-to-use/IntegrationTests.rst
+++ b/docs/how-to-use/IntegrationTests.rst
@@ -536,6 +536,17 @@ create_stack_tests.py - CreateStackNestedResourceTests
| | | initialized OpenStackVmInstance objects |
+---------------------------------------+---------------+-----------------------------------------------------------+
+create_stack_tests.py - CreateStackUpdateTests
+----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_update | 1 | Ensures that an OpenStackHeatStack can have the number of |
+| | | VMs updated and they are spawned and access can be |
+| | | obtained with SSH over floating IPs |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
create_stack_tests.py - CreateStackRouterTests
----------------------------------------------
diff --git a/snaps/config/stack.py b/snaps/config/stack.py
index 4d5db29..3fc820e 100644
--- a/snaps/config/stack.py
+++ b/snaps/config/stack.py
@@ -15,9 +15,12 @@
STACK_DELETE_TIMEOUT = 1200
STACK_COMPLETE_TIMEOUT = 1200
+STACK_UPDATE_TIMEOUT = 1200
POLL_INTERVAL = 3
STATUS_CREATE_FAILED = 'CREATE_FAILED'
STATUS_CREATE_COMPLETE = 'CREATE_COMPLETE'
+STATUS_UPDATE_FAILED = 'UPDATE_FAILED'
+STATUS_UPDATE_COMPLETE = 'UPDATE_COMPLETE'
STATUS_DELETE_COMPLETE = 'DELETE_COMPLETE'
STATUS_DELETE_FAILED = 'DELETE_FAILED'
diff --git a/snaps/openstack/create_stack.py b/snaps/openstack/create_stack.py
index 7ecf449..71e5d0a 100644
--- a/snaps/openstack/create_stack.py
+++ b/snaps/openstack/create_stack.py
@@ -102,7 +102,7 @@ class OpenStackHeatStack(OpenStackCloudObject, object):
logger.info('Found stack with name - ' + self.stack_settings.name)
return self.__stack
- def create(self):
+ def create(self, block=False):
"""
Creates the heat stack in OpenStack if it does not already exist and
returns the domain Stack object
@@ -118,7 +118,7 @@ class OpenStackHeatStack(OpenStackCloudObject, object):
self.stack_settings)
logger.info(
'Created stack with name - %s', self.stack_settings.name)
- if self.__stack and self.stack_complete(block=True):
+ if self.__stack and self.stack_complete(block=block):
logger.info('Stack is now active with name - %s',
self.stack_settings.name)
return self.__stack
@@ -128,6 +128,28 @@ class OpenStackHeatStack(OpenStackCloudObject, object):
logger.error('ERROR: STACK CREATION FAILED: %s', status)
raise StackCreationError('Failure while creating stack')
+ def update(self, env_vals, block=False):
+ """
+ Updates the heat stack in OpenStack
+ :param: env_vals - the values to update
+ :return: The Stack domain object or None
+ """
+ if self.__stack:
+ logger.info('Updating stack - %s', self.__stack.name)
+ heat_utils.update_stack(self.__heat_cli, self.__stack, env_vals)
+ if self.stack_updated(block=block):
+ logger.info('Stack %s is now updated with params: %s',
+ self.__stack.name, env_vals)
+ self.stack_settings.env_values = env_vals
+ self.__stack = heat_utils.get_stack_by_id(
+ self.__heat_cli, self.__stack.id)
+ return self.__stack
+ else:
+ status = heat_utils.get_stack_status_reason(self.__heat_cli,
+ self.__stack.id)
+ logger.error('ERROR: STACK UPDATE FAILED: %s', status)
+ raise StackUpdateError('Failure while updating stack')
+
def clean(self):
"""
Cleanse environment of all artifacts
@@ -220,6 +242,22 @@ class OpenStackHeatStack(OpenStackCloudObject, object):
snaps.config.stack.STATUS_CREATE_COMPLETE, block, timeout,
poll_interval, snaps.config.stack.STATUS_CREATE_FAILED)
+ def stack_updated(self, block=False,
+ timeout=snaps.config.stack.STACK_UPDATE_TIMEOUT,
+ poll_interval=snaps.config.stack.POLL_INTERVAL):
+ """
+ Returns true when the stack status returns the value of
+ expected_status_code
+ :param block: When true, thread will block until active or timeout
+ value in seconds has been exceeded (False)
+ :param timeout: The timeout value
+ :param poll_interval: The polling interval in seconds
+ :return: T/F
+ """
+ return self._stack_status_check(
+ snaps.config.stack.STATUS_UPDATE_COMPLETE, block, timeout,
+ poll_interval, snaps.config.stack.STATUS_UPDATE_FAILED)
+
def stack_deleted(self, block=False,
timeout=snaps.config.stack.STACK_DELETE_TIMEOUT,
poll_interval=snaps.config.stack.POLL_INTERVAL):
@@ -546,6 +584,11 @@ class StackCreationError(Exception):
Exception to be thrown when an stack cannot be created
"""
+class StackUpdateError(Exception):
+ """
+ Exception to be thrown when an stack update failed
+ """
+
class StackError(Exception):
"""
diff --git a/snaps/openstack/tests/create_stack_tests.py b/snaps/openstack/tests/create_stack_tests.py
index a320aa0..be4d6ea 100644
--- a/snaps/openstack/tests/create_stack_tests.py
+++ b/snaps/openstack/tests/create_stack_tests.py
@@ -22,7 +22,8 @@ import snaps
from snaps import file_utils
from snaps.config.flavor import FlavorConfig
from snaps.config.image import ImageConfig
-from snaps.config.stack import StackConfigError, StackConfig
+from snaps.config.stack import (StackConfigError, StackConfig,
+ STATUS_UPDATE_COMPLETE)
from snaps.openstack.create_flavor import OpenStackFlavor
from snaps.openstack.create_image import OpenStackImage
@@ -208,7 +209,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
@@ -239,7 +240,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
with self.assertRaises(StackCreationError):
- self.stack_creator.create()
+ self.stack_creator.create(block=True)
def test_create_stack_template_dict(self):
"""
@@ -256,7 +257,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
@@ -280,7 +281,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
@@ -324,7 +325,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- created_stack1 = self.stack_creator.create()
+ created_stack1 = self.stack_creator.create(block=True)
retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
created_stack1.id)
@@ -335,7 +336,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
# Should be retrieving the instance data
stack_creator2 = OpenStackHeatStack(self.os_creds, stack_settings)
- stack2 = stack_creator2.create()
+ stack2 = stack_creator2.create(block=True)
self.assertEqual(created_stack1.id, stack2.id)
def test_retrieve_network_creators(self):
@@ -349,7 +350,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
net_creators = self.stack_creator.get_network_creators()
@@ -390,7 +391,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
vm_inst_creators = self.stack_creator.get_vm_inst_creators()
@@ -502,7 +503,7 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase):
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings,
[self.image_creator.image_settings])
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
@@ -531,7 +532,7 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase):
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings,
[self.image_creator.image_settings])
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
derived_stack = create_stack.generate_creator(
@@ -647,7 +648,7 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase):
the retrieval of two VM instance creators and attempt to connect via
SSH to the first one with a floating IP.
"""
- created_stack = self.stack_creator.create()
+ created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(created_stack)
self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
@@ -660,6 +661,135 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase):
create_instance_tests.validate_ssh_client(vm_inst_creator))
+class CreateStackUpdateTests(OSIntegrationTestCase):
+ """
+ Tests to ensure that stack update commands work
+ """
+
+ def setUp(self):
+ self.user_roles = ['heat_stack_owner']
+
+ super(self.__class__, self).__start__()
+
+ self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+ self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+ self.stack_creator = None
+
+ self.image_creator = OpenStackImage(
+ self.os_creds, openstack_tests.cirros_image_settings(
+ name=self.guid + '-image',
+ image_metadata=self.image_metadata))
+ self.image_creator.create()
+
+ self.flavor_creator = OpenStackFlavor(
+ self.admin_os_creds,
+ FlavorConfig(
+ name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
+ self.flavor_creator.create()
+
+ env_values = {
+ 'network_name': self.guid + '-network',
+ 'public_network': self.ext_net_name,
+ 'agent_image': self.image_creator.image_settings.name,
+ 'agent_flavor': self.flavor_creator.flavor_settings.name,
+ 'key_name': self.guid + '-key',
+ }
+
+ heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'agent-group.yaml')
+ heat_resource_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'agent.yaml')
+
+ stack_settings = StackConfig(
+ name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+ template_path=heat_tmplt_path,
+ resource_files=[heat_resource_path],
+ env_values=env_values)
+
+ self.stack_creator = OpenStackHeatStack(
+ self.os_creds, stack_settings,
+ [self.image_creator.image_settings])
+
+ self.vm_inst_creators = list()
+
+ 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
+
+ if self.flavor_creator:
+ try:
+ self.flavor_creator.clean()
+ except:
+ pass
+
+ for vm_inst_creator in self.vm_inst_creators:
+ try:
+ keypair_settings = vm_inst_creator.keypair_settings
+ if keypair_settings and keypair_settings.private_filepath:
+ expanded_path = os.path.expanduser(
+ keypair_settings.private_filepath)
+ os.chmod(expanded_path, 0o755)
+ os.remove(expanded_path)
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_update(self):
+ """
+ Tests the update of an OpenStack stack from Heat template file
+ by changing the number of VM instances from 1 to 2, and
+ the retrieval of two VM instance creators and attempt to connect via
+ SSH to the first one with a floating IP.
+ """
+ created_stack = self.stack_creator.create(block=True)
+ self.assertIsNotNone(created_stack)
+
+ self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+ heat_keypair_option='private_key')
+ self.assertIsNotNone(self.vm_inst_creators)
+ self.assertEqual(1, len(self.vm_inst_creators))
+
+ for vm_inst_creator in self.vm_inst_creators:
+ self.assertTrue(
+ create_instance_tests.validate_ssh_client(vm_inst_creator))
+
+ env_values = {
+ 'network_name': self.guid + '-network',
+ 'public_network': self.ext_net_name,
+ 'agent_count': 2,
+ 'agent_image': self.image_creator.image_settings.name,
+ 'agent_flavor': self.flavor_creator.flavor_settings.name,
+ 'key_name': self.guid + '-key',
+ }
+
+ updated_stack = self.stack_creator.update(env_values, block=True)
+ self.assertIsNotNone(updated_stack)
+ self.assertEqual(STATUS_UPDATE_COMPLETE, updated_stack.status)
+
+ self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+ heat_keypair_option='private_key')
+ self.assertIsNotNone(self.vm_inst_creators)
+ self.assertEqual(2, len(self.vm_inst_creators))
+
+ for vm_inst_creator in self.vm_inst_creators:
+ self.assertTrue(
+ create_instance_tests.validate_ssh_client(vm_inst_creator))
+
+
class CreateStackRouterTests(OSIntegrationTestCase):
"""
Tests for the CreateStack class defined in create_stack.py where the
@@ -701,7 +831,7 @@ class CreateStackRouterTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- self.created_stack = self.stack_creator.create()
+ self.created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(self.created_stack)
def tearDown(self):
@@ -767,7 +897,7 @@ class CreateStackVolumeTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- self.created_stack = self.stack_creator.create()
+ self.created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(self.created_stack)
def tearDown(self):
@@ -853,7 +983,7 @@ class CreateStackFlavorTests(OSIntegrationTestCase):
template_path=self.heat_tmplt_path)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- self.created_stack = self.stack_creator.create()
+ self.created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(self.created_stack)
def tearDown(self):
@@ -919,7 +1049,7 @@ class CreateStackKeypairTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- self.created_stack = self.stack_creator.create()
+ self.created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(self.created_stack)
self.keypair_creators = list()
@@ -1003,7 +1133,7 @@ class CreateStackSecurityGroupTests(OSIntegrationTestCase):
env_values=self.env_values)
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
- self.created_stack = self.stack_creator.create()
+ self.created_stack = self.stack_creator.create(block=True)
self.assertIsNotNone(self.created_stack)
def tearDown(self):
@@ -1092,7 +1222,7 @@ class CreateStackNegativeTests(OSIntegrationTestCase):
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
with self.assertRaises(HTTPBadRequest):
- self.stack_creator.create()
+ self.stack_creator.create(block=True)
def test_bad_stack_file(self):
"""
@@ -1103,7 +1233,7 @@ class CreateStackNegativeTests(OSIntegrationTestCase):
self.stack_creator = OpenStackHeatStack(
self.os_creds, stack_settings)
with self.assertRaises(IOError):
- self.stack_creator.create()
+ self.stack_creator.create(block=True)
class CreateStackFailureTests(OSIntegrationTestCase):
@@ -1200,7 +1330,7 @@ class CreateStackFailureTests(OSIntegrationTestCase):
with self.assertRaises(StackError):
try:
- self.stack_creator.create()
+ self.stack_creator.create(block=True)
except StackError:
resources = heat_utils.get_resources(
self.heat_cli, self.stack_creator.get_stack().id)
diff --git a/snaps/openstack/tests/heat/agent-group.yaml b/snaps/openstack/tests/heat/agent-group.yaml
index 4b97495..494da0a 100644
--- a/snaps/openstack/tests/heat/agent-group.yaml
+++ b/snaps/openstack/tests/heat/agent-group.yaml
@@ -70,7 +70,7 @@ resources:
network:
type: OS::Neutron::Net
properties:
- name: { get_param: agent_count }
+ name: { get_param: network_name }
subnet:
type: OS::Neutron::Subnet
diff --git a/snaps/openstack/utils/heat_utils.py b/snaps/openstack/utils/heat_utils.py
index a90690b..17de020 100644
--- a/snaps/openstack/utils/heat_utils.py
+++ b/snaps/openstack/utils/heat_utils.py
@@ -144,6 +144,24 @@ def create_stack(heat_cli, stack_settings):
return get_stack_by_id(heat_cli, stack_id=stack['stack']['id'])
+def update_stack(heat_cli, stack, env_vals):
+ """
+ Updates the specified parameters in the stack
+ :param heat_cli: the OpenStack heat client object
+ :param stack_settings: the stack configuration
+ """
+ args = dict()
+
+ args['stack_name'] = stack.name
+ args['existing'] = True
+
+ if env_vals:
+ args['parameters'] = env_vals
+ heat_cli.stacks.update(stack.id, **args)
+ else:
+ logger.warn('Stack not updated, env_vals are None')
+
+
def delete_stack(heat_cli, stack):
"""
Deletes the Heat stack
diff --git a/snaps/openstack/utils/nova_utils.py b/snaps/openstack/utils/nova_utils.py
index 8be9b2a..005b56f 100644
--- a/snaps/openstack/utils/nova_utils.py
+++ b/snaps/openstack/utils/nova_utils.py
@@ -206,9 +206,14 @@ def __map_os_server_obj_to_vm_inst(neutron, keystone, os_server,
network = neutron_utils.get_network(
neutron, keystone, network_name=net_name,
project_name=project_name)
- ports = neutron_utils.get_ports(neutron, network, ips)
- for port in ports:
- out_ports.append(port)
+ if network:
+ ports = neutron_utils.get_ports(neutron, network, ips)
+ for port in ports:
+ out_ports.append(port)
+ else:
+ raise NovaException(
+ 'Unable to locate network in project {} with '
+ 'name {}'.format(project_name, net_name))
volumes = None
if hasattr(os_server, 'os-extended-volumes:volumes_attached'):
diff --git a/snaps/test_suite_builder.py b/snaps/test_suite_builder.py
index 35a39f2..1fe1f8a 100644
--- a/snaps/test_suite_builder.py
+++ b/snaps/test_suite_builder.py
@@ -94,7 +94,8 @@ from snaps.openstack.tests.create_stack_tests import (
StackSettingsUnitTests, CreateStackSuccessTests, CreateStackNegativeTests,
CreateStackFlavorTests, CreateStackFloatingIpTests,
CreateStackNestedResourceTests, CreateStackKeypairTests,
- CreateStackVolumeTests, CreateStackSecurityGroupTests)
+ CreateStackVolumeTests, CreateStackSecurityGroupTests,
+ CreateStackUpdateTests)
from snaps.openstack.tests.create_user_tests import (
UserSettingsUnitTests, CreateUserSuccessTests)
from snaps.openstack.tests.create_volume_tests import (
@@ -683,6 +684,11 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name,
ext_net_name=ext_net_name, use_keystone=use_keystone,
flavor_metadata=flavor_metadata, image_metadata=image_metadata,
log_level=log_level))
+ suite.addTest(OSIntegrationTestCase.parameterize(
+ CreateStackUpdateTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, use_keystone=use_keystone,
+ flavor_metadata=flavor_metadata, image_metadata=image_metadata,
+ log_level=log_level))
def add_ansible_integration_tests(suite, os_creds, ext_net_name,