1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> 5# Copyright: (c) 2020, Shreyas Srish (@shrsr) <ssrish@cisco.com> 6# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) 7 8from __future__ import absolute_import, division, print_function 9__metaclass__ = type 10 11ANSIBLE_METADATA = {'metadata_version': '1.1', 12 'status': ['preview'], 13 'supported_by': 'community'} 14 15DOCUMENTATION = r''' 16--- 17module: mso_tenant_site 18short_description: Manage tenants with cloud sites. 19description: 20- Manage tenants with cloud sites on Cisco ACI Multi-Site. 21author: 22- Shreyas Srish (@shrsr) 23options: 24 tenant: 25 description: 26 - The name of the tenant. 27 type: str 28 required: yes 29 site: 30 description: 31 - The name of the site. 32 - This can either be cloud site or non-cloud site. 33 type: str 34 aliases: [ name ] 35 cloud_account: 36 description: 37 - Required for cloud site. 38 - Account id of AWS in the form '000000000000'. 39 - Account id of Azure in the form 'uni/tn-(tenant_name)/act-[(subscription_id)]-azure_vendor-azure'. 40 - Example values inside account id of Azure '(tenant_name)=tenant_test and (subscription_id)=10'. 41 type: str 42 security_domains: 43 description: 44 - List of security domains for cloud sites. 45 type: list 46 elements: str 47 default: [] 48 aws_account_org: 49 description: 50 - AWS account for organization. 51 default: false 52 type: bool 53 aws_trusted: 54 description: 55 - AWS account's access in trusted mode. Credentials are required, when set to false. 56 type: bool 57 aws_access_key: 58 description: 59 - AWS account's access key id. This is required when aws_trusted is set to false. 60 type: str 61 azure_access_type: 62 description: 63 - Managed mode for Azure. 64 - Unmanaged mode for Azure. 65 - Shared mode if the attribute is not specified. 66 choices: [ managed, unmanaged, shared ] 67 default: shared 68 type: str 69 azure_active_directory_id: 70 description: 71 - Azure account's active directory id. 72 - This attribute is required when azure_access_type is in unmanaged mode. 73 type: str 74 azure_active_directory_name: 75 description: 76 - Azure account's active directory name. Example being 'CiscoINSBUAd' as active directory name. 77 - This attribute is required when azure_access_type is in unmanaged mode. 78 type: str 79 azure_subscription_id: 80 description: 81 - Azure account's subscription id. 82 - This attribute is required when azure_access_type is either in managed mode or unmanaged mode. 83 type: str 84 azure_application_id: 85 description: 86 - Azure account's application id. 87 - This attribute is required when azure_access_type is either in managed mode or unmanaged mode. 88 type: str 89 azure_credential_name: 90 description: 91 - Azure account's credential name. 92 - This attribute is required when azure_access_type is in unmanaged mode. 93 type: str 94 secret_key: 95 description: 96 - secret key of AWS for untrusted account. Required when aws_trusted is set to false. 97 - secret key of Azure account for unmanaged identity. Required in unmanaged mode of Azure account. 98 type: str 99 state: 100 description: 101 - Use C(present) or C(absent) for adding or removing. 102 - Use C(query) for listing an object or multiple objects. 103 type: str 104 choices: [ absent, present, query ] 105 default: present 106extends_documentation_fragment: cisco.mso.modules 107''' 108 109EXAMPLES = r''' 110- name: Associate a non-cloud site with a tenant 111 cisco.mso.mso_tenant_site: 112 host: mso_host 113 username: admin 114 password: SomeSecretPassword 115 tenant: tenant_name 116 site: site_name 117 state: present 118 delegate_to: localhost 119 120- name: Associate AWS site with a tenant, with aws_trusted set to true 121 cisco.mso.mso_tenant_site: 122 host: mso_host 123 username: admin 124 password: SomeSecretPassword 125 tenant: tenant_name 126 site: site_name 127 cloud_account: '000000000000' 128 aws_trusted: true 129 state: present 130 delegate_to: localhost 131 132- name: Associate AWS site with a tenant, with aws_trusted set to false 133 cisco.mso.mso_tenant_site: 134 host: mso_host 135 username: admin 136 password: SomeSecretPassword 137 tenant: tenant_name 138 site: AWS 139 cloud_account: '000000000000' 140 aws_trusted: false 141 aws_access_key: '1' 142 secret_key: '0' 143 aws_account_org: false 144 state: present 145 delegate_to: localhost 146 147- name: Associate Azure site in managed mode 148 mso.cisco.mso_tenant_site: 149 host: mso_host 150 username: admin 151 password: SomeSecretPassword 152 tenant: tenant_name 153 site: site_name 154 cloud_account: uni/tn-ansible_test/act-[9]-azure_vendor-azure 155 azure_access_type: managed 156 azure_subscription_id: '9' 157 azure_application_id: '100' 158 state: present 159 delegate_to: localhost 160 161- name: Associate Azure site in unmanaged mode 162 mso.cisco.mso_tenant_site: 163 host: mso_host 164 username: admin 165 password: SomeSecretPassword 166 tenant: tenant_name 167 site: site_name 168 cloud_account: uni/tn-ansible_test/act-[9]-azure_vendor-azure 169 azure_access_type: unmanaged 170 azure_subscription_id: '9' 171 azure_application_id: '100' 172 azure_credential_name: cApicApp 173 secret_key: iins 174 azure_active_directory_id: '32' 175 azure_active_directory_name: CiscoINSBUAd 176 state: present 177 delegate_to: localhost 178 179- name: Dissociate a site 180 cisco.mso.mso_tenant_site: 181 host: mso_host 182 username: admin 183 password: SomeSecretPassword 184 tenant: tenant_name 185 site: site_name 186 state: absent 187 delegate_to: localhost 188 189- name: Query a site 190 cisco.mso.mso_tenant_site: 191 host: mso_host 192 username: admin 193 password: SomeSecretPassword 194 tenant: tenant_name 195 site: site_name 196 state: query 197 delegate_to: localhost 198 199- name: Query all sites of a tenant 200 cisco.mso.mso_tenant_site: 201 host: mso_host 202 username: admin 203 password: SomeSecretPassword 204 tenant: tenant_name 205 state: query 206 delegate_to: localhost 207 register: query_result 208''' 209 210RETURN = r''' 211''' 212 213from ansible.module_utils.basic import AnsibleModule 214from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec 215 216 217def main(): 218 argument_spec = mso_argument_spec() 219 argument_spec.update( 220 tenant=dict(type='str', aliases=['name'], required=True), 221 site=dict(type='str', aliases=['name']), 222 cloud_account=dict(type='str'), 223 security_domains=dict(type='list', elements='str', default=[]), 224 aws_trusted=dict(type='bool'), 225 azure_access_type=dict(type='str', default='shared', choices=['managed', 'unmanaged', 'shared']), 226 azure_active_directory_id=dict(type='str'), 227 aws_access_key=dict(type='str'), 228 aws_account_org=dict(type='bool', default='false'), 229 azure_active_directory_name=dict(type='str'), 230 azure_subscription_id=dict(type='str'), 231 azure_application_id=dict(type='str'), 232 azure_credential_name=dict(type='str'), 233 secret_key=dict(type='str'), 234 state=dict(type='str', default='present', choices=['absent', 'present', 'query']), 235 ) 236 237 module = AnsibleModule( 238 argument_spec=argument_spec, 239 supports_check_mode=True, 240 required_if=[ 241 ['state', 'absent', ['tenant', 'site']], 242 ['state', 'present', ['tenant', 'site']], 243 ], 244 ) 245 246 state = module.params.get('state') 247 security_domains = module.params.get('security_domains') 248 cloud_account = module.params.get('cloud_account') 249 azure_access_type = module.params.get('azure_access_type') 250 azure_credential_name = module.params.get('azure_credential_name') 251 azure_application_id = module.params.get('azure_application_id') 252 azure_active_directory_id = module.params.get('azure_active_directory_id') 253 azure_active_directory_name = module.params.get('azure_active_directory_name') 254 azure_subscription_id = module.params.get('azure_subscription_id') 255 secret_key = module.params.get('secret_key') 256 aws_account_org = module.params.get('aws_account_org') 257 aws_access_key = module.params.get('aws_access_key') 258 aws_trusted = module.params.get('aws_trusted') 259 260 mso = MSOModule(module) 261 262 # Get tenant_id and site_id 263 tenant_id = mso.lookup_tenant(module.params.get('tenant')) 264 site_id = mso.lookup_site(module.params.get('site')) 265 tenants = [(t.get('id')) for t in mso.query_objs('tenants')] 266 tenant_idx = tenants.index((tenant_id)) 267 268 # set tenent and port paths 269 tenant_path = 'tenants/{0}'.format(tenant_id) 270 ops = [] 271 ports_path = '/siteAssociations/-' 272 port_path = '/siteAssociations/{0}'.format(site_id) 273 274 payload = dict( 275 siteId=site_id, 276 securityDomains=security_domains, 277 cloudAccount=cloud_account, 278 ) 279 280 if cloud_account: 281 if 'azure' in cloud_account: 282 azure_account = dict( 283 accessType=azure_access_type, 284 securityDomains=security_domains, 285 vendor='azure', 286 ) 287 288 payload['azureAccount'] = [azure_account] 289 290 cloudSubscription = dict( 291 cloudSubscriptionId=azure_subscription_id, 292 cloudApplicationId=azure_application_id, 293 ) 294 295 payload['azureAccount'][0]['cloudSubscription'] = cloudSubscription 296 297 if azure_access_type == 'shared': 298 payload['azureAccount'] = [] 299 300 if azure_access_type == 'managed': 301 if not azure_subscription_id: 302 mso.fail_json(msg="azure_susbscription_id is required when in managed mode.") 303 if not azure_application_id: 304 mso.fail_json(msg="azure_application_id is required when in managed mode.") 305 payload['azureAccount'][0]['cloudApplication'] = [] 306 payload['azureAccount'][0]['cloudActiveDirectory'] = [] 307 308 if azure_access_type == 'unmanaged': 309 if not azure_subscription_id: 310 mso.fail_json(msg="azure_subscription_id is required when in unmanaged mode.") 311 if not azure_application_id: 312 mso.fail_json(msg="azure_application_id is required when in unmanaged mode.") 313 if not secret_key: 314 mso.fail_json(msg="secret_key is required when in unmanaged mode.") 315 if not azure_active_directory_id: 316 mso.fail_json(msg="azure_active_directory_id is required when in unmanaged mode.") 317 if not azure_active_directory_name: 318 mso.fail_json(msg="azure_active_directory_name is required when in unmanaged mode.") 319 if not azure_credential_name: 320 mso.fail_json(msg="azure_credential_name is required when in unmanaged mode.") 321 azure_account.update( 322 accessType='credentials', 323 ) 324 cloudApplication = dict( 325 cloudApplicationId=azure_application_id, 326 cloudCredentialName=azure_credential_name, 327 secretKey=secret_key, 328 cloudActiveDirectoryId=azure_active_directory_id 329 ) 330 cloudActiveDirectory = dict( 331 cloudActiveDirectoryId=azure_active_directory_id, 332 cloudActiveDirectoryName=azure_active_directory_name 333 ) 334 payload['azureAccount'][0]['cloudApplication'] = [cloudApplication] 335 payload['azureAccount'][0]['cloudActiveDirectory'] = [cloudActiveDirectory] 336 337 else: 338 aws_account = dict( 339 accountId=cloud_account, 340 isTrusted=aws_trusted, 341 accessKeyId=aws_access_key, 342 secretKey=secret_key, 343 isAccountInOrg=aws_account_org, 344 ) 345 346 if not aws_trusted: 347 if not aws_access_key: 348 mso.fail_json(msg="aws_access_key is a required field in untrusted mode.") 349 if not secret_key: 350 mso.fail_json(msg="secret_key is a required field in untrusted mode.") 351 payload['awsAccount'] = [aws_account] 352 353 sites = [(s.get('siteId')) for s in mso.query_objs('tenants')[tenant_idx]['siteAssociations']] 354 355 if site_id in sites: 356 site_idx = sites.index((site_id)) 357 mso.existing = mso.query_objs('tenants')[tenant_idx]['siteAssociations'][site_idx] 358 359 if state == 'query': 360 if len(sites) == 0: 361 mso.fail_json(msg="No site associated with tenant Id {0}".format(tenant_id)) 362 elif site_id not in sites and site_id is not None: 363 mso.fail_json(msg="Site Id {0} not associated with tenant Id {1}".format(site_id, tenant_id)) 364 elif site_id is None: 365 mso.existing = mso.query_objs('tenants')[tenant_idx]['siteAssociations'] 366 mso.exit_json() 367 368 mso.previous = mso.existing 369 370 if state == 'absent': 371 if mso.existing: 372 mso.sent = mso.existing = {} 373 ops.append(dict(op='remove', path=port_path)) 374 if state == 'present': 375 mso.sanitize(payload, collate=True) 376 377 if mso.existing: 378 ops.append(dict(op='replace', path=port_path, value=mso.sent)) 379 else: 380 ops.append(dict(op='add', path=ports_path, value=mso.sent)) 381 382 mso.existing = mso.proposed 383 384 if not module.check_mode and mso.proposed != mso.previous: 385 mso.request(tenant_path, method='PATCH', data=ops) 386 387 mso.exit_json() 388 389 390if __name__ == "__main__": 391 main() 392