1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> 5# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'community'} 13 14DOCUMENTATION = r''' 15--- 16module: mso_schema_site_vrf_region_hub_network 17short_description: Manage site-local VRF region hub network in schema template 18description: 19- Manage site-local VRF region hub network in schema template on Cisco ACI Multi-Site. 20- The 'Hub Network' feature was introduced in Multi-Site Orchestrator (MSO) version 3.0(1) for AWS and version 3.0(2) for Azure. 21author: 22- Cindy Zhao (@cizhao) 23options: 24 schema: 25 description: 26 - The name of the schema. 27 type: str 28 required: yes 29 site: 30 description: 31 - The name of the site. 32 type: str 33 required: yes 34 template: 35 description: 36 - The name of the template. 37 type: str 38 required: yes 39 vrf: 40 description: 41 - The name of the VRF. 42 type: str 43 required: yes 44 region: 45 description: 46 - The name of the region. 47 type: str 48 required: yes 49 hub_network: 50 description: 51 - The hub network to be managed. 52 type: dict 53 suboptions: 54 name: 55 description: 56 - The name of the hub network. 57 - The hub-default is the default created hub network. 58 type: str 59 required: yes 60 tenant: 61 description: 62 - The tenant name of the hub network. 63 type: str 64 required: yes 65 state: 66 description: 67 - Use C(present) or C(absent) for adding or removing. 68 - Use C(query) for listing an object or multiple objects. 69 type: str 70 choices: [ absent, present, query ] 71 default: present 72notes: 73- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. 74 This can cause silent corruption on concurrent access when changing/removing on object as 75 the wrong object may be referenced. This module is affected by this deficiency. 76seealso: 77- module: cisco.mso.mso_schema_site_vrf_region 78- module: cisco.mso.mso_schema_template_vrf 79extends_documentation_fragment: cisco.mso.modules 80''' 81 82EXAMPLES = r''' 83- name: Add a new site VRF region hub network 84 cisco.mso.mso_schema_site_vrf_region_hub_network: 85 host: mso_host 86 username: admin 87 password: SomeSecretPassword 88 schema: Schema1 89 site: Site1 90 template: Template1 91 vrf: VRF1 92 region: us-west-1 93 hub_network: 94 name: hub-default 95 tenant: infra 96 state: present 97 delegate_to: localhost 98 99- name: Remove a site VRF region hub network 100 cisco.mso.mso_schema_site_vrf_region_hub_network: 101 host: mso_host 102 username: admin 103 password: SomeSecretPassword 104 schema: Schema1 105 site: Site1 106 template: Template1 107 vrf: VRF1 108 state: absent 109 delegate_to: localhost 110 111- name: Query site VRF region hub network 112 cisco.mso.mso_schema_site_vrf_region_hub_network: 113 host: mso_host 114 username: admin 115 password: SomeSecretPassword 116 schema: Schema1 117 site: Site1 118 template: Template1 119 vrf: VRF1 120 region: us-west-1 121 state: query 122 delegate_to: localhost 123 register: query_result 124''' 125 126RETURN = r''' 127''' 128 129from ansible.module_utils.basic import AnsibleModule 130from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec, mso_hub_network_spec 131 132 133def main(): 134 argument_spec = mso_argument_spec() 135 argument_spec.update( 136 schema=dict(type='str', required=True), 137 site=dict(type='str', required=True), 138 template=dict(type='str', required=True), 139 vrf=dict(type='str', required=True), 140 region=dict(type='str', required=True), 141 hub_network=dict(type='dict', options=mso_hub_network_spec()), 142 state=dict(type='str', default='present', choices=['absent', 'present', 'query']), 143 ) 144 145 module = AnsibleModule( 146 argument_spec=argument_spec, 147 supports_check_mode=True, 148 required_if=[ 149 ['state', 'present', ['hub_network']], 150 ], 151 ) 152 153 schema = module.params.get('schema') 154 site = module.params.get('site') 155 template = module.params.get('template').replace(' ', '') 156 vrf = module.params.get('vrf') 157 region = module.params.get('region') 158 hub_network = module.params.get('hub_network') 159 state = module.params.get('state') 160 161 mso = MSOModule(module) 162 163 # Get schema objects 164 schema_id, schema_path, schema_obj = mso.query_schema(schema) 165 166 # Get template 167 templates = [t.get('name') for t in schema_obj.get('templates')] 168 if template not in templates: 169 mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) 170 171 # Get site 172 site_id = mso.lookup_site(site) 173 174 # Get site_idx 175 if 'sites' not in schema_obj: 176 mso.fail_json(msg="No site associated with template '{0}'. Associate the site with the template using mso_schema_site.".format(template)) 177 sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] 178 if (site_id, template) not in sites: 179 mso.fail_json(msg="Provided site-template association '{0}-{1}' does not exist.".format(site, template)) 180 181 # Schema-access uses indexes 182 site_idx = sites.index((site_id, template)) 183 # Path-based access uses site_id-template 184 site_template = '{0}-{1}'.format(site_id, template) 185 186 # Get VRF 187 vrf_ref = mso.vrf_ref(schema_id=schema_id, template=template, vrf=vrf) 188 vrfs = [v.get('vrfRef') for v in schema_obj.get('sites')[site_idx]['vrfs']] 189 vrfs_name = [mso.dict_from_ref(v).get('vrfName') for v in vrfs] 190 if vrf_ref not in vrfs: 191 mso.fail_json(msg="Provided vrf '{0}' does not exist. Existing vrfs: {1}".format(vrf, ', '.join(vrfs_name))) 192 vrf_idx = vrfs.index(vrf_ref) 193 194 # Get Region 195 regions = [r.get('name') for r in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions']] 196 if region not in regions: 197 mso.fail_json(msg="Provided region '{0}' does not exist. Existing regions: {1}".format(region, ', '.join(regions))) 198 region_idx = regions.index(region) 199 # Get Region object 200 region_obj = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx] 201 region_path = '/sites/{0}/vrfs/{1}/regions/{2}'.format(site_template, vrf, region) 202 203 # Get hub network 204 existing_hub_network = region_obj.get('cloudRsCtxProfileToGatewayRouterP') 205 if existing_hub_network is not None: 206 mso.existing = existing_hub_network 207 208 if state == 'query': 209 if not mso.existing: 210 mso.fail_json(msg="Hub network not found") 211 mso.exit_json() 212 213 ops = [] 214 215 mso.previous = mso.existing 216 if state == 'absent': 217 if mso.existing: 218 mso.sent = mso.existing = {} 219 ops.append(dict(op='remove', path=region_path + '/cloudRsCtxProfileToGatewayRouterP')) 220 ops.append(dict(op='replace', path=region_path + '/isTGWAttachment', value=False)) 221 222 elif state == 'present': 223 new_hub_network = dict( 224 name=hub_network.get('name'), 225 tenantName=hub_network.get('tenant'), 226 ) 227 payload = region_obj 228 payload.update( 229 cloudRsCtxProfileToGatewayRouterP=new_hub_network, 230 isTGWAttachment=True, 231 ) 232 233 mso.sanitize(payload, collate=True) 234 235 ops.append(dict(op='replace', path=region_path, value=mso.sent)) 236 237 mso.existing = new_hub_network 238 239 if not module.check_mode and mso.previous != mso.existing: 240 mso.request(schema_path, method='PATCH', data=ops) 241 242 mso.exit_json() 243 244 245if __name__ == "__main__": 246 main() 247