diff --git a/ci/roles/orchestration/defaults/main.yaml b/ci/roles/orchestration/defaults/main.yaml index 798cdf86..d419f1ab 100644 --- a/ci/roles/orchestration/defaults/main.yaml +++ b/ci/roles/orchestration/defaults/main.yaml @@ -1,2 +1,35 @@ --- stack_name: "test-stack" +expected_fields: + - added + - capabilities + - created_at + - deleted + - deleted_at + - description + - environment + - environment_files + - files + - files_container + - id + - is_rollback_disabled + - links + - name + - notification_topics + - outputs + - owner_id + - parameters + - parent_id + - replaced + - stack_name + - status + - status_reason + - tags + - template + - template_description + - template_url + - timeout_mins + - unchanged + - updated + - updated_at + - user_project_id diff --git a/ci/roles/orchestration/tasks/main.yaml b/ci/roles/orchestration/tasks/main.yaml index b205d46f..8cbecf51 100644 --- a/ci/roles/orchestration/tasks/main.yaml +++ b/ci/roles/orchestration/tasks/main.yaml @@ -5,6 +5,12 @@ # template is searched related to playbook location or as absolute path template: "roles/orchestration/files/hello-world.yaml" name: "{{ stack_name }}" + register: minimal_stack + +- name: Assert fields returned by create stack + assert: + that: item in minimal_stack.stack + loop: "{{ expected_fields }}" - name: List stacks openstack.cloud.stack_info: @@ -21,6 +27,11 @@ name: "{{ stack_name }}" register: test_stack +- name: Assert fields returned by stack info + assert: + that: item in test_stack.stack[0] + loop: "{{ expected_fields }}" + - assert: that: - test_stack is defined diff --git a/plugins/modules/stack.py b/plugins/modules/stack.py index baff7565..b36ed308 100644 --- a/plugins/modules/stack.py +++ b/plugins/modules/stack.py @@ -58,14 +58,12 @@ requirements: extends_documentation_fragment: - openstack.cloud.openstack ''' + EXAMPLES = ''' ---- - name: create stack - ignore_errors: True - register: stack_create openstack.cloud.stack: - name: "{{ stack_name }}" - tag: "{{ tag_name }}" + name: "teststack" + tag: "tag1,tag2" state: present template: "/path/to/my_stack.yaml" environment: @@ -75,62 +73,90 @@ EXAMPLES = ''' bmc_flavor: m1.medium bmc_image: CentOS key_name: default - private_net: "{{ private_net_param }}" node_count: 2 name: undercloud image: CentOS my_flavor: m1.large - external_net: "{{ external_net_param }}" ''' RETURN = ''' -id: - description: Stack ID. - type: str - sample: "97a3f543-8136-4570-920e-fd7605c989d6" - returned: always - stack: description: stack info - type: complex + type: dict returned: always contains: - action: - description: Action, could be Create or Update. + added: + description: List of resource objects that will be added. + type: list + capabilities: + description: AWS compatible template listing capabilities. + type: list + created_at: + description: Time when created. type: str - sample: "CREATE" - creation_time: - description: Time when the action has been made. + sample: "2016-07-05T17:38:12Z" + deleted: + description: A list of resource objects that will be deleted. + type: list + deleted_at: + description: Time when the deleted. type: str sample: "2016-07-05T17:38:12Z" description: - description: Description of the Stack provided in the heat template. + description: > + Description of the Stack provided in the heat + template. type: str sample: "HOT template to create a new instance and networks" + environment: + description: A JSON environment for the stack. + type: dict + environment_files: + description: > + An ordered list of names for environment files found + in the files dict. + type: list + files: + description: > + Additional files referenced in the template or + the environment + type: dict + files_container: + description: > + Name of swift container with child templates and + files. + type: str id: description: Stack ID. type: str sample: "97a3f543-8136-4570-920e-fd7605c989d6" - name: - description: Name of the Stack - type: str - sample: "test-stack" - identifier: - description: Identifier of the current Stack action. - type: str - sample: "test-stack/97a3f543-8136-4570-920e-fd7605c989d6" + is_rollback_disabled: + description: Whether the stack will support a rollback. + type: bool links: description: Links to the current Stack. type: list elements: dict - sample: "[{'href': 'http://foo:8004/v1/7f6a/stacks/test-stack/97a3f543-8136-4570-920e-fd7605c989d6']" + sample: "[{'href': 'http://foo:8004/v1/7f6a/stacks/test-stack/ + 97a3f543-8136-4570-920e-fd7605c989d6']" + name: + description: Name of the Stack + type: str + sample: "test-stack" + notification_topics: + description: Stack related events. + type: str + sample: "HOT template to create a new instance and networks" outputs: description: Output returned by the Stack. type: list elements: dict - sample: "{'description': 'IP address of server1 in private network', + sample: "[{'description': 'IP of server1 in private network', 'output_key': 'server1_private_ip', - 'output_value': '10.1.10.103'}" + 'output_value': '10.1.10.103'}]" + owner_id: + description: The ID of the owner stack if any. + type: str parameters: description: Parameters of the current Stack type: dict @@ -138,21 +164,70 @@ stack: 'OS::stack_id': '97a3f543-8136-4570-920e-fd7605c989d6', 'OS::stack_name': 'test-stack', 'stack_status': 'CREATE_COMPLETE', - 'stack_status_reason': 'Stack CREATE completed successfully', + 'stack_status_reason': + 'Stack CREATE completed successfully', 'status': 'COMPLETE', - 'template_description': 'HOT template to create a new instance and networks', + 'template_description': + 'HOT template to create a new instance and nets', 'timeout_mins': 60, 'updated_time': null}" + parent_id: + description: The ID of the parent stack if any. + type: str + replaced: + description: A list of resource objects that will be replaced. + type: str + stack_name: + description: Name of the stack. + type: str + status: + description: stack status. + type: str + status_reason: + description: > + Explaining how the stack transits to its current + status. + type: str + tags: + description: A list of strings used as tags on the stack + type: list + template: + description: A dict containing the template use for stack creation. + type: dict + template_description: + description: Stack template description text. + type: str + template_url: + description: The URL where a stack template can be found. + type: str + timeout_mins: + description: Stack operation timeout in minutes. + type: str + unchanged: + description: > + A list of resource objects that will remain unchanged + if a stack. + type: list + updated: + description: > + A list of resource objects that will have their + properties updated. + type: list + updated_at: + description: Timestamp of last update on the stack. + type: str + user_project_id: + description: The ID of the user project created for this stack. + type: str ''' - from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule class StackModule(OpenStackModule): argument_spec = dict( name=dict(required=True), - tag=dict(min_ver='0.28.0'), + tag=dict(), template=dict(), environment=dict(type='list', elements='str'), parameters=dict(default={}, type='dict'), @@ -162,7 +237,9 @@ class StackModule(OpenStackModule): ) module_kwargs = dict( - supports_check_mode=True + supports_check_mode=True, + required_if=[ + ('state', 'present', ('template',), True)] ) def _create_stack(self, stack, parameters): @@ -175,8 +252,7 @@ class StackModule(OpenStackModule): rollback=self.params['rollback'], **parameters) - stack = self.conn.get_stack(stack.id, None) - if stack.stack_status == 'CREATE_COMPLETE': + if stack.status == 'CREATE_COMPLETE': return stack else: self.fail_json(msg="Failure in creating stack: {0}".format(stack)) @@ -200,8 +276,10 @@ class StackModule(OpenStackModule): def _system_state_change(self, stack): state = self.params['state'] if state == 'present': - if not stack: - return True + # This method will always return True if state is present to + # include the case of stack update as there is no simple way + # to check if the stack will indeed be updated + return True if state == 'absent' and stack: return True return False @@ -209,11 +287,6 @@ class StackModule(OpenStackModule): def run(self): state = self.params['state'] name = self.params['name'] - # Check for required parameters when state == 'present' - if state == 'present': - for p in ['template']: - if not self.params[p]: - self.fail_json(msg='%s required with present state' % p) stack = self.conn.get_stack(name) @@ -227,14 +300,13 @@ class StackModule(OpenStackModule): else: stack = self._update_stack(stack, parameters) self.exit_json(changed=True, - stack=stack, - id=stack.id) + stack=stack.to_dict(computed=False)) elif state == 'absent': if not stack: changed = False else: changed = True - if not self.conn.delete_stack(name, wait=self.params['wait']): + if not self.conn.delete_stack(stack['id'], wait=self.params['wait']): self.fail_json(msg='delete stack failed for stack: %s' % name) self.exit_json(changed=changed)