From d80b873a69d81be0fc7e35334bde2b78d1a92d95 Mon Sep 17 00:00:00 2001 From: David Soper Date: Thu, 21 Dec 2017 14:15:07 -0600 Subject: [PATCH] UCS SAN Connectivity Policy module and integration test (#34009) * vhba template and integration test * SAN Connectivity Policies and integration test * VSANs and integration tests * WWNN/WWPN Pools and integration test * SAN Connectivity Policies only * option description spelled out (aliased to descr) removed docs for list of dictionaries syntax follow ACI indent style state absent only requires dn name match (not full prop match) --- .../ucs/ucs_san_connectivity.py | 235 ++++++++++++++++++ .../targets/ucs_san_connectivity/aliases | 6 + .../ucs_san_connectivity/tasks/main.yml | 132 ++++++++++ 3 files changed, 373 insertions(+) create mode 100644 lib/ansible/modules/remote_management/ucs/ucs_san_connectivity.py create mode 100644 test/integration/targets/ucs_san_connectivity/aliases create mode 100644 test/integration/targets/ucs_san_connectivity/tasks/main.yml diff --git a/lib/ansible/modules/remote_management/ucs/ucs_san_connectivity.py b/lib/ansible/modules/remote_management/ucs/ucs_san_connectivity.py new file mode 100644 index 00000000000..c895e8ecaff --- /dev/null +++ b/lib/ansible/modules/remote_management/ucs/ucs_san_connectivity.py @@ -0,0 +1,235 @@ +#!/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: ucs_san_connectivity +short_description: Configures SAN Connectivity Policies on Cisco UCS Manager +description: +- Configures SAN Connectivity Policies on Cisco UCS Manager. +- Examples can be used with the UCS Platform Emulator U(https://communities.cisco.com/ucspe). +extends_documentation_fragment: ucs +options: + state: + description: + - If C(present), will verify SAN Connectivity Policies are present and will create if needed. + - If C(absent), will verify SAN Connectivity Policies are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - Name of the SAN Connectivity Policy + required: yes + description: + description: + - Description for the SAN Connectivity Policy + aliases: [ descr ] + wwnn_pool: + description: + - WWNN Pool name + default: default + vhba_list: + description: + - List of vHBAs contained in the SAN Connectivity Policy + - Each list element has the following suboptions + - name (Name of the vHBA (required)) + - vhba_template (vHBA template (required)) + - adapter_policy ('' (default), Linux, Solaris, VMware, Windows, WindowsBoot, or default) + - order (string specifying vHBA assignment order (unspecified (default), '1', '2', etc.) + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure SAN Connectivity Policy + ucs_san_connectivity: + hostname: 172.16.143.150 + username: admin + password: password + name: Cntr-FC-Boot + wwnn_pool: WWNN-Pool + vhba_list: + - name: Fabric-A + vhba_template: vHBA-Template-A + adapter_policy: Linux + - name: Fabric-B + vhba_template: vHBA-Template-B + adapter_policy: Linux + +- name: Remove SAN Connectivity Policy + ucs_san_connectivity: + hostname: 172.16.143.150 + username: admin + password: password + name: Cntr-FC-Boot + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.remote_management.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str'), + descr=dict(type='str'), + wwnn_pool=dict(type='str', default='default'), + vhba_list=dict(type='list'), + state=dict(type='str', default='present', choices=['present', 'absent']), + san_connectivity_list=dict(type='list'), + ) + + # Note that use of san_connectivity_list is an experimental feature which allows multiple resource updates with a single UCSM connection. + # Support for san_connectivity_list may change or be removed once persistent UCS connections are supported. + # Either san_connectivity_list or name is required (user can specify either a list or single resource). + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_one_of=[ + ['san_connectivity_list', 'name'], + ], + mutually_exclusive=[ + ['san_connectivity_list', 'name'], + ], + ) + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.vnic.VnicSanConnPolicy import VnicSanConnPolicy + from ucsmsdk.mometa.vnic.VnicFcNode import VnicFcNode + from ucsmsdk.mometa.vnic.VnicFc import VnicFc + from ucsmsdk.mometa.vnic.VnicFcIf import VnicFcIf + + changed = False + try: + # Only documented use is a single resource, but to also support experimental + # feature allowing multiple updates all params are converted to a san_connectivity_list below. + + if module.params['san_connectivity_list']: + # directly use the list (single resource and list are mutually exclusive + san_connectivity_list = module.params['san_connectivity_list'] + else: + # single resource specified, create list from the current params + san_connectivity_list = [module.params] + for san_connectivity in san_connectivity_list: + mo_exists = False + props_match = False + # set default params. Done here to set values for lists which can't be done in the argument_spec + if not san_connectivity.get('descr'): + san_connectivity['descr'] = '' + if not san_connectivity.get('wwnn_pool'): + san_connectivity['wwnn_pool'] = 'default' + if san_connectivity.get('vhba_list'): + for vhba in san_connectivity['vhba_list']: + if not vhba.get('adapter_policy'): + vhba['adapter_policy'] = '' + if not vhba.get('order'): + vhba['order'] = 'unspecified' + # dn is /san-conn-pol- + dn = module.params['org_dn'] + '/san-conn-pol-' + san_connectivity['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + # check top-level mo props + kwargs = {} + kwargs['descr'] = san_connectivity['descr'] + if (mo.check_prop_match(**kwargs)): + # top-level props match, check next level mo/props + # vnicFcNode object + child_dn = dn + '/fc-node' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = {} + kwargs['ident_pool_name'] = san_connectivity['wwnn_pool'] + if (mo_1.check_prop_match(**kwargs)): + if not san_connectivity.get('vhba_list'): + props_match = True + else: + # check vnicFc props + for vhba in san_connectivity['vhba_list']: + child_dn = dn + '/fc-' + vhba['name'] + mo_2 = ucs.login_handle.query_dn(child_dn) + kwargs = {} + kwargs['adaptor_profile_name'] = vhba['adapter_policy'] + kwargs['order'] = vhba['order'] + kwargs['nw_templ_name'] = vhba['vhba_template'] + if (mo_2.check_prop_match(**kwargs)): + props_match = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = VnicSanConnPolicy( + parent_mo_or_dn=module.params['org_dn'], + name=san_connectivity['name'], + descr=san_connectivity['descr'], + ) + mo_1 = VnicFcNode( + parent_mo_or_dn=mo, + ident_pool_name=san_connectivity['wwnn_pool'], + addr='pool-derived', + ) + if san_connectivity.get('vhba_list'): + for vhba in san_connectivity['vhba_list']: + mo_2 = VnicFc( + parent_mo_or_dn=mo, + name=vhba['name'], + adaptor_profile_name=vhba['adapter_policy'], + nw_templ_name=vhba['vhba_template'], + order=vhba['order'], + ) + mo_2_1 = VnicFcIf( + parent_mo_or_dn=mo_2, + name='default', + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/ucs_san_connectivity/aliases b/test/integration/targets/ucs_san_connectivity/aliases new file mode 100644 index 00000000000..30cc9de4a27 --- /dev/null +++ b/test/integration/targets/ucs_san_connectivity/aliases @@ -0,0 +1,6 @@ +# Not enabled, but can be used with the UCS Platform Emulator or UCS hardware. +# Example integration_config.yml: +# --- +# ucs_hostname: 172.16.143.136 +# ucs_username: admin +# ucs_password: password diff --git a/test/integration/targets/ucs_san_connectivity/tasks/main.yml b/test/integration/targets/ucs_san_connectivity/tasks/main.yml new file mode 100644 index 00000000000..c237dd1c09b --- /dev/null +++ b/test/integration/targets/ucs_san_connectivity/tasks/main.yml @@ -0,0 +1,132 @@ +# Test code for the UCS modules +# Copyright 2017, David Soper (@dsoper2) + +- name: Test that we have a UCS host, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + + +# Setup (clean environment) +- name: SAN Connectivity Policies absent + ucs_san_connectivity: &san_connectivity_absent + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + name: Cntr-FC-Boot + state: absent + +# Test present (check_mode) +- name: SAN Connectivity Policies present (check_mode) + ucs_san_connectivity: &san_connectivity_present + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + name: Cntr-FC-Boot + wwnn_pool: WWNN-Pool + vhba_list: + - name: Fabric-A + vhba_template: vHBA-Template-A + adapter_policy: Linux + - name: Fabric-B + vhba_template: vHBA-Template-B + adapter_policy: Linux + check_mode: yes + register: cm_san_connectivity_present + + +# Present (normal mode) +- name: SAN Connectivity Policies present (normal mode) + ucs_san_connectivity: *san_connectivity_present + register: nm_san_connectivity_present + + +# Test present again (idempotent) +- name: SAN Connectivity Policies present again (check_mode) + ucs_san_connectivity: *san_connectivity_present + check_mode: yes + register: cm_san_connectivity_present_again + + +# Present again (normal mode) +- name: SAN Connectivity Policies present again (normal mode) + ucs_san_connectivity: *san_connectivity_present + register: nm_san_connectivity_present_again + + +# Verfiy present +- name: Verify SAN Connectivity Policies present results + assert: + that: + - cm_san_connectivity_present.changed == nm_san_connectivity_present.changed == true + - cm_san_connectivity_present_again.changed == nm_san_connectivity_present_again.changed == false + + +# Test change (check_mode) +- name: SAN Connectivity Policies description change (check_mode) + ucs_san_connectivity: &san_connectivity_change + <<: *san_connectivity_present + descr: Testing Ansible + check_mode: yes + register: cm_san_connectivity_descr_change + + +# Change (normal mode) +- name: SAN Connectivity Policies description change (normal mode) + ucs_san_connectivity: *san_connectivity_change + register: nm_san_connectivity_descr_change + + +# Test change again (idempotent) +- name: SAN Connectivity Policies description again (check_mode) + ucs_san_connectivity: *san_connectivity_change + check_mode: yes + register: cm_san_connectivity_descr_change_again + + +# Change again (normal mode) +- name: SAN Connectivity Policies description change again (normal mode) + ucs_san_connectivity: *san_connectivity_change + register: nm_san_connectivity_descr_change_again + + +# Verfiy change +- name: Verify SAN Connectivity Policies change results + assert: + that: + - cm_san_connectivity_descr_change.changed == nm_san_connectivity_descr_change.changed == true + - cm_san_connectivity_descr_change_again.changed == nm_san_connectivity_descr_change_again.changed == false + + +# Teardown (clean environment) +- name: SAN Connectivity Policies absent (check_mode) + ucs_san_connectivity: *san_connectivity_absent + check_mode: yes + register: cm_san_connectivity_absent + + +# Absent (normal mode) +- name: SAN Connectivity Policies absent (normal mode) + ucs_san_connectivity: *san_connectivity_absent + register: nm_san_connectivity_absent + + +# Test absent again (idempotent) +- name: SAN Connectivity Policies absent again (check_mode) + ucs_san_connectivity: *san_connectivity_absent + check_mode: yes + register: cm_san_connectivity_absent_again + + +# Absent again (normal mode) +- name: SAN Connectivity Policies absent again (normal mode) + ucs_san_connectivity: *san_connectivity_absent + register: nm_san_connectivity_absent_again + + +# Verfiy absent +- name: Verify SAN Connectivity Policies absent results + assert: + that: + - cm_san_connectivity_absent.changed == nm_san_connectivity_absent.changed == true + - cm_san_connectivity_absent_again.changed == nm_san_connectivity_absent_again.changed == false