From f20ec3a151d15079664ba2430b063bb7680fdeff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Seren?= Date: Fri, 21 May 2021 00:37:49 +0200 Subject: [PATCH] Add address scope module Change-Id: If02e03f124a8677cba42aa7019947e8f00ea476f --- ci/roles/address_scope/defaults/main.yml | 1 + ci/roles/address_scope/tasks/main.yml | 40 +++++ ci/run-collection.yml | 1 + meta/runtime.yml | 1 + plugins/modules/address_scope.py | 201 +++++++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 ci/roles/address_scope/defaults/main.yml create mode 100644 ci/roles/address_scope/tasks/main.yml create mode 100644 plugins/modules/address_scope.py diff --git a/ci/roles/address_scope/defaults/main.yml b/ci/roles/address_scope/defaults/main.yml new file mode 100644 index 00000000..77ef2d66 --- /dev/null +++ b/ci/roles/address_scope/defaults/main.yml @@ -0,0 +1 @@ +address_scope_name: "adrdess_scope" \ No newline at end of file diff --git a/ci/roles/address_scope/tasks/main.yml b/ci/roles/address_scope/tasks/main.yml new file mode 100644 index 00000000..1e035e1e --- /dev/null +++ b/ci/roles/address_scope/tasks/main.yml @@ -0,0 +1,40 @@ +--- +- name: Create address_scope + openstack.cloud.address_scope: + cloud: "{{ cloud }}" + name: "{{ address_scope_name }}" + shared: False + ip_version: "4" + register: create_address_scope + +- name: Verify address scope + assert: + that: + - create_address_scope is successful + - create_address_scope is changed + - create_address_scope.address_scope.name == address_scope_name + - create_address_scope.address_scope.is_shared == False + - create_address_scope.address_scope.ip_version == 4 + +- name: Update address scope + openstack.cloud.address_scope: + cloud: "{{ cloud }}" + name: "{{ address_scope_name }}" + shared: True + ip_version: "4" + register: update_address_scope + +- name: Verify updated IPv4 address scope + assert: + that: + - update_address_scope is successful + - update_address_scope is changed + - update_address_scope.address_scope.name == address_scope_name + - update_address_scope.address_scope.is_shared == True + - update_address_scope.address_scope.ip_version == 4 + +- name: Delete created address scope + openstack.cloud.address_scope: + cloud: "{{ cloud }}" + name: "{{ address_scope_name }}" + state: absent \ No newline at end of file diff --git a/ci/run-collection.yml b/ci/run-collection.yml index cfe9a7b9..a4331b52 100644 --- a/ci/run-collection.yml +++ b/ci/run-collection.yml @@ -4,6 +4,7 @@ gather_facts: true roles: + - { role: address_scope, tags: address_scope } - { role: auth, tags: auth } - { role: client_config, tags: client_config } - role: object_container diff --git a/meta/runtime.yml b/meta/runtime.yml index 934cc3ea..cff522ad 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,5 +1,6 @@ action_groups: openstack: + - address_scope - auth - baremetal_inspect - baremetal_inspect diff --git a/plugins/modules/address_scope.py b/plugins/modules/address_scope.py new file mode 100644 index 00000000..1d2aa303 --- /dev/null +++ b/plugins/modules/address_scope.py @@ -0,0 +1,201 @@ +#!/usr/bin/python +# coding: utf-8 -*- +# +# Copyright (c) 2021 by Uemit Seren +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = ''' +--- +module: address_scope +short_description: Create or delete address scopes from OpenStack +author: OpenStack Ansible SIG +description: + - Create or Delete address scopes from OpenStack. +options: + state: + description: + - Indicate desired state of the resource + choices: ['present', 'absent'] + default: present + type: str + name: + description: + - Name to be give to the address scope + required: true + type: str + project: + description: + - Unique name or ID of the project. + type: str + ip_version: + description: + - The IP version of the subnet 4 or 6 + default: 4 + type: str + choices: ['4', '6'] + shared: + description: + - Whether this address scope is shared or not. + type: bool + default: 'no' + extra_specs: + description: + - Dictionary with extra key/value pairs passed to the API + required: false + default: {} + type: dict +requirements: + - "python >= 3.6" + - "openstacksdk" + +extends_documentation_fragment: +- openstack.cloud.openstack +''' + +EXAMPLES = ''' +# Create an IPv4 address scope. +- openstack.cloud.address_scope: + cloud: mycloud + state: present + name: my_adress_scope + +# Create a shared IPv6 address scope for a given project. +- openstack.cloud.address_scope: + cloud: mycloud + state: present + ip_version: 6 + name: ipv6_address_scope + project: myproj + +# Delete address scope. +- openstack.cloud.address_scope: + cloud: mycloud + state: absent + name: my_adress_scope +''' + +RETURN = ''' +address_scope: + description: Dictionary describing the address scope. + returned: On success when I(state) is 'present' + type: complex + contains: + id: + description: Address Scope ID. + type: str + sample: "474acfe5-be34-494c-b339-50f06aa143e4" + name: + description: Address Scope name. + type: str + sample: "my_address_scope" + tenant_id: + description: The tenant ID. + type: str + sample: "861174b82b43463c9edc5202aadc60ef" + ip_version: + description: The IP version of the subnet 4 or 6. + type: str + sample: "4" + is_shared: + description: Indicates whether this address scope is shared across all tenants. + type: bool + sample: false + +''' + +from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule + + +class AddressScopeModule(OpenStackModule): + argument_spec = dict( + state=dict(default='present', choices=['absent', 'present']), + name=dict(required=True), + shared=dict(default=False, type='bool'), + ip_version=dict(type='str', default='4', choices=['4', '6']), + project=dict(default=None), + extra_specs=dict(type='dict', default=dict()) + ) + + def _needs_update(self, address_scope, filters=None): + """Decide if the given address_scope needs an update. + """ + ip_version = int(self.params['ip_version']) + if address_scope['is_shared'] != self.params['shared']: + return True + if ip_version and address_scope['ip_version'] != ip_version: + self.fail_json(msg='Cannot update ip_version in existing address scope') + return False + + def _system_state_change(self, address_scope, filters=None): + """Check if the system state would be changed.""" + state = self.params['state'] + if state == 'absent' and address_scope: + return True + if state == 'present': + if not address_scope: + return True + return self._needs_update(address_scope, filters) + return False + + def run(self): + + state = self.params['state'] + name = self.params['name'] + shared = self.params['shared'] + ip_version = self.params['ip_version'] + project = self.params['project'] + extra_specs = self.params['extra_specs'] + + if project is not None: + proj = self.conn.get_project(project) + if proj is None: + self.fail(msg='Project %s could not be found' % project) + project_id = proj['id'] + else: + project_id = self.conn.current_project_id + + address_scope = self.conn.network.find_address_scope(name_or_id=name) + if self.ansible.check_mode: + self.exit_json( + changed=self._system_state_change(address_scope) + ) + + if state == 'present': + changed = False + + if not address_scope: + kwargs = dict( + name=name, + ip_version=ip_version, + is_shared=shared, + tenant_id=project_id) + dup_args = set(kwargs.keys()) & set(extra_specs.keys()) + if dup_args: + raise ValueError('Duplicate key(s) {0} in extra_specs' + .format(list(dup_args))) + kwargs = dict(kwargs, **extra_specs) + address_scope = self.conn.network.create_address_scope(**kwargs) + changed = True + else: + if self._needs_update(address_scope): + address_scope = self.conn.network.update_address_scope(address_scope['id'], is_shared=shared) + changed = True + else: + changed = False + self.exit_json(changed=changed, address_scope=address_scope, id=address_scope['id']) + + elif state == 'absent': + if not address_scope: + self.exit(changed=False) + else: + self.conn.network.delete_address_scope(address_scope['id']) + self.exit_json(changed=True) + + +def main(): + module = AddressScopeModule() + module() + + +if __name__ == '__main__': + main()