From 67aa98c30fd91649c7315860dbf2dcd35cf535e0 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Fri, 2 Nov 2018 19:22:20 +0100 Subject: [PATCH] msc_label: Manage label on ACI MultiSite (#47753) This includes the new msc_label module and integration tests. --- lib/ansible/modules/network/aci/msc_label.py | 181 +++++++++++ test/integration/targets/msc_label/aliases | 2 + .../targets/msc_label/tasks/main.yml | 283 ++++++++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 lib/ansible/modules/network/aci/msc_label.py create mode 100644 test/integration/targets/msc_label/aliases create mode 100644 test/integration/targets/msc_label/tasks/main.yml diff --git a/lib/ansible/modules/network/aci/msc_label.py b/lib/ansible/modules/network/aci/msc_label.py new file mode 100644 index 00000000000..44e974c57fa --- /dev/null +++ b/lib/ansible/modules/network/aci/msc_label.py @@ -0,0 +1,181 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: msc_label +short_description: Manage labels +description: +- Manage labels on Cisco ACI Multi-Site. +author: +- Dag Wieers (@dagwieers) +version_added: '2.8' +options: + label_id: + description: + - The ID of the label. + required: yes + label: + description: + - The name of the label. + required: yes + aliases: [ label_name, name ] + display_name: + description: + - The name of the label displayed in the web UI. + type: + description: + - The type of the label. + choices: [ site ] + default: site + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: msc +''' + +EXAMPLES = r''' +- name: Add a new label + msc_label: + host: msc_host + username: admin + password: SomeSecretPassword + name: north_europe + label_id: 101 + description: North European Datacenter + state: present + delegate_to: localhost + +- name: Remove a label + msc_label: + host: msc_host + username: admin + password: SomeSecretPassword + name: north_europe + state: absent + delegate_to: localhost + +- name: Query a label + msc_label: + host: msc_host + username: admin + password: SomeSecretPassword + name: north_europe + state: query + delegate_to: localhost + register: query_result + +- name: Query all labels + msc_label: + host: msc_host + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.aci.msc import MSCModule, msc_argument_spec, issubset + + +def main(): + argument_spec = msc_argument_spec() + argument_spec.update( + label=dict(type='str', required=False, aliases=['name', 'label_name']), + label_id=dict(type='str', required=False), + display_name=dict(type='str'), + type=dict(type='str', default='site', choices=['site']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['label']], + ['state', 'present', ['label']], + ], + ) + + label = module.params['label'] + label_id = module.params['label_id'] + label_type = module.params['type'] + display_name = module.params['display_name'] + state = module.params['state'] + + msc = MSCModule(module) + + path = 'labels' + + # Query for existing object(s) + if label_id is None and label is None: + msc.existing = msc.query_objs(path) + elif label_id is None: + msc.existing = msc.get_obj(path, displayName=label) + if msc.existing: + label_id = msc.existing['id'] + elif label is None: + msc.existing = msc.get_obj(path, id=label_id) + else: + msc.existing = msc.get_obj(path, id=label_id) + existing_by_name = msc.get_obj(path, displayName=label) + if existing_by_name and label_id != existing_by_name['id']: + msc.fail_json(msg="Provided label '{1}' with id '{2}' does not match existing id '{3}'.".format(label, label_id, existing_by_name['id'])) + + # If we found an existing object, continue with it + if label_id: + path = 'labels/{id}'.format(id=label_id) + + if state == 'query': + pass + + elif state == 'absent': + msc.previous = msc.existing + if msc.existing: + if module.check_mode: + msc.existing = {} + else: + msc.existing = msc.request(path, method='DELETE') + + elif state == 'present': + msc.previous = msc.existing + + msc.sanitize(dict( + id=label_id, + displayName=display_name, + type=label_type, + ), collate=True) + + if msc.existing: + if not issubset(msc.sent, msc.existing): + if module.check_mode: + msc.existing = msc.proposed + else: + msc.existing = msc.request(path, method='PUT', data=msc.sent) + else: + if module.check_mode: + msc.existing = msc.proposed + else: + msc.existing = msc.request(path, method='POST', data=msc.sent) + + msc.exit_json() + + +if __name__ == "__main__": + main() diff --git a/test/integration/targets/msc_label/aliases b/test/integration/targets/msc_label/aliases new file mode 100644 index 00000000000..cd28d3c3621 --- /dev/null +++ b/test/integration/targets/msc_label/aliases @@ -0,0 +1,2 @@ +# No ACI MultiSite infrastructure, so not enabled +unsupported diff --git a/test/integration/targets/msc_label/tasks/main.yml b/test/integration/targets/msc_label/tasks/main.yml new file mode 100644 index 00000000000..142acf16e1e --- /dev/null +++ b/test/integration/targets/msc_label/tasks/main.yml @@ -0,0 +1,283 @@ +# Test code for the MSC modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI MultiSite host, username and password + fail: + msg: 'Please define the following variables: msc_hostname, msc_username and msc_password.' + when: msc_hostname is not defined or msc_username is not defined or msc_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove label ansible_test2 + msc_label: &label_absent + host: '{{ msc_hostname }}' + username: '{{ msc_username }}' + password: '{{ msc_password }}' + validate_certs: '{{ msc_validate_certs | default(false) }}' + use_ssl: '{{ msc_use_ssl | default(true) }}' + use_proxy: '{{ msc_use_proxy | default(true) }}' + output_level: '{{ msc_output_level | default("info") }}' + label: ansible_test2 + state: absent + +- name: Remove label ansible_test + msc_label: + <<: *label_absent + label: ansible_test + register: cm_remove_label + + +# ADD LABEL +- name: Add label (check_mode) + msc_label: &label_present + host: '{{ msc_hostname }}' + username: '{{ msc_username }}' + password: '{{ msc_password }}' + validate_certs: '{{ msc_validate_certs | default(false) }}' + use_ssl: '{{ msc_use_ssl | default(true) }}' + use_proxy: '{{ msc_use_proxy | default(true) }}' + output_level: '{{ msc_output_level | default("info") }}' + label: ansible_test + state: present + check_mode: yes + register: cm_add_label + +- name: Verify cm_add_label + assert: + that: + - cm_add_label is changed + - cm_add_label.previous == {} + - cm_add_label.current.id is not defined + - cm_add_label.current.displayName == 'ansible_test' + - cm_add_label.current.type == 'site' + +- name: Add label (normal mode) + msc_label: *label_present + register: nm_add_label + +- name: nm_Verify add_label + assert: + that: + - nm_add_label is changed + - nm_add_label.previous == {} + - nm_add_label.current.id is defined + - nm_add_label.current.displayName == 'ansible_test' + - nm_add_label.current.type == 'site' + +- name: Add label again (check_mode) + msc_label: *label_present + check_mode: yes + register: cm_add_label_again + +- name: Verify cm_add_label_again + assert: + that: + - cm_add_label_again is not changed + - cm_add_label_again.previous.displayName == 'ansible_test' + - cm_add_label_again.previous.type == 'site' + - cm_add_label_again.current.id == nm_add_label.current.id + - cm_add_label_again.current.displayName == 'ansible_test' + - cm_add_label_again.current.type == 'site' + +- name: Add label again (normal mode) + msc_label: *label_present + register: nm_add_label_again + +- name: Verify nm_add_label_again + assert: + that: + - nm_add_label_again is not changed + - nm_add_label_again.previous.displayName == 'ansible_test' + - nm_add_label_again.previous.type == 'site' + - nm_add_label_again.current.id == nm_add_label.current.id + - nm_add_label_again.current.displayName == 'ansible_test' + - nm_add_label_again.current.type == 'site' + + +# CHANGE LABEL +- name: Change label (check_mode) + msc_label: + <<: *label_present + label_id: '{{ nm_add_label.current.id }}' + label: ansible_test2 + check_mode: yes + register: cm_change_label + +- name: Verify cm_change_label + assert: + that: + - cm_change_label is changed + - cm_change_label.current.id == nm_add_label.current.id + - cm_change_label.current.displayName == 'ansible_test2' + - cm_change_label.current.type == 'site' + +- name: Change label (normal mode) + msc_label: + <<: *label_present + label_id: '{{ nm_add_label.current.id }}' + label: ansible_test2 + output_level: debug + register: nm_change_label + +- name: Verify nm_change_label + assert: + that: + - nm_change_label is changed + - nm_change_label.current.id == nm_add_label.current.id + - nm_change_label.current.displayName == 'ansible_test2' + - nm_change_label.current.type == 'site' + +- name: Change label again (check_mode) + msc_label: + <<: *label_present + label_id: '{{ nm_add_label.current.id }}' + label: ansible_test2 + check_mode: yes + register: cm_change_label_again + +- name: Verify cm_change_label_again + assert: + that: + - cm_change_label_again is not changed + - cm_change_label_again.current.id == nm_add_label.current.id + - cm_change_label_again.current.displayName == 'ansible_test2' + - cm_change_label_again.current.type == 'site' + +- name: Change label again (normal mode) + msc_label: + <<: *label_present + label_id: '{{ nm_add_label.current.id }}' + label: ansible_test2 + register: nm_change_label_again + +- name: Verify nm_change_label_again + assert: + that: + - nm_change_label_again is not changed + - nm_change_label_again.current.id == nm_add_label.current.id + - nm_change_label_again.current.displayName == 'ansible_test2' + - nm_change_label_again.current.type == 'site' + + +# QUERY ALL LABELS +- name: Query all labels (check_mode) + msc_label: &label_query + host: '{{ msc_hostname }}' + username: '{{ msc_username }}' + password: '{{ msc_password }}' + validate_certs: '{{ msc_validate_certs | default(false) }}' + use_ssl: '{{ msc_use_ssl | default(true) }}' + use_proxy: '{{ msc_use_proxy | default(true) }}' + output_level: '{{ msc_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_labels + +- name: Query all labels (normal mode) + msc_label: *label_query + register: nm_query_all_labels + +- name: Verify query_all_labels + assert: + that: + - cm_query_all_labels is not changed + - nm_query_all_labels is not changed + # NOTE: Order of labels is not stable between calls + #- cm_query_all_labels == nm_query_all_labels + + +# QUERY A LABEL +- name: Query our label + msc_label: + <<: *label_query + label: ansible_test2 + check_mode: yes + register: cm_query_label + +- name: Query our label + msc_label: + <<: *label_query + label: ansible_test2 + register: nm_query_label + +- name: Verify query_label + assert: + that: + - cm_query_label is not changed + - cm_query_label.current.id == nm_add_label.current.id + - cm_query_label.current.displayName == 'ansible_test2' + - cm_query_label.current.type == 'site' + - nm_query_label is not changed + - nm_query_label.current.id == nm_add_label.current.id + - nm_query_label.current.displayName == 'ansible_test2' + - nm_query_label.current.type == 'site' + - cm_query_label == nm_query_label + + +# REMOVE LABEL +- name: Remove label (check_mode) + msc_label: *label_absent + check_mode: yes + register: cm_remove_label + +- name: Verify cm_remove_label + assert: + that: + - cm_remove_label is changed + - cm_remove_label.current == {} + +- name: Remove label (normal mode) + msc_label: *label_absent + register: nm_remove_label + +- name: Verify nm_remove_label + assert: + that: + - nm_remove_label is changed + - nm_remove_label.current == {} + +- name: Remove label again (check_mode) + msc_label: *label_absent + check_mode: yes + register: cm_remove_label_again + +- name: Verify cm_remove_label_again + assert: + that: + - cm_remove_label_again is not changed + - cm_remove_label_again.current == {} + +- name: Remove label again (normal mode) + msc_label: *label_absent + register: nm_remove_label_again + +- name: Verify nm_remove_label_again + assert: + that: + - nm_remove_label_again is not changed + - nm_remove_label_again.current == {} + + +# QUERY NON-EXISTING LABEL +- name: Query non-existing label (check_mode) + msc_label: + <<: *label_query + label: ansible_test + check_mode: yes + register: cm_query_non_label + +- name: Query non-existing label (normal mode) + msc_label: + <<: *label_query + label: ansible_test + register: nm_query_non_label + +# TODO: Implement more tests +- name: Verify query_non_label + assert: + that: + - cm_query_non_label is not changed + - nm_query_non_label is not changed + - cm_query_non_label == nm_query_non_label