diff --git a/lib/ansible/module_utils/network/aci/mso.py b/lib/ansible/module_utils/network/aci/mso.py deleted file mode 100644 index a6036d2bb8e..00000000000 --- a/lib/ansible/module_utils/network/aci/mso.py +++ /dev/null @@ -1,569 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule, json -from ansible.module_utils.six import PY3 -from ansible.module_utils.six.moves.urllib.parse import urlencode, urljoin -from ansible.module_utils.urls import fetch_url -from ansible.module_utils._text import to_bytes, to_native - - -if PY3: - def cmp(a, b): - return (a > b) - (a < b) - - -def issubset(subset, superset): - ''' Recurse through nested dictionary and compare entries ''' - - # Both objects are the same object - if subset is superset: - return True - - # Both objects are identical - if subset == superset: - return True - - # Both objects have a different type - if type(subset) != type(superset): - return False - - for key, value in subset.items(): - # Ignore empty values - if value is None: - return True - - # Item from subset is missing from superset - if key not in superset: - return False - - # Item has different types in subset and superset - if type(superset[key]) != type(value): - return False - - # Compare if item values are subset - if isinstance(value, dict): - if not issubset(superset[key], value): - return False - elif isinstance(value, list): - try: - # NOTE: Fails for lists of dicts - if not set(value) <= set(superset[key]): - return False - except TypeError: - # Fall back to exact comparison for lists of dicts - if not cmp(value, superset[key]): - return False - elif isinstance(value, set): - if not value <= superset[key]: - return False - else: - if not value == superset[key]: - return False - - return True - - -def update_qs(params): - ''' Append key-value pairs to self.filter_string ''' - accepted_params = dict((k, v) for (k, v) in params.items() if v is not None) - return '?' + urlencode(accepted_params) - - -def mso_argument_spec(): - return dict( - host=dict(type='str', required=True, aliases=['hostname']), - port=dict(type='int', required=False), - username=dict(type='str', default='admin'), - password=dict(type='str', required=True, no_log=True), - output_level=dict(type='str', default='normal', choices=['debug', 'info', 'normal']), - timeout=dict(type='int', default=30), - use_proxy=dict(type='bool', default=True), - use_ssl=dict(type='bool', default=True), - validate_certs=dict(type='bool', default=True), - ) - - -def mso_reference_spec(): - return dict( - name=dict(type='str', required=True), - schema=dict(type='str'), - template=dict(type='str'), - ) - - -def mso_subnet_spec(): - return dict( - subnet=dict(type='str', required=True, aliases=['ip']), - description=dict(type='str'), - scope=dict(type='str', choices=['private', 'public']), - shared=dict(type='bool'), - no_default_gateway=dict(type='bool'), - ) - - -def mso_contractref_spec(): - return dict( - name=dict(type='str', required=True), - schema=dict(type='str'), - template=dict(type='str'), - type=dict(type='str', required=True, choices=['consumer', 'provider']), - ) - - -class MSOModule(object): - - def __init__(self, module): - self.module = module - self.params = module.params - self.result = dict(changed=False) - self.headers = {'Content-Type': 'text/json'} - - # normal output - self.existing = dict() - - # info output - self.previous = dict() - self.proposed = dict() - self.sent = dict() - - # debug output - self.has_modified = False - self.filter_string = '' - self.method = None - self.path = None - self.response = None - self.status = None - self.url = None - - # Ensure protocol is set - self.params['protocol'] = 'https' if self.params.get('use_ssl', True) else 'http' - - # Set base_uri - if self.params.get('port') is not None: - self.baseuri = '{protocol}://{host}:{port}/api/v1/'.format(**self.params) - else: - self.baseuri = '{protocol}://{host}/api/v1/'.format(**self.params) - - if self.module._debug: - self.module.warn('Enable debug output because ANSIBLE_DEBUG was set.') - self.params['output_level'] = 'debug' - - if self.params.get('password'): - # Perform password-based authentication, log on using password - self.login() - else: - self.module.fail_json(msg="Parameter 'password' is required for authentication") - - def login(self): - ''' Log in to MSO ''' - - # Perform login request - self.url = urljoin(self.baseuri, 'auth/login') - payload = {'username': self.params.get('username'), 'password': self.params.get('password')} - resp, auth = fetch_url(self.module, - self.url, - data=json.dumps(payload), - method='POST', - headers=self.headers, - timeout=self.params.get('timeout'), - use_proxy=self.params.get('use_proxy')) - - # Handle MSO response - if auth.get('status') != 201: - self.response = auth.get('msg') - self.status = auth.get('status') - self.fail_json(msg='Authentication failed: {msg}'.format(**auth)) - - payload = json.loads(resp.read()) - - self.headers['Authorization'] = 'Bearer {token}'.format(**payload) - - def request(self, path, method=None, data=None, qs=None): - ''' Generic HTTP method for MSO requests. ''' - self.path = path - - if method is not None: - self.method = method - - # If we PATCH with empty operations, return - if method == 'PATCH' and not data: - return {} - - self.url = urljoin(self.baseuri, path) - - if qs is not None: - self.url = self.url + update_qs(qs) - - resp, info = fetch_url(self.module, - self.url, - headers=self.headers, - data=json.dumps(data), - method=self.method, - timeout=self.params.get('timeout'), - use_proxy=self.params.get('use_proxy'), - ) - self.response = info.get('msg') - self.status = info.get('status') - - # self.result['info'] = info - - # Get change status from HTTP headers - if 'modified' in info: - self.has_modified = True - if info.get('modified') == 'false': - self.result['changed'] = False - elif info.get('modified') == 'true': - self.result['changed'] = True - - # 200: OK, 201: Created, 202: Accepted, 204: No Content - if self.status in (200, 201, 202, 204): - output = resp.read() - if output: - return json.loads(output) - - # 404: Not Found - elif self.method == 'DELETE' and self.status == 404: - return {} - - # 400: Bad Request, 401: Unauthorized, 403: Forbidden, - # 405: Method Not Allowed, 406: Not Acceptable - # 500: Internal Server Error, 501: Not Implemented - elif self.status >= 400: - try: - output = resp.read() - payload = json.loads(output) - except (ValueError, AttributeError): - try: - payload = json.loads(info['body']) - except Exception: - self.fail_json(msg='MSO Error:', data=data, info=info) - if 'code' in payload: - self.fail_json(msg='MSO Error {code}: {message}'.format(**payload), data=data, info=info, payload=payload) - else: - self.fail_json(msg='MSO Error:'.format(**payload), data=data, info=info, payload=payload) - - return {} - - def query_objs(self, path, key=None, **kwargs): - ''' Query the MSO REST API for objects in a path ''' - found = [] - objs = self.request(path, method='GET') - - if objs == {}: - return found - - if key is None: - key = path - - if key not in objs: - self.fail_json(msg="Key '%s' missing from data", data=objs) - - for obj in objs[key]: - for kw_key, kw_value in kwargs.items(): - if kw_value is None: - continue - if obj[kw_key] != kw_value: - break - else: - found.append(obj) - return found - - def get_obj(self, path, **kwargs): - ''' Get a specific object from a set of MSO REST objects ''' - objs = self.query_objs(path, **kwargs) - if len(objs) == 0: - return {} - if len(objs) > 1: - self.fail_json(msg='More than one object matches unique filter: {0}'.format(kwargs)) - return objs[0] - - def lookup_schema(self, schema): - ''' Look up schema and return its id ''' - if schema is None: - return schema - - s = self.get_obj('schemas', displayName=schema) - if not s: - self.module.fail_json(msg="Schema '%s' is not a valid schema name." % schema) - if 'id' not in s: - self.module.fail_json(msg="Schema lookup failed for schema '%s': %s" % (schema, s)) - return s.get('id') - - def lookup_domain(self, domain): - ''' Look up a domain and return its id ''' - if domain is None: - return domain - - d = self.get_obj('auth/domains', key='domains', name=domain) - if not d: - self.module.fail_json(msg="Domain '%s' is not a valid domain name." % domain) - if 'id' not in d: - self.module.fail_json(msg="Domain lookup failed for domain '%s': %s" % (domain, d)) - return d.get('id') - - def lookup_roles(self, roles): - ''' Look up roles and return their ids ''' - if roles is None: - return roles - - ids = [] - for role in roles: - r = self.get_obj('roles', name=role) - if not r: - self.module.fail_json(msg="Role '%s' is not a valid role name." % role) - if 'id' not in r: - self.module.fail_json(msg="Role lookup failed for role '%s': %s" % (role, r)) - ids.append(dict(roleId=r.get('id'))) - return ids - - def lookup_site(self, site): - ''' Look up a site and return its id ''' - if site is None: - return site - - s = self.get_obj('sites', name=site) - if not s: - self.module.fail_json(msg="Site '%s' is not a valid site name." % site) - if 'id' not in s: - self.module.fail_json(msg="Site lookup failed for site '%s': %s" % (site, s)) - return s.get('id') - - def lookup_sites(self, sites): - ''' Look up sites and return their ids ''' - if sites is None: - return sites - - ids = [] - for site in sites: - s = self.get_obj('sites', name=site) - if not s: - self.module.fail_json(msg="Site '%s' is not a valid site name." % site) - if 'id' not in s: - self.module.fail_json(msg="Site lookup failed for site '%s': %s" % (site, s)) - ids.append(dict(siteId=s.get('id'), securityDomains=[])) - return ids - - def lookup_tenant(self, tenant): - ''' Look up a tenant and return its id ''' - if tenant is None: - return tenant - - t = self.get_obj('tenants', key='tenants', name=tenant) - if not t: - self.module.fail_json(msg="Tenant '%s' is not valid tenant name." % tenant) - if 'id' not in t: - self.module.fail_json(msg="Tenant lookup failed for tenant '%s': %s" % (tenant, t)) - return t.get('id') - - def lookup_users(self, users): - ''' Look up users and return their ids ''' - if users is None: - return users - - ids = [] - for user in users: - u = self.get_obj('users', username=user) - if not u: - self.module.fail_json(msg="User '%s' is not a valid user name." % user) - if 'id' not in u: - self.module.fail_json(msg="User lookup failed for user '%s': %s" % (user, u)) - ids.append(dict(userId=u.get('id'))) - return ids - - def create_label(self, label, label_type): - ''' Create a new label ''' - return self.request('labels', method='POST', data=dict(displayName=label, type=label_type)) - - def lookup_labels(self, labels, label_type): - ''' Look up labels and return their ids (create if necessary) ''' - if labels is None: - return None - - ids = [] - for label in labels: - l = self.get_obj('labels', displayName=label) - if not l: - l = self.create_label(label, label_type) - if 'id' not in l: - self.module.fail_json(msg="Label lookup failed for label '%s': %s" % (label, l)) - ids.append(l.get('id')) - return ids - - def anp_ref(self, **data): - ''' Create anpRef string ''' - return '/schemas/{schema_id}/templates/{template}/anps/{anp}'.format(**data) - - def epg_ref(self, **data): - ''' Create epgRef string ''' - return '/schemas/{schema_id}/templates/{template}/anps/{anp}/epgs/{epg}'.format(**data) - - def bd_ref(self, **data): - ''' Create bdRef string ''' - return '/schemas/{schema_id}/templates/{template}/bds/{bd}'.format(**data) - - def contract_ref(self, **data): - ''' Create contractRef string ''' - # Support the contract argspec - if 'name' in data: - data['contract'] = data.get('name') - return '/schemas/{schema_id}/templates/{template}/contracts/{contract}'.format(**data) - - def filter_ref(self, **data): - ''' Create a filterRef string ''' - return '/schemas/{schema_id}/templates/{template}/filters/{filter}'.format(**data) - - def vrf_ref(self, **data): - ''' Create vrfRef string ''' - return '/schemas/{schema_id}/templates/{template}/vrfs/{vrf}'.format(**data) - - def make_reference(self, data, reftype, schema_id, template): - ''' Create a reference from a dictionary ''' - # Removes entry from payload - if data is None: - return None - - if data.get('schema') is not None: - schema_obj = self.get_obj('schemas', displayName=data.get('schema')) - if not schema_obj: - self.fail_json(msg="Referenced schema '{schema}' in {reftype}ref does not exist".format(reftype=reftype, **data)) - schema_id = schema_obj.get('id') - - if data.get('template') is not None: - template = data.get('template') - - refname = '%sName' % reftype - - return { - refname: data.get('name'), - 'schemaId': schema_id, - 'templateName': template, - } - - def make_subnets(self, data): - ''' Create a subnets list from input ''' - if data is None: - return None - - subnets = [] - for subnet in data: - subnets.append(dict( - ip=subnet.get('ip'), - description=subnet.get('description', subnet.get('ip')), - scope=subnet.get('scope', 'private'), - shared=subnet.get('shared', False), - noDefaultGateway=subnet.get('no_default_gateway', False), - )) - - return subnets - - def sanitize(self, updates, collate=False, required=None, unwanted=None): - ''' Clean up unset keys from a request payload ''' - if required is None: - required = [] - if unwanted is None: - unwanted = [] - self.proposed = deepcopy(self.existing) - self.sent = deepcopy(self.existing) - - for key in self.existing: - # Remove References - if key.endswith('Ref'): - del(self.proposed[key]) - del(self.sent[key]) - continue - - # Removed unwanted keys - elif key in unwanted: - del(self.proposed[key]) - del(self.sent[key]) - continue - - # Clean up self.sent - for key in updates: - # Always retain 'id' - if key in required: - pass - - # Remove unspecified values - elif not collate and updates[key] is None: - if key in self.existing: - del(self.sent[key]) - continue - - # Remove identical values - elif not collate and key in self.existing and updates[key] == self.existing[key]: - del(self.sent[key]) - continue - - # Add everything else - if updates[key] is not None: - self.sent[key] = updates[key] - - # Update self.proposed - self.proposed.update(self.sent) - - def exit_json(self, **kwargs): - ''' Custom written method to exit from module. ''' - - if self.params.get('state') in ('absent', 'present'): - if self.params.get('output_level') in ('debug', 'info'): - self.result['previous'] = self.previous - # FIXME: Modified header only works for PATCH - if not self.has_modified and self.previous != self.existing: - self.result['changed'] = True - - # Return the gory details when we need it - if self.params.get('output_level') == 'debug': - self.result['method'] = self.method - self.result['response'] = self.response - self.result['status'] = self.status - self.result['url'] = self.url - - if self.params.get('state') in ('absent', 'present'): - self.result['sent'] = self.sent - self.result['proposed'] = self.proposed - - self.result['current'] = self.existing - - if self.module._diff and self.result.get('changed') is True: - self.result['diff'] = dict( - before=self.previous, - after=self.existing, - ) - - self.result.update(**kwargs) - self.module.exit_json(**self.result) - - def fail_json(self, msg, **kwargs): - ''' Custom written method to return info on failure. ''' - - if self.params.get('state') in ('absent', 'present'): - if self.params.get('output_level') in ('debug', 'info'): - self.result['previous'] = self.previous - # FIXME: Modified header only works for PATCH - if not self.has_modified and self.previous != self.existing: - self.result['changed'] = True - - # Return the gory details when we need it - if self.params.get('output_level') == 'debug': - if self.url is not None: - self.result['method'] = self.method - self.result['response'] = self.response - self.result['status'] = self.status - self.result['url'] = self.url - - if self.params.get('state') in ('absent', 'present'): - self.result['sent'] = self.sent - self.result['proposed'] = self.proposed - - self.result['current'] = self.existing - - self.result.update(**kwargs) - self.module.fail_json(msg=msg, **self.result) diff --git a/lib/ansible/modules/network/aci/mso_label.py b/lib/ansible/modules/network/aci/mso_label.py deleted file mode 100644 index 590a5ae1c01..00000000000 --- a/lib/ansible/modules/network/aci/mso_label.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_label -short_description: Manage labels -description: -- Manage labels on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - label: - description: - - The name of the label. - type: str - required: yes - aliases: [ name ] - type: - description: - - The type of the label. - type: str - 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. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new label - mso_label: - host: mso_host - username: admin - password: SomeSecretPassword - label: Belgium - type: site - state: present - delegate_to: localhost - -- name: Remove a label - mso_label: - host: mso_host - username: admin - password: SomeSecretPassword - label: Belgium - state: absent - delegate_to: localhost - -- name: Query a label - mso_label: - host: mso_host - username: admin - password: SomeSecretPassword - label: Belgium - state: query - delegate_to: localhost - register: query_result - -- name: Query all labels - mso_label: - host: mso_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.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - label=dict(type='str', aliases=['name']), - 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.get('label') - label_type = module.params.get('type') - state = module.params.get('state') - - mso = MSOModule(module) - - label_id = None - path = 'labels' - - # Query for existing object(s) - if label: - mso.existing = mso.get_obj(path, displayName=label) - if mso.existing: - label_id = mso.existing.get('id') - # If we found an existing object, continue with it - path = 'labels/{id}'.format(id=label_id) - else: - mso.existing = mso.query_objs(path) - - if state == 'query': - pass - - elif state == 'absent': - mso.previous = mso.existing - if mso.existing: - if module.check_mode: - mso.existing = {} - else: - mso.existing = mso.request(path, method='DELETE') - - elif state == 'present': - mso.previous = mso.existing - - payload = dict( - id=label_id, - displayName=label, - type=label_type, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - if not issubset(mso.sent, mso.existing): - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='PUT', data=mso.sent) - else: - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='POST', data=mso.sent) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_role.py b/lib/ansible/modules/network/aci/mso_role.py deleted file mode 100644 index 215d5b54f5a..00000000000 --- a/lib/ansible/modules/network/aci/mso_role.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_role -short_description: Manage roles -description: -- Manage roles on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - role: - description: - - The name of the role. - type: str - required: yes - aliases: [ name ] - display_name: - description: - - The name of the role to be displayed in the web UI. - type: str - description: - description: - - The description of the role. - type: str - permissions: - description: - - A list of permissions tied to this role. - type: list - choices: - - backup-db - - manage-audit-records - - manage-labels - - manage-roles - - manage-schemas - - manage-sites - - manage-tenants - - manage-tenant-schemas - - manage-users - - platform-logs - - view-all-audit-records - - view-labels - - view-roles - - view-schemas - - view-sites - - view-tenants - - view-tenant-schemas - - view-users - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new role - mso_role: - host: mso_host - username: admin - password: SomeSecretPassword - role: readOnly - display_name: Read Only - description: Read-only access for troubleshooting - permissions: - - view-roles - - view-schemas - - view-sites - - view-tenants - - view-tenant-schemas - - view-users - state: present - delegate_to: localhost - -- name: Remove a role - mso_role: - host: mso_host - username: admin - password: SomeSecretPassword - role: readOnly - state: absent - delegate_to: localhost - -- name: Query a role - mso_role: - host: mso_host - username: admin - password: SomeSecretPassword - role: readOnly - state: query - delegate_to: localhost - register: query_result - -- name: Query all roles - mso_role: - host: mso_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.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - role=dict(type='str', aliases=['name']), - display_name=dict(type='str'), - description=dict(type='str'), - permissions=dict(type='list', choices=[ - 'backup-db', - 'manage-audit-records', - 'manage-labels', - 'manage-roles', - 'manage-schemas', - 'manage-sites', - 'manage-tenants', - 'manage-tenant-schemas', - 'manage-users', - 'platform-logs', - 'view-all-audit-records', - 'view-labels', - 'view-roles', - 'view-schemas', - 'view-sites', - 'view-tenants', - 'view-tenant-schemas', - 'view-users', - ]), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['role']], - ['state', 'present', ['role']], - ], - ) - - role = module.params.get('role') - description = module.params.get('description') - permissions = module.params.get('permissions') - state = module.params.get('state') - - mso = MSOModule(module) - - role_id = None - path = 'roles' - - # Query for existing object(s) - if role: - mso.existing = mso.get_obj(path, name=role) - if mso.existing: - role_id = mso.existing.get('id') - # If we found an existing object, continue with it - path = 'roles/{id}'.format(id=role_id) - else: - mso.existing = mso.query_objs(path) - - if state == 'query': - pass - - elif state == 'absent': - mso.previous = mso.existing - if mso.existing: - if module.check_mode: - mso.existing = {} - else: - mso.existing = mso.request(path, method='DELETE') - - elif state == 'present': - mso.previous = mso.existing - - payload = dict( - id=role_id, - name=role, - displayName=role, - description=description, - permissions=permissions, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - if not issubset(mso.sent, mso.existing): - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='PUT', data=mso.sent) - else: - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='POST', data=mso.sent) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema.py b/lib/ansible/modules/network/aci/mso_schema.py deleted file mode 100644 index 263e20ec167..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema -short_description: Manage schemas -description: -- Manage schemas on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - aliases: [ name ] - templates: - description: - - A list of templates for this schema. - type: list - sites: - description: - - A list of sites mapped to templates in this schema. - type: list - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- Due to restrictions of the MSO REST API this module cannot create empty schemas (i.e. schemas without templates). - Use the M(mso_schema_template) to automatically create schemas with templates. -seealso: -- module: mso_schema_site -- module: mso_schema_template -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new schema - mso_schema: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - state: present - templates: - - name: Template1 - displayName: Template 1 - tenantId: north_europe - anps: - <...> - - name: Template2 - displayName: Template 2 - tenantId: nort_europe - anps: - <...> - delegate_to: localhost - -- name: Remove schemas - mso_schema: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - state: absent - delegate_to: localhost - -- name: Query a schema - mso_schema: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all schemas - mso_schema: - host: mso_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.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', aliases=['name']), - templates=dict(type='list'), - sites=dict(type='list'), - # messages=dict(type='dict'), - # associations=dict(type='list'), - # health_faults=dict(type='list'), - # references=dict(type='dict'), - # policy_states=dict(type='list'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['schema']], - ['state', 'present', ['schema', 'templates']], - ], - ) - - schema = module.params.get('schema') - templates = module.params.get('templates') - sites = module.params.get('sites') - state = module.params.get('state') - - mso = MSOModule(module) - - schema_id = None - path = 'schemas' - - # Query for existing object(s) - if schema: - mso.existing = mso.get_obj(path, displayName=schema) - if mso.existing: - schema_id = mso.existing.get('id') - path = 'schemas/{id}'.format(id=schema_id) - else: - mso.existing = mso.query_objs(path) - - if state == 'query': - pass - - elif state == 'absent': - mso.previous = mso.existing - if mso.existing: - if module.check_mode: - mso.existing = {} - else: - mso.existing = mso.request(path, method='DELETE') - - elif state == 'present': - mso.previous = mso.existing - - payload = dict( - id=schema_id, - displayName=schema, - templates=templates, - sites=sites, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - if not issubset(mso.sent, mso.existing): - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='PUT', data=mso.sent) - else: - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='POST', data=mso.sent) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site.py b/lib/ansible/modules/network/aci/mso_schema_site.py deleted file mode 100644 index 4371b43f121..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_site -short_description: Manage sites in schemas -description: -- Manage sites on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site to manage. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - aliases: [ name ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_template -- module: mso_site -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site to a schema - mso_schema_site: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - site: bdsol-pod51 - template: Template 1 - state: present - delegate_to: localhost - -- name: Remove a site from a schema - mso_schema_site: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - site: bdsol-pod51 - template: Template 1 - state: absent - delegate_to: localhost - -- name: Query a schema site - mso_schema_site: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - site: bdsol-pod51 - template: Template 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all schema sites - mso_schema_site: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - site: bdsol-pod51 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', aliases=['name']), - template=dict(type='str'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['site', 'template']], - ['state', 'present', ['site', 'template']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - # Schema exists - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get site - site_id = mso.lookup_site(site) - - mso.existing = {} - if 'sites' in schema_obj: - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if template: - if (site_id, template) in sites: - site_idx = sites.index((site_id, template)) - mso.existing = schema_obj.get('sites')[site_idx] - else: - mso.existing = schema_obj.get('sites') - - if state == 'query': - if not mso.existing: - if template: - mso.fail_json(msg="Template '{0}' not found".format(template)) - else: - mso.existing = [] - mso.exit_json() - - sites_path = '/sites' - site_path = '/sites/{0}'.format(site) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - # Remove existing site - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=site_path)) - - elif state == 'present': - if not mso.existing: - # Add new site - payload = dict( - siteId=site_id, - templateName=template, - anps=[], - bds=[], - contracts=[], - externalEpgs=[], - intersiteL3outs=[], - serviceGraphs=[], - vrfs=[], - ) - - mso.sanitize(payload, collate=True) - - ops.append(dict(op='add', path=sites_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_anp.py b/lib/ansible/modules/network/aci/mso_schema_site_anp.py deleted file mode 100644 index 75d54c615d3..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_anp.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_anp -short_description: Manage site-local Application Network Profiles (ANPs) in schema template -description: -- Manage site-local ANPs in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP to manage. - type: str - aliases: [ name ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_site -- module: mso_schema_site_anp_epg -- module: mso_schema_template_anp -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site ANP - mso_schema_site_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: present - delegate_to: localhost - -- name: Remove a site ANP - mso_schema_site_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: absent - delegate_to: localhost - -- name: Query a specific site ANPs - mso_schema_site_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site ANPs - mso_schema_site_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['anp']], - ['state', 'present', ['anp']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - anp = module.params.get('anp') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get ANP - anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) - anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] - - if anp is not None and anp_ref in anps: - anp_idx = anps.index(anp_ref) - anp_path = '/sites/{0}/anps/{1}'.format(site_template, anp) - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx] - - if state == 'query': - if anp is None: - mso.existing = schema_obj.get('sites')[site_idx]['anps'] - elif not mso.existing: - mso.fail_json(msg="ANP '{anp}' not found".format(anp=anp)) - mso.exit_json() - - anps_path = '/sites/{0}/anps'.format(site_template) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=anp_path)) - - elif state == 'present': - - payload = dict( - anpRef=dict( - schemaId=schema_id, - templateName=template, - anpName=anp, - ), - ) - - mso.sanitize(payload, collate=True) - - if not mso.existing: - ops.append(dict(op='add', path=anps_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg.py b/lib/ansible/modules/network/aci/mso_schema_site_anp_epg.py deleted file mode 100644 index 5128f833df2..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_anp_epg -short_description: Manage site-local Endpoint Groups (EPGs) in schema template -description: -- Manage site-local EPGs in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - epg: - description: - - The name of the EPG to manage. - type: str - aliases: [ name ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_site_anp -- module: mso_schema_site_anp_epg_subnet -- module: mso_schema_template_anp_epg -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site EPG - mso_schema_site_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - state: present - delegate_to: localhost - -- name: Remove a site EPG - mso_schema_site_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - state: absent - delegate_to: localhost - -- name: Query a specific site EPGs - mso_schema_site_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site EPGs - mso_schema_site_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['epg']], - ['state', 'present', ['epg']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get ANP - anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) - anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] - if anp_ref not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp_ref) - - # Get EPG - epg_ref = mso.epg_ref(schema_id=schema_id, template=template, anp=anp, epg=epg) - epgs = [e.get('epgRef') for e in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs']] - if epg is not None and epg_ref in epgs: - epg_idx = epgs.index(epg_ref) - epg_path = '/sites/{0}/anps/{1}/epgs/{2}'.format(site_template, anp, epg) - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx] - - if state == 'query': - if epg is None: - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'] - elif not mso.existing: - mso.fail_json(msg="EPG '{epg}' not found".format(epg=epg)) - mso.exit_json() - - epgs_path = '/sites/{0}/anps/{1}/epgs'.format(site_template, anp) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=epg_path)) - - elif state == 'present': - - payload = dict( - epgRef=dict( - schemaId=schema_id, - templateName=template, - anpName=anp, - epgName=epg, - ), - ) - - mso.sanitize(payload, collate=True) - - if not mso.existing: - ops.append(dict(op='add', path=epgs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_domain.py b/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_domain.py deleted file mode 100644 index fe6734fdaf2..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_domain.py +++ /dev/null @@ -1,401 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Nirav Katarmal (@nkatarmal-crest) -# 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: mso_schema_site_anp_epg_domain -short_description: Manage site-local EPG domains in schema template -description: -- Manage site-local EPG domains in schema template on Cisco ACI Multi-Site. -author: -- Nirav Katarmal (@nkatarmal-crest) -version_added: '2.9' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - epg: - description: - - The name of the EPG. - type: str - domain_association_type: - description: - - The type of domain to associate. - type: str - choices: [ vmmDomain, l3ExtDomain, l2ExtDomain, physicalDomain, fibreChannel ] - domain_profile: - description: - - The domain profile name. - type: str - deployment_immediacy: - description: - - The deployment immediacy of the domain. - - C(immediate) means B(Deploy immediate). - - C(lazy) means B(deploy on demand). - type: str - choices: [ immediate, lazy ] - resolution_immediacy: - description: - - Determines when the policies should be resolved and available. - - Defaults to C(lazy) when unset during creation. - type: str - choices: [ immediate, lazy, pre-provision ] - micro_seg_vlan_type: - description: - - Virtual LAN type for microsegmentation. This attribute can only be used with vmmDomain domain association. - type: str - micro_seg_vlan: - description: - - Virtual LAN for microsegmentation. This attribute can only be used with vmmDomain domain association. - type: int - port_encap_vlan_type: - description: - - Virtual LAN type for port encap. This attribute can only be used with vmmDomain domain association. - type: str - port_encap_vlan: - description: - - Virtual LAN type for port encap. This attribute can only be used with vmmDomain domain association. - type: int - vlan_encap_mode: - description: - - Which VLAN enacap mode to use. This attribute can only be used with vmmDomain domain association. - type: str - choices: [ static, dynamic ] - allow_micro_segmentation: - description: - - Specifies microsegmentation is enabled or not. This attribute can only be used with vmmDomain domain association. - type: bool - switch_type: - description: - - Which switch type to use with this domain association. This attribute can only be used with vmmDomain domain association. - type: str - switching_mode: - description: - - Which switching mode to use with this domain association. This attribute can only be used with vmmDomain domain association. - type: str - enhanced_lagpolicy_name: - description: - - EPG enhanced lagpolicy name. This attribute can only be used with vmmDomain domain association. - type: str - enhanced_lagpolicy_dn: - description: - - Distinguished name of EPG lagpolicy. This attribute can only be used with vmmDomain domain association. - type: str - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_anp_epg -- module: mso_schema_template_anp_epg -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new static leaf to a site EPG - mso_schema_site_anp_epg_domain: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - domain_association_type: vmmDomain - domain_profile: 'VMware-VMM' - deployment_immediacy: lazy - resolution_immediacy: pre-provision - state: present - delegate_to: localhost - -- name: Remove a static leaf from a site EPG - mso_schema_site_anp_epg_domain: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - domain_association_type: vmmDomain - domain_profile: 'VMware-VMM' - deployment_immediacy: lazy - resolution_immediacy: pre-provision - state: absent - delegate_to: localhost - -- name: Query a specific site EPG static leaf - mso_schema_site_anp_epg_domain: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - domain_association_type: vmmDomain - domain_profile: 'VMware-VMM' - state: query - delegate_to: localhost - register: query_result - -- name: Query all site EPG static leafs - mso_schema_site_anp_epg_domain: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', required=True), - domain_association_type=dict(type='str', choices=['vmmDomain', 'l3ExtDomain', 'l2ExtDomain', 'physicalDomain', 'fibreChannel']), - domain_profile=dict(type='str'), - deployment_immediacy=dict(type='str', choices=['immediate', 'lazy']), - resolution_immediacy=dict(type='str', choices=['immediate', 'lazy', 'pre-provision']), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - micro_seg_vlan_type=dict(type='str'), - micro_seg_vlan=dict(type='int'), - port_encap_vlan_type=dict(type='str'), - port_encap_vlan=dict(type='int'), - vlan_encap_mode=dict(type='str', choices=['static', 'dynamic']), - allow_micro_segmentation=dict(type='bool'), - switch_type=dict(type='str'), - switching_mode=dict(type='str'), - enhanced_lagpolicy_name=dict(type='str'), - enhanced_lagpolicy_dn=dict(type='str'), - - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['domain_association_type', 'domain_profile', 'deployment_immediacy', 'resolution_immediacy']], - ['state', 'present', ['domain_association_type', 'domain_profile', 'deployment_immediacy', 'resolution_immediacy']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - domain_association_type = module.params.get('domain_association_type') - domain_profile = module.params.get('domain_profile') - deployment_immediacy = module.params.get('deployment_immediacy') - resolution_immediacy = module.params.get('resolution_immediacy') - state = module.params.get('state') - micro_seg_vlan_type = module.params.get('micro_seg_vlan_type') - micro_seg_vlan = module.params.get('micro_seg_vlan') - port_encap_vlan_type = module.params.get('port_encap_vlan_type') - port_encap_vlan = module.params.get('port_encap_vlan') - vlan_encap_mode = module.params.get('vlan_encap_mode') - allow_micro_segmentation = module.params.get('allow_micro_segmentation') - switch_type = module.params.get('switch_type') - switching_mode = module.params.get('switching_mode') - enhanced_lagpolicy_name = module.params.get('enhanced_lagpolicy_name') - enhanced_lagpolicy_dn = module.params.get('enhanced_lagpolicy_dn') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get ANP - anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) - anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] - if anp_ref not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp_ref) - - # Get EPG - epg_ref = mso.epg_ref(schema_id=schema_id, template=template, anp=anp, epg=epg) - print(epg_ref) - epgs = [e.get('epgRef') for e in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs']] - if epg_ref not in epgs: - mso.fail_json(msg="Provided epg '{0}' does not exist. Existing epgs: {1} epgref {2}".format(epg, str(schema_obj.get('sites')[site_idx]), epg_ref)) - epg_idx = epgs.index(epg_ref) - - if domain_association_type == 'vmmDomain': - domain_dn = 'uni/vmmp-VMware/dom-{0}'.format(domain_profile) - elif domain_association_type == 'l3ExtDomain': - domain_dn = 'uni/l3dom-{0}'.format(domain_profile) - elif domain_association_type == 'l2ExtDomain': - domain_dn = 'uni/l2dom-{0}'.format(domain_profile) - elif domain_association_type == 'physicalDomain': - domain_dn = 'uni/phys-{0}'.format(domain_profile) - elif domain_association_type == 'fibreChannel': - domain_dn = 'uni/fc-{0}'.format(domain_profile) - else: - domain_dn = '' - - # Get Domains - domains = [dom.get('dn') for dom in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['domainAssociations']] - if domain_dn in domains: - domain_idx = domains.index(domain_dn) - domain_path = '/sites/{0}/anps/{1}/epgs/{2}/domainAssociations/{3}'.format(site_template, anp, epg, domain_idx) - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['domainAssociations'][domain_idx] - - if state == 'query': - if domain_association_type is None or domain_profile is None: - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['domainAssociations'] - elif not mso.existing: - mso.fail_json(msg="Domain association '{domain_association_type}/{domain_profile}' not found".format( - domain_association_type=domain_association_type, - domain_profile=domain_profile)) - mso.exit_json() - - domains_path = '/sites/{0}/anps/{1}/epgs/{2}/domainAssociations'.format(site_template, anp, epg) - ops = [] - if domain_association_type == 'vmmDomain': - vmmDomainProperties = {} - if micro_seg_vlan_type and micro_seg_vlan: - microSegVlan = dict(vlanType=micro_seg_vlan_type, vlan=micro_seg_vlan) - vmmDomainProperties['microSegVlan'] = microSegVlan - elif not micro_seg_vlan_type and micro_seg_vlan: - mso.fail_json(msg="micro_seg_vlan_type is required when micro_seg_vlan is provided.") - elif micro_seg_vlan_type and not micro_seg_vlan: - mso.fail_json(msg="micro_seg_vlan is required when micro_seg_vlan_type is provided.") - - if micro_seg_vlan_type and micro_seg_vlan: - portEncapVlan = dict(vlanType=port_encap_vlan_type, vlan=port_encap_vlan) - vmmDomainProperties['portEncapVlan'] = portEncapVlan - elif not port_encap_vlan_type and port_encap_vlan: - mso.fail_json(msg="port_encap_vlan_type is required when port_encap_vlan is provided.") - elif port_encap_vlan_type and not port_encap_vlan: - mso.fail_json(msg="port_encap_vlan is required when port_encap_vlan_type is provided.") - - if vlan_encap_mode: - vmmDomainProperties['vlanEncapMode'] = vlan_encap_mode - - if allow_micro_segmentation: - vmmDomainProperties['allowMicroSegmentation'] = allow_micro_segmentation - if switch_type: - vmmDomainProperties['switchType'] = switch_type - if switching_mode: - vmmDomainProperties['switchingMode'] = switching_mode - - if enhanced_lagpolicy_name and enhanced_lagpolicy_dn: - enhancedLagPol = dict(name=enhanced_lagpolicy_name, dn=enhanced_lagpolicy_dn) - epgLagPol = dict(enhancedLagPol=enhancedLagPol) - vmmDomainProperties['epgLagPol'] = epgLagPol - elif not enhanced_lagpolicy_name and enhanced_lagpolicy_dn: - mso.fail_json(msg="enhanced_lagpolicy_name is required when enhanced_lagpolicy_dn is provided.") - elif enhanced_lagpolicy_name and not enhanced_lagpolicy_dn: - mso.fail_json(msg="enhanced_lagpolicy_dn is required when enhanced_lagpolicy_name is provided.") - - payload = dict( - dn=domain_dn, - domainType=domain_association_type, - deploymentImmediacy=deployment_immediacy, - resolutionImmediacy=resolution_immediacy, - ) - - if vmmDomainProperties: - payload['vmmDomainProperties'] = vmmDomainProperties - else: - payload = dict( - dn=domain_dn, - domainType=domain_association_type, - deploymentImmediacy=deployment_immediacy, - resolutionImmediacy=resolution_immediacy, - ) - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=domains_path)) - elif state == 'present': - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=domain_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=domains_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticleaf.py b/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticleaf.py deleted file mode 100644 index 4c296d75029..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticleaf.py +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_anp_epg_staticleaf -short_description: Manage site-local EPG static leafs in schema template -description: -- Manage site-local EPG static leafs in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - epg: - description: - - The name of the EPG. - type: str - pod: - description: - - The pod of the static leaf. - type: str - leaf: - description: - - The path of the static leaf. - type: str - aliases: [ name ] - vlan: - description: - - The VLAN id of the static leaf. - type: int - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_anp_epg -- module: mso_schema_template_anp_epg -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new static leaf to a site EPG - mso_schema_site_anp_epg_staticleaf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - leaf: Leaf1 - vlan: 123 - state: present - delegate_to: localhost - -- name: Remove a static leaf from a site EPG - mso_schema_site_anp_epg_staticleaf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - leaf: Leaf1 - state: absent - delegate_to: localhost - -- name: Query a specific site EPG static leaf - mso_schema_site_anp_epg_staticleaf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - leaf: Leaf1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site EPG static leafs - mso_schema_site_anp_epg_staticleaf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', required=True), - pod=dict(type='str'), # This parameter is not required for querying all objects - leaf=dict(type='str', aliases=['name']), - vlan=dict(type='int'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['pod', 'leaf', 'vlan']], - ['state', 'present', ['pod', 'leaf', 'vlan']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - pod = module.params.get('pod') - leaf = module.params.get('leaf') - vlan = module.params.get('vlan') - state = module.params.get('state') - - leafpath = 'topology/{0}/node-{1}'.format(pod, leaf) - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get ANP - anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) - anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] - if anp_ref not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp_ref) - - # Get EPG - epg_ref = mso.epg_ref(schema_id=schema_id, template=template, anp=anp, epg=epg) - epgs = [e.get('epgRef') for e in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs']] - if epg_ref not in epgs: - mso.fail_json(msg="Provided epg '{0}' does not exist. Existing epgs: {1}".format(epg, ', '.join(epgs))) - epg_idx = epgs.index(epg_ref) - - # Get Leaf - leafs = [(l.get('path'), l.get('portEncapVlan')) for l in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['staticLeafs']] - if (leafpath, vlan) in leafs: - leaf_idx = leafs.index((leafpath, vlan)) - # FIXME: Changes based on index are DANGEROUS - leaf_path = '/sites/{0}/anps/{1}/epgs/{2}/staticLeafs/{3}'.format(site_template, anp, epg, leaf_idx) - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['staticLeafs'][leaf_idx] - - if state == 'query': - if leaf is None or vlan is None: - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['staticLeafs'] - elif not mso.existing: - mso.fail_json(msg="Static leaf '{leaf}/{vlan}' not found".format(leaf=leaf, vlan=vlan)) - mso.exit_json() - - leafs_path = '/sites/{0}/anps/{1}/epgs/{2}/staticLeafs'.format(site_template, anp, epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=leaf_path)) - - elif state == 'present': - payload = dict( - path=leafpath, - portEncapVlan=vlan, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=leaf_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=leafs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticport.py b/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticport.py deleted file mode 100644 index c2db268ba11..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticport.py +++ /dev/null @@ -1,315 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_anp_epg_staticport -short_description: Manage site-local EPG static ports in schema template -description: -- Manage site-local EPG static ports in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - epg: - description: - - The name of the EPG. - type: str - type: - description: - - The path type of the static port - type: str - choices: [ port, vpc ] - default: port - pod: - description: - - The pod of the static port. - type: str - leaf: - description: - - The leaf of the static port. - type: str - path: - description: - - The path of the static port. - type: str - vlan: - description: - - The port encap VLAN id of the static port. - type: int - deployment_immediacy: - description: - - The deployment immediacy of the static port. - - C(immediate) means B(Deploy immediate). - - C(lazy) means B(deploy on demand). - type: str - choices: [ immediate, lazy ] - mode: - description: - - The mode of the static port. - - C(native) means B(Access (802.1p)). - - C(regular) means B(Trunk). - - C(untagged) means B(Access (untagged)). - type: str - choices: [ native, regular, untagged ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing an object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_anp_epg -- module: mso_schema_template_anp_epg -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new static port to a site EPG - mso_schema_site_anp_epg_staticport: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - type: port - pod: pod-1 - leaf: 101 - path: eth1/1 - vlan: 126 - deployment_immediacy: immediate - state: present - delegate_to: localhost - -- name: Remove a static port from a site EPG - mso_schema_site_anp_epg_staticport: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - type: port - pod: pod-1 - leaf: 101 - path: eth1/1 - state: absent - delegate_to: localhost - -- name: Query a specific site EPG static port - mso_schema_site_anp_epg_staticport: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - type: port - pod: pod-1 - leaf: 101 - path: eth1/1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site EPG static ports - mso_schema_site_anp_epg_staticport: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', required=True), - type=dict(type='str', default='port', choices=['port', 'vpc']), - pod=dict(type='str'), # This parameter is not required for querying all objects - leaf=dict(type='str'), # This parameter is not required for querying all objects - path=dict(type='str'), # This parameter is not required for querying all objects - vlan=dict(type='int'), # This parameter is not required for querying all objects - deployment_immediacy=dict(type='str', choices=['immediate', 'lazy']), - mode=dict(type='str', choices=['native', 'regular', 'untagged']), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['type', 'pod', 'leaf', 'path', 'vlan']], - ['state', 'present', ['type', 'pod', 'leaf', 'path', 'vlan']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - path_type = module.params.get('type') - pod = module.params.get('pod') - leaf = module.params.get('leaf') - path = module.params.get('path') - vlan = module.params.get('vlan') - deployment_immediacy = module.params.get('deployment_immediacy') - mode = module.params.get('mode') - state = module.params.get('state') - - if path_type == 'port': - portpath = 'topology/{0}/paths-{1}/pathep-[{2}]'.format(pod, leaf, path) - elif path_type == 'vpc': - portpath = 'topology/{0}/protpaths-{1}/pathep-[{2}]'.format(pod, leaf, path) - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get ANP - anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) - anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] - if anp_ref not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp_ref) - - # Get EPG - epg_ref = mso.epg_ref(schema_id=schema_id, template=template, anp=anp, epg=epg) - epgs = [e.get('epgRef') for e in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs']] - if epg_ref not in epgs: - mso.fail_json(msg="Provided epg '{0}' does not exist. Existing epgs: {1}".format(epg, ', '.join(epgs))) - epg_idx = epgs.index(epg_ref) - - # Get Leaf - portpaths = [p.get('path') for p in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['staticPorts']] - if portpath in portpaths: - portpath_idx = portpaths.index(portpath) - # FIXME: Changes based on index are DANGEROUS - port_path = '/sites/{0}/anps/{1}/epgs/{2}/staticPorts/{3}'.format(site_template, anp, epg, portpath_idx) - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['staticPorts'][portpath_idx] - - if state == 'query': - if leaf is None or vlan is None: - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['staticPorts'] - elif not mso.existing: - mso.fail_json(msg="Static port '{portpath}' not found".format(portpath=portpath)) - mso.exit_json() - - ports_path = '/sites/{0}/anps/{1}/epgs/{2}/staticPorts'.format(site_template, anp, epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=port_path)) - - elif state == 'present': - if not mso.existing: - if deployment_immediacy is None: - deployment_immediacy = 'lazy' - if mode is None: - mode = 'untagged' - - payload = dict( - deploymentImmediacy=deployment_immediacy, - mode=mode, - path=portpath, - portEncapVlan=vlan, - type=path_type, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=port_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=ports_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_subnet.py b/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_subnet.py deleted file mode 100644 index 92d168ce1d9..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_anp_epg_subnet.py +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_anp_epg_subnet -short_description: Manage site-local EPG subnets in schema template -description: -- Manage site-local EPG subnets in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - epg: - description: - - The name of the EPG. - type: str - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - aliases: [ ip ] - description: - description: - - The description of this subnet. - type: str - scope: - description: - - The scope of the subnet. - type: str - choices: [ private, public ] - shared: - description: - - Whether this subnet is shared between VRFs. - type: bool - no_default_gateway: - description: - - Whether this subnet has a default gateway. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_anp_epg -- module: mso_schema_template_anp_epg_subnet -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new subnet to a site EPG - mso_schema_site_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - subnet: 10.0.0.0/24 - state: present - delegate_to: localhost - -- name: Remove a subnet from a site EPG - mso_schema_site_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - subnet: 10.0.0.0/24 - state: absent - delegate_to: localhost - -- name: Query a specific site EPG subnet - mso_schema_site_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - epg: EPG1 - subnet: 10.0.0.0/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site EPG subnets - mso_schema_site_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - anp: ANP1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_subnet_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - argument_spec.update(mso_subnet_spec()) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['subnet']], - ['state', 'present', ['subnet']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - subnet = module.params.get('subnet') - description = module.params.get('description') - scope = module.params.get('scope') - shared = module.params.get('shared') - no_default_gateway = module.params.get('no_default_gateway') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get ANP - anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) - anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] - if anp_ref not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp_ref) - - # Get EPG - epg_ref = mso.epg_ref(schema_id=schema_id, template=template, anp=anp, epg=epg) - epgs = [e.get('epgRef') for e in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs']] - if epg_ref not in epgs: - mso.fail_json(msg="Provided epg '{0}' does not exist. Existing epgs: {1}".format(epg, ', '.join(epgs))) - epg_idx = epgs.index(epg_ref) - - # Get Subnet - subnets = [s.get('ip') for s in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets']] - if subnet in subnets: - subnet_idx = subnets.index(subnet) - # FIXME: Changes based on index are DANGEROUS - subnet_path = '/sites/{0}/anps/{1}/epgs/{2}/subnets/{3}'.format(site_template, anp, epg, subnet_idx) - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets'][subnet_idx] - - if state == 'query': - if subnet is None: - mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets'] - elif not mso.existing: - mso.fail_json(msg="Subnet '{subnet}' not found".format(subnet=subnet)) - mso.exit_json() - - subnets_path = '/sites/{0}/anps/{1}/epgs/{2}/subnets'.format(site_template, anp, epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=subnet_path)) - - elif state == 'present': - if not mso.existing: - if description is None: - description = subnet - if scope is None: - scope = 'private' - if shared is None: - shared = False - if no_default_gateway is None: - no_default_gateway = False - - payload = dict( - ip=subnet, - description=description, - scope=scope, - shared=shared, - noDefaultGateway=no_default_gateway, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_bd.py b/lib/ansible/modules/network/aci/mso_schema_site_bd.py deleted file mode 100644 index 636cbbf9ab5..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_bd.py +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_bd -short_description: Manage site-local Bridge Domains (BDs) in schema template -description: -- Manage site-local BDs in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - bd: - description: - - The name of the BD to manage. - type: str - aliases: [ name ] - host_route: - description: - - Whether host-based routing is enabled. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_site -- module: mso_schema_site_bd_l3out -- module: mso_schema_site_bd_subnet -- module: mso_schema_template_bd -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site BD - mso_schema_site_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - state: present - delegate_to: localhost - -- name: Remove a site BD - mso_schema_site_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - state: absent - delegate_to: localhost - -- name: Query a specific site BD - mso_schema_site_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site BDs - mso_schema_site_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - bd=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - host_route=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['bd']], - ['state', 'present', ['bd']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - bd = module.params.get('bd') - host_route = module.params.get('host_route') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get BD - bd_ref = mso.bd_ref(schema_id=schema_id, template=template, bd=bd) - bds = [v.get('bdRef') for v in schema_obj.get('sites')[site_idx]['bds']] - if bd is not None and bd_ref in bds: - bd_idx = bds.index(bd_ref) - bd_path = '/sites/{0}/bds/{1}'.format(site_template, bd) - mso.existing = schema_obj.get('sites')[site_idx]['bds'][bd_idx] - - if state == 'query': - if bd is None: - mso.existing = schema_obj.get('sites')[site_idx]['bds'] - elif not mso.existing: - mso.fail_json(msg="BD '{bd}' not found".format(bd=bd)) - mso.exit_json() - - bds_path = '/sites/{0}/bds'.format(site_template) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=bd_path)) - - elif state == 'present': - if not mso.existing: - if host_route is None: - host_route = False - - payload = dict( - bdRef=dict( - schemaId=schema_id, - templateName=template, - bdName=bd, - ), - hostBasedRouting=host_route, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=bd_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=bds_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_bd_l3out.py b/lib/ansible/modules/network/aci/mso_schema_site_bd_l3out.py deleted file mode 100644 index a296d44c6c8..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_bd_l3out.py +++ /dev/null @@ -1,222 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_bd_l3out -short_description: Manage site-local BD l3out's in schema template -description: -- Manage site-local BDs l3out's in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - bd: - description: - - The name of the BD. - type: str - aliases: [ name ] - l3out: - description: - - The name of the l3out. - type: str - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_bd -- module: mso_schema_template_bd -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site BD l3out - mso_schema_site_bd_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - l3out: L3out1 - state: present - delegate_to: localhost - -- name: Remove a site BD l3out - mso_schema_site_bd_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - l3out: L3out1 - state: absent - delegate_to: localhost - -- name: Query a specific site BD l3out - mso_schema_site_bd_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - l3out: L3out1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site BD l3outs - mso_schema_site_bd_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - bd=dict(type='str', required=True), - l3out=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['l3out']], - ['state', 'present', ['l3out']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - bd = module.params.get('bd') - l3out = module.params.get('l3out') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get BD - bd_ref = mso.bd_ref(schema_id=schema_id, template=template, bd=bd) - bds = [v.get('bdRef') for v in schema_obj.get('sites')[site_idx]['bds']] - if bd_ref not in bds: - mso.fail_json(msg="Provided BD '{0}' does not exist. Existing BDs: {1}".format(bd, ', '.join(bds))) - bd_idx = bds.index(bd_ref) - - # Get L3out - l3outs = schema_obj.get('sites')[site_idx]['bds'][bd_idx]['l3Outs'] - if l3out is not None and l3out in l3outs: - l3out_idx = l3outs.index(l3out) - # FIXME: Changes based on index are DANGEROUS - l3out_path = '/sites/{0}/bds/{1}/l3Outs/{2}'.format(site_template, bd, l3out_idx) - mso.existing = schema_obj.get('sites')[site_idx]['bds'][bd_idx]['l3Outs'][l3out_idx] - - if state == 'query': - if l3out is None: - mso.existing = schema_obj.get('sites')[site_idx]['bds'][bd_idx]['l3Outs'] - elif not mso.existing: - mso.fail_json(msg="L3out '{l3out}' not found".format(l3out=l3out)) - mso.exit_json() - - l3outs_path = '/sites/{0}/bds/{1}/l3Outs'.format(site_template, bd) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=l3out_path)) - - elif state == 'present': - mso.sent = l3out - if not mso.existing: - ops.append(dict(op='add', path=l3outs_path + '/-', value=l3out)) - - mso.existing = mso.sent - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_bd_subnet.py b/lib/ansible/modules/network/aci/mso_schema_site_bd_subnet.py deleted file mode 100644 index f2c74f1ea00..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_bd_subnet.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_bd_subnet -short_description: Manage site-local BD subnets in schema template -description: -- Manage site-local BD subnets in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - bd: - description: - - The name of the BD. - type: str - aliases: [ name ] - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - aliases: [ ip ] - description: - description: - - The description of this subnet. - type: str - scope: - description: - - The scope of the subnet. - type: str - choices: [ private, public ] - shared: - description: - - Whether this subnet is shared between VRFs. - type: bool - no_default_gateway: - description: - - Whether this subnet has a default gateway. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_bd -- module: mso_schema_template_bd -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site BD subnet - mso_schema_site_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - subnet: 11.11.11.0/24 - state: present - delegate_to: localhost - -- name: Remove a site BD subnet - mso_schema_site_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - subnet: 11.11.11.0/24 - state: absent - delegate_to: localhost - -- name: Query a specific site BD subnet - mso_schema_site_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - subnet: 11.11.11.0/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site BD subnets - mso_schema_site_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - bd: BD1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_subnet_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - bd=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - argument_spec.update(mso_subnet_spec()) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['bd']], - ['state', 'present', ['bd']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - bd = module.params.get('bd') - subnet = module.params.get('subnet') - description = module.params.get('description') - scope = module.params.get('scope') - shared = module.params.get('shared') - no_default_gateway = module.params.get('no_default_gateway') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get BD - bd_ref = mso.bd_ref(schema_id=schema_id, template=template, bd=bd) - bds = [v.get('bdRef') for v in schema_obj.get('sites')[site_idx]['bds']] - if bd_ref not in bds: - mso.fail_json(msg="Provided BD '{0}' does not exist. Existing BDs: {1}".format(bd, ', '.join(bds))) - bd_idx = bds.index(bd_ref) - - # Get Subnet - subnets = [s.get('ip') for s in schema_obj.get('sites')[site_idx]['bds'][bd_idx]['subnets']] - if subnet in subnets: - subnet_idx = subnets.index(subnet) - # FIXME: Changes based on index are DANGEROUS - subnet_path = '/sites/{0}/bds/{1}/subnets/{2}'.format(site_template, bd, subnet_idx) - mso.existing = schema_obj.get('sites')[site_idx]['bds'][bd_idx]['subnets'][subnet_idx] - - if state == 'query': - if subnet is None: - mso.existing = schema_obj.get('sites')[site_idx]['bds'][bd_idx]['subnets'] - elif not mso.existing: - mso.fail_json(msg="Subnet IP '{subnet}' not found".format(subnet=subnet)) - mso.exit_json() - - subnets_path = '/sites/{0}/bds/{1}/subnets'.format(site_template, bd) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=subnet_path)) - - elif state == 'present': - if not mso.existing: - if description is None: - description = subnet - if scope is None: - scope = 'private' - if shared is None: - shared = False - if no_default_gateway is None: - no_default_gateway = False - - payload = dict( - ip=subnet, - description=description, - scope=scope, - shared=shared, - noDefaultGateway=no_default_gateway, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_vrf.py b/lib/ansible/modules/network/aci/mso_schema_site_vrf.py deleted file mode 100644 index 7e9e3ff3a2a..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_vrf.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_vrf -short_description: Manage site-local VRFs in schema template -description: -- Manage site-local VRFs in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - vrf: - description: - - The name of the VRF to manage. - type: str - aliases: [ name ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_site -- module: mso_schema_template_vrf -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site VRF - mso_schema_site_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - state: present - delegate_to: localhost - -- name: Remove a site VRF - mso_schema_site_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - state: absent - delegate_to: localhost - -- name: Query a specific site VRF - mso_schema_site_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site VRFs - mso_schema_site_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - vrf=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['vrf']], - ['state', 'present', ['vrf']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - vrf = module.params.get('vrf') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get VRF - vrf_ref = mso.vrf_ref(schema_id=schema_id, template=template, vrf=vrf) - vrfs = [v.get('vrfRef') for v in schema_obj.get('sites')[site_idx]['vrfs']] - if vrf is not None and vrf_ref in vrfs: - vrf_idx = vrfs.index(vrf_ref) - vrf_path = '/sites/{0}/vrfs/{1}'.format(site_template, vrf) - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx] - - if state == 'query': - if vrf is None: - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'] - elif not mso.existing: - mso.fail_json(msg="VRF '{vrf}' not found".format(vrf=vrf)) - mso.exit_json() - - vrfs_path = '/sites/{0}/vrfs'.format(site_template) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=vrf_path)) - - elif state == 'present': - payload = dict( - vrfRef=dict( - schemaId=schema_id, - templateName=template, - vrfName=vrf, - ), - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=vrf_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=vrfs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_vrf_region.py b/lib/ansible/modules/network/aci/mso_schema_site_vrf_region.py deleted file mode 100644 index 1f314d9ae7f..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_vrf_region.py +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_vrf_region -short_description: Manage site-local VRF regions in schema template -description: -- Manage site-local VRF regions in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - vrf: - description: - - The name of the VRF. - type: str - region: - description: - - The name of the region to manage. - type: str - aliases: [ name ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_site_vrf -- module: mso_schema_template_vrf -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site VRF region - mso_schema_template_vrf_region: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - state: present - delegate_to: localhost - -- name: Remove a site VRF region - mso_schema_template_vrf_region: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - state: absent - delegate_to: localhost - -- name: Query a specific site VRF region - mso_schema_template_vrf_region: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site VRF regions - mso_schema_template_vrf_region: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - vrf=dict(type='str', required=True), - region=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['region']], - ['state', 'present', ['region']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - vrf = module.params.get('vrf') - region = module.params.get('region') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get VRF - vrf_ref = mso.vrf_ref(schema_id=schema_id, template=template, vrf=vrf) - vrfs = [v.get('vrfRef') for v in schema_obj.get('sites')[site_idx]['vrfs']] - if vrf_ref not in vrfs: - mso.fail_json(msg="Provided vrf '{0}' does not exist. Existing vrfs: {1}".format(vrf, ', '.join(vrfs))) - vrf_idx = vrfs.index(vrf_ref) - - # Get Region - regions = [r.get('name') for r in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions']] - if region is not None and region in regions: - region_idx = regions.index(region) - region_path = '/sites/{0}/vrfs/{1}/regions/{2}'.format(site_template, vrf, region) - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx] - - if state == 'query': - if region is None: - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'] - elif not mso.existing: - mso.fail_json(msg="Region '{region}' not found".format(region=region)) - mso.exit_json() - - regions_path = '/sites/{0}/vrfs/{1}/regions'.format(site_template, vrf) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=region_path)) - - elif state == 'present': - - payload = dict( - name=region, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=region_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=regions_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr.py b/lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr.py deleted file mode 100644 index 842e58fe22a..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr.py +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_vrf_region_cidr -short_description: Manage site-local VRF region CIDRs in schema template -description: -- Manage site-local VRF region CIDRs in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - vrf: - description: - - The name of the VRF. - type: str - region: - description: - - The name of the region. - type: str - cidr: - description: - - The name of the region CIDR to manage. - type: str - aliases: [ ip ] - primary: - description: - - Whether this is the primary CIDR. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_vrf_region -- module: mso_schema_site_vrf_region_cidr_subnet -- module: mso_schema_template_vrf -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site VRF region CIDR - mso_schema_template_vrf_region_cidr: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - state: present - delegate_to: localhost - -- name: Remove a site VRF region CIDR - mso_schema_template_vrf_region_cidr: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - state: absent - delegate_to: localhost - -- name: Query a specific site VRF region CIDR - mso_schema_template_vrf_region_cidr: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site VRF region CIDR - mso_schema_template_vrf_region_cidr: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - vrf=dict(type='str', required=True), - region=dict(type='str', required=True), - cidr=dict(type='str', aliases=['ip']), # This parameter is not required for querying all objects - primary=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['cidr']], - ['state', 'present', ['cidr']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - vrf = module.params.get('vrf') - region = module.params.get('region') - cidr = module.params.get('cidr') - primary = module.params.get('primary') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get VRF - vrf_ref = mso.vrf_ref(schema_id=schema_id, template=template, vrf=vrf) - vrfs = [v.get('vrfRef') for v in schema_obj.get('sites')[site_idx]['vrfs']] - if vrf_ref not in vrfs: - mso.fail_json(msg="Provided vrf '{0}' does not exist. Existing vrfs: {1}".format(vrf, ', '.join(vrfs))) - vrf_idx = vrfs.index(vrf_ref) - - # Get Region - regions = [r.get('name') for r in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions']] - if region not in regions: - mso.fail_json(msg="Provided region '{0}' does not exist. Existing regions: {1}".format(region, ', '.join(regions))) - region_idx = regions.index(region) - - # Get CIDR - cidrs = [c.get('ip') for c in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs']] - if cidr is not None and cidr in cidrs: - cidr_idx = cidrs.index(cidr) - # FIXME: Changes based on index are DANGEROUS - cidr_path = '/sites/{0}/vrfs/{1}/regions/{2}/cidrs/{3}'.format(site_template, vrf, region, cidr_idx) - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs'][cidr_idx] - - if state == 'query': - if cidr is None: - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs'] - elif not mso.existing: - mso.fail_json(msg="CIDR IP '{cidr}' not found".format(cidr=cidr)) - mso.exit_json() - - cidrs_path = '/sites/{0}/vrfs/{1}/regions/{2}/cidrs'.format(site_template, vrf, region) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=cidr_path)) - - elif state == 'present': - if not mso.existing: - if primary is None: - primary = False - - payload = dict( - ip=cidr, - primary=primary, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=cidr_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=cidrs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr_subnet.py b/lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr_subnet.py deleted file mode 100644 index 1e26932e844..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr_subnet.py +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Dag Wieers (@dagwieers) -# 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: mso_schema_site_vrf_region_cidr_subnet -short_description: Manage site-local VRF regions in schema template -description: -- Manage site-local VRF regions in schema template on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - site: - description: - - The name of the site. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - vrf: - description: - - The name of the VRF. - type: str - region: - description: - - The name of the region. - type: str - cidr: - description: - - The IP range of for the region CIDR. - type: str - subnet: - description: - - The IP subnet of this region CIDR. - type: str - aliases: [ ip ] - zone: - description: - - The name of the zone for the region CIDR subnet. - type: str - aliases: [ name ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index. - This can cause silent corruption on concurrent access when changing/removing on object as - the wrong object may be referenced. This module is affected by this deficiency. -seealso: -- module: mso_schema_site_vrf_region_cidr -- module: mso_schema_template_vrf -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site VRF region CIDR subnet - mso_schema_template_vrf_region_cidr_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - subnet: 14.14.14.2/24 - zone: us-west-1a - state: present - delegate_to: localhost - -- name: Remove a site VRF region CIDR subnet - mso_schema_site_vrf_region_cidr_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - subnet: 14.14.14.2/24 - state: absent - delegate_to: localhost - -- name: Query a specific site VRF region CIDR subnet - mso_schema_site_vrf_region_cidr_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - subnet: 14.14.14.2/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all site VRF region CIDR subnet - mso_schema_site_vrf_region_cidr_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema1 - site: Site1 - template: Template1 - vrf: VRF1 - region: us-west-1 - cidr: 14.14.14.1/24 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - site=dict(type='str', required=True), - template=dict(type='str', required=True), - vrf=dict(type='str', required=True), - region=dict(type='str', required=True), - cidr=dict(type='str', required=True), - subnet=dict(type='str', aliases=['ip']), # This parameter is not required for querying all objects - zone=dict(type='str', aliases=['name']), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['subnet']], - ['state', 'present', ['subnet', 'zone']], - ], - ) - - schema = module.params.get('schema') - site = module.params.get('site') - template = module.params.get('template') - vrf = module.params.get('vrf') - region = module.params.get('region') - cidr = module.params.get('cidr') - subnet = module.params.get('subnet') - zone = module.params.get('zone') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - schema_id = schema_obj.get('id') - - # Get site - site_id = mso.lookup_site(site) - - # Get site_idx - sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] - if (site_id, template) not in sites: - mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) - - # Schema-access uses indexes - site_idx = sites.index((site_id, template)) - # Path-based access uses site_id-template - site_template = '{0}-{1}'.format(site_id, template) - - # Get VRF - vrf_ref = mso.vrf_ref(schema_id=schema_id, template=template, vrf=vrf) - vrfs = [v.get('vrfRef') for v in schema_obj.get('sites')[site_idx]['vrfs']] - if vrf_ref not in vrfs: - mso.fail_json(msg="Provided vrf '{0}' does not exist. Existing vrfs: {1}".format(vrf, ', '.join(vrfs))) - vrf_idx = vrfs.index(vrf_ref) - - # Get Region - regions = [r.get('name') for r in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions']] - if region not in regions: - mso.fail_json(msg="Provided region '{0}' does not exist. Existing regions: {1}".format(region, ', '.join(regions))) - region_idx = regions.index(region) - - # Get CIDR - cidrs = [c.get('ip') for c in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs']] - if cidr not in cidrs: - mso.fail_json(msg="Provided CIDR IP '{0}' does not exist. Existing CIDR IPs: {1}".format(cidr, ', '.join(cidrs))) - cidr_idx = cidrs.index(cidr) - - # Get Subnet - subnets = [s.get('ip') for s in schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs'][cidr_idx]['subnets']] - if subnet is not None and subnet in subnets: - subnet_idx = subnets.index(subnet) - # FIXME: Changes based on index are DANGEROUS - subnet_path = '/sites/{0}/vrfs/{1}/regions/{2}/cidrs/{3}/subnets/{4}'.format(site_template, vrf, region, cidr_idx, subnet_idx) - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs'][cidr_idx]['subnets'][subnet_idx] - - if state == 'query': - if subnet is None: - mso.existing = schema_obj.get('sites')[site_idx]['vrfs'][vrf_idx]['regions'][region_idx]['cidrs'][cidr_idx]['subnets'] - elif not mso.existing: - mso.fail_json(msg="Subnet IP '{subnet}' not found".format(subnet=subnet)) - mso.exit_json() - - subnets_path = '/sites/{0}/vrfs/{1}/regions/{2}/cidrs/{3}/subnets'.format(site_template, vrf, region, cidr_idx) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=subnet_path)) - - elif state == 'present': - - payload = dict( - ip=subnet, - zone=zone, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template.py b/lib/ansible/modules/network/aci/mso_schema_template.py deleted file mode 100644 index a2af7cb6b53..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template -short_description: Manage templates in schemas -description: -- Manage templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - tenant: - description: - - The tenant used for this template. - type: str - required: yes - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- Due to restrictions of the MSO REST API this module creates schemas when needed, and removes them when the last template has been removed. -seealso: -- module: mso_schema -- module: mso_schema_site -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new template to a schema - mso_schema_template: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: Tenant 1 - schema: Schema 1 - template: Template 1 - state: present - delegate_to: localhost - -- name: Remove a template from a schema - mso_schema_template: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: Tenant 1 - schema: Schema 1 - template: Template 1 - state: absent - delegate_to: localhost - -- name: Query a template - mso_schema_template: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: Tenant 1 - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all templates - mso_schema_template: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: Tenant 1 - schema: Schema 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - tenant=dict(type='str', required=True), - schema=dict(type='str', required=True), - template=dict(type='str', aliases=['name']), - display_name=dict(type='str'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['template']], - ['state', 'present', ['template']], - ], - ) - - tenant = module.params.get('tenant') - schema = module.params.get('schema') - template = module.params.get('template') - display_name = module.params.get('display_name') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema - schema_obj = mso.get_obj('schemas', displayName=schema) - - mso.existing = {} - if schema_obj: - # Schema exists - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template: - if template in templates: - template_idx = templates.index(template) - mso.existing = schema_obj.get('templates')[template_idx] - else: - mso.existing = schema_obj.get('templates') - else: - schema_path = 'schemas' - - if state == 'query': - if not mso.existing: - if template: - mso.fail_json(msg="Template '{0}' not found".format(template)) - else: - mso.existing = [] - mso.exit_json() - - template_path = '/templates/{0}'.format(template) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - mso.proposed = mso.sent = {} - - if not schema_obj: - # There was no schema to begin with - pass - elif len(templates) == 1: - # There is only one tenant, remove schema - mso.existing = {} - if not module.check_mode: - mso.request(schema_path, method='DELETE') - elif mso.existing: - # Remove existing template - mso.existing = {} - ops.append(dict(op='remove', path=template_path)) - else: - # There was no template to begin with - pass - - elif state == 'present': - tenant_id = mso.lookup_tenant(tenant) - - if display_name is None: - display_name = mso.existing.get('displayName', template) - - if not schema_obj: - # Schema does not exist, so we have to create it - payload = dict( - displayName=schema, - templates=[dict( - name=template, - displayName=display_name, - tenantId=tenant_id, - )], - sites=[], - ) - - mso.existing = payload.get('templates')[0] - - if not module.check_mode: - mso.request(schema_path, method='POST', data=payload) - - elif mso.existing: - # Template exists, so we have to update it - payload = dict( - name=template, - displayName=display_name, - tenantId=tenant_id, - ) - - mso.sanitize(payload, collate=True) - - ops.append(dict(op='replace', path=template_path + '/displayName', value=display_name)) - ops.append(dict(op='replace', path=template_path + '/tenantId', value=tenant_id)) - - mso.existing = mso.proposed - else: - # Template does not exist, so we have to add it - payload = dict( - name=template, - displayName=display_name, - tenantId=tenant_id, - ) - - mso.sanitize(payload, collate=True) - - ops.append(dict(op='add', path='/templates/-', value=payload)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp.py b/lib/ansible/modules/network/aci/mso_schema_template_anp.py deleted file mode 100644 index 5be9caa9ed3..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_anp -short_description: Manage Application Network Profiles (ANPs) in schema templates -description: -- Manage ANPs in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_template -- module: mso_schema_template_anp_epg -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new ANP - mso_schema_template_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - state: present - delegate_to: localhost - -- name: Remove an ANP - mso_schema_template_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - state: absent - delegate_to: localhost - -- name: Query a specific ANPs - mso_schema_template_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all ANPs - mso_schema_template_anp: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - display_name=dict(type='str'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['anp']], - ['state', 'present', ['anp']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - anp = module.params.get('anp') - display_name = module.params.get('display_name') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get ANP - anps = [a.get('name') for a in schema_obj.get('templates')[template_idx]['anps']] - - if anp is not None and anp in anps: - anp_idx = anps.index(anp) - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx] - - if state == 'query': - if anp is None: - mso.existing = schema_obj.get('templates')[template_idx]['anps'] - elif not mso.existing: - mso.fail_json(msg="ANP '{anp}' not found".format(anp=anp)) - mso.exit_json() - - anps_path = '/templates/{0}/anps'.format(template) - anp_path = '/templates/{0}/anps/{1}'.format(template, anp) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=anp_path)) - - elif state == 'present': - - if display_name is None and not mso.existing: - display_name = anp - - epgs = [] - if mso.existing: - epgs = None - - payload = dict( - name=anp, - displayName=display_name, - epgs=epgs, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - if display_name is not None: - ops.append(dict(op='replace', path=anp_path + '/displayName', value=display_name)) - else: - ops.append(dict(op='add', path=anps_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py deleted file mode 100644 index 6d91ddc6187..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py +++ /dev/null @@ -1,373 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_anp_epg -short_description: Manage Endpoint Groups (EPGs) in schema templates -description: -- Manage EPGs in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - required: yes - epg: - description: - - The name of the EPG to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str -# contracts: -# description: -# - A list of contracts associated to this ANP. -# type: list - bd: - description: - - The BD associated to this ANP. - type: dict - suboptions: - name: - description: - - The name of the BD to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced BD. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced BD. - type: str - vrf: - version_added: '2.9' - description: - - The VRF associated to this ANP. - type: dict - suboptions: - name: - description: - - The name of the VRF to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced VRF. - type: str - subnets: - description: - - The subnets associated to this ANP. - type: list - suboptions: - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - aliases: [ ip ] - description: - description: - - The description of this subnet. - type: str - scope: - description: - - The scope of the subnet. - type: str - choices: [ private, public ] - shared: - description: - - Whether this subnet is shared between VRFs. - type: bool - no_default_gateway: - description: - - Whether this subnet has a default gateway. - type: bool - useg_epg: - description: - - Whether this is a USEG EPG. - type: bool -# useg_epg_attributes: -# description: -# - A dictionary consisting of USEG attributes. -# type: dict - intra_epg_isolation: - description: - - Whether intra EPG isolation is enforced. - - When not specified, this parameter defaults to C(unenforced). - type: str - choices: [ enforced, unenforced ] - intersite_multicaste_source: - description: - - Whether intersite multicast source is enabled. - - When not specified, this parameter defaults to C(no). - type: bool - preferred_group: - description: - - Whether this EPG is added to preferred group or not. - - When not specified, this parameter defaults to C(no). - type: bool - version_added: 2.9 - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_template_anp -- module: mso_schema_template_anp_epg_subnet -- module: mso_schema_template_bd -- module: mso_schema_template_contract_filter -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new EPG - mso_schema_template_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - bd: - name: bd1 - vrf: - name: vrf1 - state: present - delegate_to: localhost - -- name: Add a new EPG with preferred group. - mso_schema_template_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - state: present - preferred_group: yes - delegate_to: localhost - -- name: Remove an EPG - mso_schema_template_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - bd: - name: bd1 - vrf: - name: vrf1 - state: absent - delegate_to: localhost - -- name: Query a specific EPG - mso_schema_template_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - bd: - name: bd1 - vrf: - name: vrf1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all EPGs - mso_schema_template_anp_epg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - bd: - name: bd1 - vrf: - name: vrf1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, mso_subnet_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - bd=dict(type='dict', options=mso_reference_spec()), - vrf=dict(type='dict', options=mso_reference_spec()), - display_name=dict(type='str'), - useg_epg=dict(type='bool'), - intra_epg_isolation=dict(type='str', choices=['enforced', 'unenforced']), - intersite_multicaste_source=dict(type='bool'), - subnets=dict(type='list', options=mso_subnet_spec()), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - preferred_group=dict(type='bool'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['epg']], - ['state', 'present', ['epg']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - display_name = module.params.get('display_name') - bd = module.params.get('bd') - vrf = module.params.get('vrf') - useg_epg = module.params.get('useg_epg') - intra_epg_isolation = module.params.get('intra_epg_isolation') - intersite_multicaste_source = module.params.get('intersite_multicaste_source') - subnets = module.params.get('subnets') - state = module.params.get('state') - preferred_group = module.params.get('preferred_group') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj.get('id') - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get ANP - anps = [a.get('name') for a in schema_obj.get('templates')[template_idx]['anps']] - if anp not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp) - - # Get EPG - epgs = [e.get('name') for e in schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs']] - if epg is not None and epg in epgs: - epg_idx = epgs.index(epg) - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx] - - if state == 'query': - if epg is None: - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'] - elif not mso.existing: - mso.fail_json(msg="EPG '{epg}' not found".format(epg=epg)) - mso.exit_json() - - epgs_path = '/templates/{0}/anps/{1}/epgs'.format(template, anp) - epg_path = '/templates/{0}/anps/{1}/epgs/{2}'.format(template, anp, epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=epg_path)) - - elif state == 'present': - bd_ref = mso.make_reference(bd, 'bd', schema_id, template) - vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) - subnets = mso.make_subnets(subnets) - - if display_name is None and not mso.existing: - display_name = epg - - payload = dict( - name=epg, - displayName=display_name, - uSegEpg=useg_epg, - intraEpg=intra_epg_isolation, - proxyArp=intersite_multicaste_source, - # FIXME: Missing functionality - # uSegAttrs=[], - contractRelationships=[], - subnets=subnets, - bdRef=bd_ref, - preferredGroup=preferred_group, - vrfRef=vrf_ref, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=epg_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=epgs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_contract.py b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_contract.py deleted file mode 100644 index c05a9e4dab4..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_contract.py +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_anp_epg_contract -short_description: Manage EPG contracts in schema templates -description: -- Manage EPG contracts in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template to change. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - required: yes - epg: - description: - - The name of the EPG to manage. - type: str - required: yes - contract: - description: - - A contract associated to this EPG. - type: dict - suboptions: - name: - description: - - The name of the Contract to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced BD. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced BD. - type: str - type: - description: - - The type of contract. - type: str - required: true - choices: [ consumer, provider ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_template_anp_epg -- module: mso_schema_template_contract_filter -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a contract to an EPG - mso_schema_template_anp_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - contract: - name: Contract 1 - type: consumer - state: present - delegate_to: localhost - -- name: Remove a Contract - mso_schema_template_anp_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - contract: - name: Contract 1 - state: absent - delegate_to: localhost - -- name: Query a specific Contract - mso_schema_template_anp_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - contract: - name: Contract 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all Contracts - mso_schema_template_anp_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_contractref_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', required=True), - contract=dict(type='dict', options=mso_contractref_spec()), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['contract']], - ['state', 'present', ['contract']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - contract = module.params.get('contract') - state = module.params.get('state') - - mso = MSOModule(module) - - if contract: - if contract.get('schema') is None: - contract['schema'] = schema - contract['schema_id'] = mso.lookup_schema(contract.get('schema')) - if contract.get('template') is None: - contract['template'] = template - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj.get('id') - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get ANP - anps = [a.get('name') for a in schema_obj.get('templates')[template_idx]['anps']] - if anp not in anps: - mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) - anp_idx = anps.index(anp) - - # Get EPG - epgs = [e.get('name') for e in schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs']] - if epg not in epgs: - mso.fail_json(msg="Provided epg '{epg}' does not exist. Existing epgs: {epgs}".format(epg=epg, epgs=', '.join(epgs))) - epg_idx = epgs.index(epg) - - # Get Contract - if contract: - contracts = [(c.get('contractRef'), - c.get('relationshipType')) for c in schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx]['contractRelationships']] - contract_ref = mso.contract_ref(**contract) - if (contract_ref, contract.get('type')) in contracts: - contract_idx = contracts.index((contract_ref, contract.get('type'))) - contract_path = '/templates/{0}/anps/{1}/epgs/{2}/contractRelationships/{3}'.format(template, anp, epg, contract) - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx]['contractRelationships'][contract_idx] - - if state == 'query': - if not contract: - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx]['contractRelationships'] - elif not mso.existing: - mso.fail_json(msg="Contract '{0}' not found".format(contract_ref)) - mso.exit_json() - - contracts_path = '/templates/{0}/anps/{1}/epgs/{2}/contractRelationships'.format(template, anp, epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=contract_path)) - - elif state == 'present': - payload = dict( - relationshipType=contract.get('type'), - contractRef=dict( - contractName=contract.get('name'), - templateName=contract.get('template'), - schemaId=contract.get('schema_id'), - ), - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=contract_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=contracts_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py deleted file mode 100644 index d7307f54c91..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_anp_epg_subnet -short_description: Manage EPG subnets in schema templates -description: -- Manage EPG subnets in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template to change. - type: str - required: yes - anp: - description: - - The name of the ANP. - type: str - required: yes - epg: - description: - - The name of the EPG to manage. - type: str - required: yes - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - aliases: [ ip ] - description: - description: - - The description of this subnet. - type: str - scope: - description: - - The scope of the subnet. - type: str - choices: [ private, public ] - shared: - description: - - Whether this subnet is shared between VRFs. - type: bool - no_default_gateway: - description: - - Whether this subnet has a default gateway. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- Due to restrictions of the MSO REST API concurrent modifications to EPG subnets can be dangerous and corrupt data. -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new subnet to an EPG - mso_schema_template_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - subnet: 10.0.0.0/24 - state: present - delegate_to: localhost - -- name: Remove a subnet from an EPG - mso_schema_template_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - subnet: 10.0.0.0/24 - state: absent - delegate_to: localhost - -- name: Query a specific EPG subnet - mso_schema_template_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - epg: EPG 1 - subnet: 10.0.0.0/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all EPGs subnets - mso_schema_template_anp_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - anp: ANP 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, mso_subnet_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - anp=dict(type='str', required=True), - epg=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - argument_spec.update(mso_subnet_spec()) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['subnet']], - ['state', 'present', ['subnet']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - anp = module.params.get('anp') - epg = module.params.get('epg') - subnet = module.params.get('subnet') - description = module.params.get('description') - scope = module.params.get('scope') - shared = module.params.get('shared') - no_default_gateway = module.params.get('no_default_gateway') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{template}' does not exist. Existing templates: {templates}".format(template=template, - templates=', '.join(templates))) - template_idx = templates.index(template) - - # Get ANP - anps = [a.get('name') for a in schema_obj.get('templates')[template_idx]['anps']] - if anp not in anps: - mso.fail_json(msg="Provided anp '{anp}' does not exist. Existing anps: {anps}".format(anp=anp, anps=', '.join(anps))) - anp_idx = anps.index(anp) - - # Get EPG - epgs = [e.get('name') for e in schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs']] - if epg not in epgs: - mso.fail_json(msg="Provided epg '{epg}' does not exist. Existing epgs: {epgs}".format(epg=epg, epgs=', '.join(epgs))) - epg_idx = epgs.index(epg) - - # Get Subnet - subnets = [s.get('ip') for s in schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets']] - if subnet in subnets: - subnet_idx = subnets.index(subnet) - # FIXME: Changes based on index are DANGEROUS - subnet_path = '/templates/{0}/anps/{1}/epgs/{2}/subnets/{3}'.format(template, anp, epg, subnet_idx) - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets'][subnet_idx] - - if state == 'query': - if subnet is None: - mso.existing = schema_obj.get('templates')[template_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets'] - elif not mso.existing: - mso.fail_json(msg="Subnet '{subnet}' not found".format(subnet=subnet)) - mso.exit_json() - - subnets_path = '/templates/{0}/anps/{1}/epgs/{2}/subnets'.format(template, anp, epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.existing = {} - ops.append(dict(op='remove', path=subnet_path)) - - elif state == 'present': - if not mso.existing: - if description is None: - description = subnet - if scope is None: - scope = 'private' - if shared is None: - shared = False - if no_default_gateway is None: - no_default_gateway = False - - payload = dict( - ip=subnet, - description=description, - scope=scope, - shared=shared, - noDefaultGateway=no_default_gateway, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_bd.py b/lib/ansible/modules/network/aci/mso_schema_template_bd.py deleted file mode 100644 index 0673d902e85..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_bd.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_bd -short_description: Manage Bridge Domains (BDs) in schema templates -description: -- Manage BDs in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - bd: - description: - - The name of the BD to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - vrf: - description: - - The VRF associated to this BD. This is required only when creating a new BD. - type: dict - suboptions: - name: - description: - - The name of the VRF to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current template. - type: str - subnets: - description: - - The subnets associated to this BD. - type: list - suboptions: - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - aliases: [ ip ] - description: - description: - - The description of this subnet. - type: str - scope: - description: - - The scope of the subnet. - type: str - choices: [ private, public ] - shared: - description: - - Whether this subnet is shared between VRFs. - type: bool - no_default_gateway: - description: - - Whether this subnet has a default gateway. - type: bool - intersite_bum_traffic: - description: - - Whether to allow intersite BUM traffic. - type: bool - optimize_wan_bandwidth: - description: - - Whether to optimize WAN bandwidth. - type: bool - layer2_stretch: - description: - - Whether to enable L2 stretch. - type: bool - layer2_unknown_unicast: - description: - - Layer2 unknown unicast. - type: str - choices: [ flood, proxy ] - layer3_multicast: - description: - - Whether to enable L3 multicast. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new BD - mso_schema_template_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD 1 - vrf: - name: VRF1 - state: present - delegate_to: localhost - -- name: Remove an BD - mso_schema_template_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD1 - state: absent - delegate_to: localhost - -- name: Query a specific BDs - mso_schema_template_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all BDs - mso_schema_template_bd: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, mso_subnet_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - bd=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - display_name=dict(type='str'), - intersite_bum_traffic=dict(type='bool'), - optimize_wan_bandwidth=dict(type='bool'), - layer2_stretch=dict(type='bool'), - layer2_unknown_unicast=dict(type='str', choices=['flood', 'proxy']), - layer3_multicast=dict(type='bool'), - vrf=dict(type='dict', options=mso_reference_spec()), - subnets=dict(type='list', options=mso_subnet_spec()), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['bd']], - ['state', 'present', ['bd', 'vrf']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - bd = module.params.get('bd') - display_name = module.params.get('display_name') - intersite_bum_traffic = module.params.get('intersite_bum_traffic') - optimize_wan_bandwidth = module.params.get('optimize_wan_bandwidth') - layer2_stretch = module.params.get('layer2_stretch') - layer2_unknown_unicast = module.params.get('layer2_unknown_unicast') - layer3_multicast = module.params.get('layer3_multicast') - vrf = module.params.get('vrf') - subnets = module.params.get('subnets') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj.get('id') - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get ANP - bds = [b.get('name') for b in schema_obj.get('templates')[template_idx]['bds']] - - if bd is not None and bd in bds: - bd_idx = bds.index(bd) - mso.existing = schema_obj.get('templates')[template_idx]['bds'][bd_idx] - - if state == 'query': - if bd is None: - mso.existing = schema_obj.get('templates')[template_idx]['bds'] - elif not mso.existing: - mso.fail_json(msg="BD '{bd}' not found".format(bd=bd)) - mso.exit_json() - - bds_path = '/templates/{0}/bds'.format(template) - bd_path = '/templates/{0}/bds/{1}'.format(template, bd) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=bd_path)) - - elif state == 'present': - vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) - subnets = mso.make_subnets(subnets) - - if display_name is None and not mso.existing: - display_name = bd - if subnets is None and not mso.existing: - subnets = [] - - payload = dict( - name=bd, - displayName=display_name, - intersiteBumTrafficAllow=intersite_bum_traffic, - optimizeWanBandwidth=optimize_wan_bandwidth, - l2UnknownUnicast=layer2_unknown_unicast, - l2Stretch=layer2_stretch, - l3MCast=layer3_multicast, - subnets=subnets, - vrfRef=vrf_ref, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=bd_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=bds_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py b/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py deleted file mode 100644 index f8ec751e52b..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_bd_subnet -short_description: Manage BD subnets in schema templates -description: -- Manage BD subnets in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template to change. - type: str - required: yes - bd: - description: - - The name of the BD to manage. - type: str - required: yes - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - aliases: [ ip ] - description: - description: - - The description of this subnet. - type: str - scope: - description: - - The scope of the subnet. - type: str - choices: [ private, public ] - shared: - description: - - Whether this subnet is shared between VRFs. - type: bool - no_default_gateway: - description: - - Whether this subnet has a default gateway. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- Due to restrictions of the MSO REST API concurrent modifications to BD subnets can be dangerous and corrupt data. -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new subnet to a BD - mso_schema_template_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD 1 - subnet: 10.0.0.0/24 - state: present - delegate_to: localhost - -- name: Remove a subset from a BD - mso_schema_template_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD 1 - subnet: 10.0.0.0/24 - state: absent - delegate_to: localhost - -- name: Query a specific BD subnet - mso_schema_template_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD 1 - subnet: 10.0.0.0/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all BD subnets - mso_schema_template_bd_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - bd: BD 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, mso_subnet_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - bd=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - argument_spec.update(mso_subnet_spec()) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['subnet']], - ['state', 'present', ['subnet']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - bd = module.params.get('bd') - subnet = module.params.get('subnet') - description = module.params.get('description') - scope = module.params.get('scope') - shared = module.params.get('shared') - no_default_gateway = module.params.get('no_default_gateway') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get BD - bds = [b.get('name') for b in schema_obj.get('templates')[template_idx]['bds']] - if bd not in bds: - mso.fail_json(msg="Provided BD '{0}' does not exist. Existing BDs: {1}".format(bd, ', '.join(bds))) - bd_idx = bds.index(bd) - - # Get Subnet - subnets = [s.get('ip') for s in schema_obj.get('templates')[template_idx]['bds'][bd_idx]['subnets']] - if subnet in subnets: - subnet_idx = subnets.index(subnet) - # FIXME: Changes based on index are DANGEROUS - subnet_path = '/templates/{0}/bds/{1}/subnets/{2}'.format(template, bd, subnet_idx) - mso.existing = schema_obj.get('templates')[template_idx]['bds'][bd_idx]['subnets'][subnet_idx] - - if state == 'query': - if subnet is None: - mso.existing = schema_obj.get('templates')[template_idx]['bds'][bd_idx]['subnets'] - elif not mso.existing: - mso.fail_json(msg="Subnet IP '{subnet}' not found".format(subnet=subnet)) - mso.exit_json() - - subnets_path = '/templates/{0}/bds/{1}/subnets'.format(template, bd) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=subnet_path)) - - elif state == 'present': - if not mso.existing: - if description is None: - description = subnet - if scope is None: - scope = 'private' - if shared is None: - shared = False - if no_default_gateway is None: - no_default_gateway = False - - payload = dict( - ip=subnet, - description=description, - scope=scope, - shared=shared, - noDefaultGateway=no_default_gateway, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py b/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py deleted file mode 100644 index e548fd53a1c..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py +++ /dev/null @@ -1,340 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_contract_filter -short_description: Manage contract filters in schema templates -description: -- Manage contract filters in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - contract: - description: - - The name of the contract to manage. - type: str - required: yes - contract_display_name: - description: - - The name as displayed on the MSO web interface. - - This defaults to the contract name when unset on creation. - type: str - contract_filter_type: - description: - - The type of filters defined in this contract. - - This defaults to C(both-way) when unset on creation. - type: str - choices: [ both-way, one-way ] - contract_scope: - description: - - The scope of the contract. - - This defaults to C(vrf) when unset on creation. - type: str - choices: [ application-profile, global, tenant, vrf ] - filter: - description: - - The filter to associate with this contract. - type: str - aliases: [ name ] - filter_template: - description: - - The template name in which the filter is located. - type: str - filter_schema: - description: - - The schema name in which the filter is located. - type: str - filter_type: - description: - - The type of filter to manage. - type: str - choices: [ both-way, consumer-to-provider, provider-to-consumer ] - default: both-way - aliases: [ type ] - filter_directives: - description: - - A list of filter directives. - type: list - choices: [ log, none ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_template_filter_entry -notes: -- Due to restrictions of the MSO REST API this module creates contracts when needed, and removes them when the last filter has been removed. -- Due to restrictions of the MSO REST API concurrent modifications to contract filters can be dangerous and corrupt data. -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new contract filter - mso_schema_template_contract_filter: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - contract: Contract 1 - contract_scope: global - filter: Filter 1 - state: present - delegate_to: localhost - -- name: Remove a contract filter - mso_schema_template_contract_filter: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - contract: Contract 1 - filter: Filter 1 - state: absent - delegate_to: localhost - -- name: Query a specific contract filter - mso_schema_template_contract_filter: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - contract: Contract 1 - filter: Filter 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all contract filters - mso_schema_template_contract_filter: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - contract: Contract 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, issubset - -FILTER_KEYS = { - 'both-way': 'filterRelationships', - 'consumer-to-provider': 'filterRelationshipsConsumerToProvider', - 'provider-to-consumer': 'filterRelationshipsProviderToConsumer', -} - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - contract=dict(type='str', required=True), - contract_display_name=dict(type='str'), - contract_scope=dict(type='str', choices=['application-profile', 'global', 'tenant', 'vrf']), - contract_filter_type=dict(type='str', choices=['both-way', 'one-way']), - filter=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - filter_directives=dict(type='list', choices=['log', 'none']), - filter_template=dict(type='str'), - filter_schema=dict(type='str'), - filter_type=dict(type='str', default='both-way', choices=FILTER_KEYS.keys(), aliases=['type']), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['filter']], - ['state', 'present', ['filter']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - contract = module.params.get('contract') - contract_display_name = module.params.get('contract_display_name') - contract_filter_type = module.params.get('contract_filter_type') - contract_scope = module.params.get('contract_scope') - filter_name = module.params.get('filter') - filter_directives = module.params.get('filter_directives') - filter_template = module.params.get('filter_template') - filter_schema = module.params.get('filter_schema') - filter_type = module.params.get('filter_type') - state = module.params.get('state') - - contract_ftype = 'bothWay' if contract_filter_type == 'both-way' else 'oneWay' - - if contract_filter_type == 'both-way' and filter_type != 'both-way': - module.warn("You are adding 'one-way' filters to a 'both-way' contract") - elif contract_filter_type != 'both-way' and filter_type == 'both-way': - module.warn("You are adding 'both-way' filters to a 'one-way' contract") - - if filter_template is None: - filter_template = template - - if filter_schema is None: - filter_schema = schema - - filter_key = FILTER_KEYS[filter_type] - - mso = MSOModule(module) - - filter_schema_id = mso.lookup_schema(filter_schema) - - # Get schema object - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj.get('id') - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get contracts - mso.existing = {} - contract_idx = None - filter_idx = None - contracts = [c.get('name') for c in schema_obj.get('templates')[template_idx]['contracts']] - - if contract in contracts: - contract_idx = contracts.index(contract) - - filters = [f.get('filterRef') for f in schema_obj.get('templates')[template_idx]['contracts'][contract_idx][filter_key]] - filter_ref = mso.filter_ref(schema_id=filter_schema_id, template=filter_template, filter=filter_name) - if filter_ref in filters: - filter_idx = filters.index(filter_ref) - filter_path = '/templates/{0}/contracts/{1}/{2}/{3}'.format(template, contract, filter_key, filter_name) - mso.existing = schema_obj.get('templates')[template_idx]['contracts'][contract_idx][filter_key][filter_idx] - - if state == 'query': - if contract_idx is None: - mso.fail_json(msg="Provided contract '{0}' does not exist. Existing contracts: {1}".format(contract, ', '.join(contracts))) - - if filter_name is None: - mso.existing = schema_obj.get('templates')[template_idx]['contracts'][contract_idx][filter_key] - elif not mso.existing: - mso.fail_json(msg="FilterRef '{filter_ref}' not found".format(filter_ref=filter_ref)) - mso.exit_json() - - ops = [] - contract_path = '/templates/{0}/contracts/{1}'.format(template, contract) - filters_path = '/templates/{0}/contracts/{1}/{2}'.format(template, contract, filter_key) - - mso.previous = mso.existing - if state == 'absent': - mso.proposed = mso.sent = {} - - if contract_idx is None: - # There was no contract to begin with - pass - elif filter_idx is None: - # There was no filter to begin with - pass - elif len(filters) == 1: - # There is only one filter, remove contract - mso.existing = {} - ops.append(dict(op='remove', path=contract_path)) - else: - # Remove filter - mso.existing = {} - ops.append(dict(op='remove', path=filter_path)) - - elif state == 'present': - if filter_directives is None: - filter_directives = ['none'] - - payload = dict( - filterRef=dict( - filterName=filter_name, - templateName=filter_template, - schemaId=filter_schema_id, - ), - directives=filter_directives, - ) - - mso.sanitize(payload, collate=True) - mso.existing = mso.sent - - if contract_idx is None: - # Contract does not exist, so we have to create it - if contract_display_name is None: - contract_display_name = contract - if contract_filter_type is None: - contract_ftype = 'bothWay' - if contract_scope is None: - contract_scope = 'context' - - payload = { - 'name': contract, - 'displayName': contract_display_name, - 'filterType': contract_ftype, - 'scope': contract_scope, - } - - ops.append(dict(op='add', path='/templates/{0}/contracts/-'.format(template), value=payload)) - else: - # Contract exists, but may require an update - if contract_display_name is not None: - ops.append(dict(op='replace', path=contract_path + '/displayName', value=contract_display_name)) - if contract_filter_type is not None: - ops.append(dict(op='replace', path=contract_path + '/filterType', value=contract_ftype)) - if contract_scope is not None: - ops.append(dict(op='replace', path=contract_path + '/scope', value=contract_scope)) - - if filter_idx is None: - # Filter does not exist, so we have to add it - ops.append(dict(op='add', path=filters_path + '/-', value=mso.sent)) - - else: - # Filter exists, we have to update it - ops.append(dict(op='replace', path=filter_path, value=mso.sent)) - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_deploy.py b/lib/ansible/modules/network/aci/mso_schema_template_deploy.py deleted file mode 100644 index 17375f2eabe..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_deploy.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_deploy -short_description: Deploy schema templates to sites -description: -- Deploy schema templates to sites. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - aliases: [ name ] - site: - description: - - The name of the site B(to undeploy). - type: str - state: - description: - - Use C(deploy) to deploy schema template. - - Use C(status) to get deployment status. - - Use C(undeploy) to deploy schema template from a site. - type: str - choices: [ deploy, status, undeploy ] - default: deploy -seealso: -- module: mso_schema_site -- module: mso_schema_template -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Deploy a schema template - mso_schema_template_deploy: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: deploy - delegate_to: localhost - -- name: Undeploy a schema template - mso_schema_template_deploy: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - site: Site 1 - state: undeploy - delegate_to: localhost - -- name: Get deployment status - mso_schema: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: status - delegate_to: localhost - register: status_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True, aliases=['name']), - site=dict(type='str'), - state=dict(type='str', default='deploy', choices=['deploy', 'status', 'undeploy']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'undeploy', ['site']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - site = module.params.get('site') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema - schema_id = mso.lookup_schema(schema) - - payload = dict( - schemaId=schema_id, - templateName=template, - ) - - qs = None - if state == 'deploy': - path = 'execute/schema/{0}/template/{1}'.format(schema_id, template) - elif state == 'status': - path = 'status/schema/{0}/template/{1}'.format(schema_id, template) - elif state == 'undeploy': - path = 'execute/schema/{0}/template/{1}'.format(schema_id, template) - site_id = mso.lookup_site(site) - qs = dict(undeploy=site_id) - - if not module.check_mode: - status = mso.request(path, method='GET', data=payload, qs=qs) - - mso.exit_json(**status) - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py b/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py deleted file mode 100644 index 3aa41da35b8..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_externalepg -short_description: Manage external EPGs in schema templates -description: -- Manage external EPGs in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - externalepg: - description: - - The name of the external EPG to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - vrf: - description: - - The VRF associated to this ANP. - type: dict - suboptions: - name: - description: - - The name of the VRF to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current template. - type: str - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new external EPG - mso_schema_template_externalepg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - externalepg: External EPG 1 - state: present - delegate_to: localhost - -- name: Remove an external EPG - mso_schema_template_externalepg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - externalepg: external EPG1 - state: absent - delegate_to: localhost - -- name: Query a specific external EPGs - mso_schema_template_externalepg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - externalepg: external EPG1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all external EPGs - mso_schema_template_externalepg: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - externalepg=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - display_name=dict(type='str'), - vrf=dict(type='dict', options=mso_reference_spec()), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['externalepg']], - ['state', 'present', ['externalepg', 'vrf']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - externalepg = module.params.get('externalepg') - display_name = module.params.get('display_name') - vrf = module.params.get('vrf') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj.get('id') - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get external EPGs - externalepgs = [e.get('name') for e in schema_obj.get('templates')[template_idx]['externalEpgs']] - - if externalepg is not None and externalepg in externalepgs: - externalepg_idx = externalepgs.index(externalepg) - mso.existing = schema_obj.get('templates')[template_idx]['externalEpgs'][externalepg_idx] - - if state == 'query': - if externalepg is None: - mso.existing = schema_obj.get('templates')[template_idx]['externalEpgs'] - elif not mso.existing: - mso.fail_json(msg="External EPG '{externalepg}' not found".format(externalepg=externalepg)) - mso.exit_json() - - eepgs_path = '/templates/{0}/externalEpgs'.format(template) - eepg_path = '/templates/{0}/externalEpgs/{1}'.format(template, externalepg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=eepg_path)) - - elif state == 'present': - vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) - - if display_name is None and not mso.existing: - display_name = externalepg - - payload = dict( - name=externalepg, - displayName=display_name, - vrfRef=vrf_ref, - # FIXME - subnets=[], - contractRelationships=[], - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=eepg_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=eepgs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py b/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py deleted file mode 100644 index f49e54e7ab7..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py +++ /dev/null @@ -1,363 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_filter_entry -short_description: Manage filter entries in schema templates -description: -- Manage filter entries in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - filter: - description: - - The name of the filter to manage. - type: str - required: yes - filter_display_name: - description: - - The name as displayed on the MSO web interface. - type: str - entry: - description: - - The filter entry name to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - aliases: [ entry_display_name ] - description: - description: - - The description of this filer entry. - type: str - aliases: [ entry_description ] - ethertype: - description: - - The ethernet type to use for this filter entry. - type: str - choices: [ arp, fcoe, ip, ipv4, ipv6, mac-security, mpls-unicast, trill, unspecified ] - ip_protocol: - description: - - The IP protocol to use for this filter entry. - type: str - choices: [ eigrp, egp, icmp, icmpv6, igmp, igp, l2tp, ospfigp, pim, tcp, udp, unspecified ] - tcp_session_rules: - description: - - A list of TCP session rules. - type: list - choices: [ acknowledgement, established, finish, synchronize, reset, unspecified ] - source_from: - description: - - The source port range from. - type: str - source_to: - description: - - The source port range to. - type: str - destination_from: - description: - - The destination port range from. - type: str - destination_to: - description: - - The destination port range to. - type: str - arp_flag: - description: - - The ARP flag to use for this filter entry. - type: str - choices: [ reply, request, unspecified ] - stateful: - description: - - Whether this filter entry is stateful. - type: bool - default: no - fragments_only: - description: - - Whether this filter entry only matches fragments. - type: bool - default: no - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: mso_schema_template_contract_filter -notes: -- Due to restrictions of the MSO REST API this module creates filters when needed, and removes them when the last entry has been removed. -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new filter entry - mso_schema_template_filter_entry: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - filter: Filter 1 - state: present - delegate_to: localhost - -- name: Remove a filter entry - mso_schema_template_filter_entry: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - filter: Filter 1 - state: absent - delegate_to: localhost - -- name: Query a specific filter entry - mso_schema_template_filter_entry: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - filter: Filter 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all filter entries - mso_schema_template_filter_entry: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - filter=dict(type='str', required=True), - filter_display_name=dict(type='str'), - entry=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - description=dict(type='str', aliases=['entry_description']), - display_name=dict(type='str', aliases=['entry_display_name']), - ethertype=dict(type='str', choices=['arp', 'fcoe', 'ip', 'ipv4', 'ipv6', 'mac-security', 'mpls-unicast', 'trill', 'unspecified']), - ip_protocol=dict(type='str', choices=['eigrp', 'egp', 'icmp', 'icmpv6', 'igmp', 'igp', 'l2tp', 'ospfigp', 'pim', 'tcp', 'udp', 'unspecified']), - tcp_session_rules=dict(type='list', choices=['acknowledgement', 'established', 'finish', 'synchronize', 'reset', 'unspecified']), - source_from=dict(type='str'), - source_to=dict(type='str'), - destination_from=dict(type='str'), - destination_to=dict(type='str'), - arp_flag=dict(type='str', choices=['reply', 'request', 'unspecified']), - stateful=dict(type='bool'), - fragments_only=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['entry']], - ['state', 'present', ['entry']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - filter_name = module.params.get('filter') - filter_display_name = module.params.get('filter_display_name') - entry = module.params.get('entry') - display_name = module.params.get('display_name') - description = module.params.get('description') - ethertype = module.params.get('ethertype') - ip_protocol = module.params.get('ip_protocol') - tcp_session_rules = module.params.get('tcp_session_rules') - source_from = module.params.get('source_from') - source_to = module.params.get('source_to') - destination_from = module.params.get('destination_from') - destination_to = module.params.get('destination_to') - arp_flag = module.params.get('arp_flag') - stateful = module.params.get('stateful') - fragments_only = module.params.get('fragments_only') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{template}' does not exist. Existing templates: {templates}".format(template=template, - templates=', '.join(templates))) - template_idx = templates.index(template) - - # Get filters - mso.existing = {} - filter_idx = None - entry_idx = None - filters = [f.get('name') for f in schema_obj.get('templates')[template_idx]['filters']] - if filter_name in filters: - filter_idx = filters.index(filter_name) - - entries = [f.get('name') for f in schema_obj.get('templates')[template_idx]['filters'][filter_idx]['entries']] - if entry in entries: - entry_idx = entries.index(entry) - mso.existing = schema_obj.get('templates')[template_idx]['filters'][filter_idx]['entries'][entry_idx] - - if state == 'query': - if entry is None: - if filter_idx is None: - mso.fail_json(msg="Filter '{filter}' not found".format(filter=filter_name)) - mso.existing = schema_obj.get('templates')[template_idx]['filters'][filter_idx]['entries'] - elif not mso.existing: - mso.fail_json(msg="Entry '{entry}' not found".format(entry=entry)) - mso.exit_json() - - filters_path = '/templates/{0}/filters'.format(template) - filter_path = '/templates/{0}/filters/{1}'.format(template, filter_name) - entries_path = '/templates/{0}/filters/{1}/entries'.format(template, filter_name) - entry_path = '/templates/{0}/filters/{1}/entries/{2}'.format(template, filter_name, entry) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - mso.proposed = mso.sent = {} - - if filter_idx is None: - # There was no filter to begin with - pass - elif entry_idx is None: - # There was no entry to begin with - pass - elif len(entries) == 1: - # There is only one entry, remove filter - mso.existing = {} - ops.append(dict(op='remove', path=filter_path)) - - else: - mso.existing = {} - ops.append(dict(op='remove', path=entry_path)) - - elif state == 'present': - - if not mso.existing: - if display_name is None: - display_name = entry - if description is None: - description = '' - if ethertype is None: - ethertype = 'unspecified' - if ip_protocol is None: - ip_protocol = 'unspecified' - if tcp_session_rules is None: - tcp_session_rules = ['unspecified'] - if source_from is None: - source_from = 'unspecified' - if source_to is None: - source_to = 'unspecified' - if destination_from is None: - destination_from = 'unspecified' - if destination_to is None: - destination_to = 'unspecified' - if arp_flag is None: - arp_flag = 'unspecified' - if stateful is None: - stateful = False - if fragments_only is None: - fragments_only = False - - payload = dict( - name=entry, - displayName=display_name, - description=description, - etherType=ethertype, - ipProtocol=ip_protocol, - tcpSessionRules=tcp_session_rules, - sourceFrom=source_from, - sourceTo=source_to, - destinationFrom=destination_from, - destinationTo=destination_to, - arpFlag=arp_flag, - stateful=stateful, - matchOnlyFragments=fragments_only, - ) - - mso.sanitize(payload, collate=True) - - if filter_idx is None: - # Filter does not exist, so we have to create it - if filter_display_name is None: - filter_display_name = filter_name - - payload = dict( - name=filter_name, - displayName=filter_display_name, - entries=[mso.sent], - ) - - ops.append(dict(op='add', path=filters_path + '/-', value=payload)) - - elif entry_idx is None: - # Entry does not exist, so we have to add it - ops.append(dict(op='add', path=entries_path + '/-', value=mso.sent)) - - else: - # Entry exists, we have to update it - for (key, value) in mso.sent.items(): - ops.append(dict(op='replace', path=entry_path + '/' + key, value=value)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_l3out.py b/lib/ansible/modules/network/aci/mso_schema_template_l3out.py deleted file mode 100644 index 6bdeff982f3..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_l3out.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_l3out -short_description: Manage l3outs in schema templates -description: -- Manage l3outs in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - l3out: - description: - - The name of the l3out to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - vrf: - description: - - The VRF associated to this L3out. - type: dict - suboptions: - name: - description: - - The name of the VRF to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced VRF. - - If this parameter is unspecified, it defaults to the current schema. - type: str - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new L3out - mso_schema_template_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - l3out: L3out 1 - state: present - delegate_to: localhost - -- name: Remove an L3out - mso_schema_template_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - l3out: L3out 1 - state: absent - delegate_to: localhost - -- name: Query a specific L3outs - mso_schema_template_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - l3out: L3out 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all L3outs - mso_schema_template_l3out: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - l3out=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - display_name=dict(type='str'), - vrf=dict(type='dict', options=mso_reference_spec()), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['l3out']], - ['state', 'present', ['l3out', 'vrf']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - l3out = module.params.get('l3out') - display_name = module.params.get('display_name') - vrf = module.params.get('vrf') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj.get('id') - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get L3out - l3outs = [l.get('name') for l in schema_obj.get('templates')[template_idx]['intersiteL3outs']] - - if l3out is not None and l3out in l3outs: - l3out_idx = l3outs.index(l3out) - mso.existing = schema_obj.get('templates')[template_idx]['intersiteL3outs'][l3out_idx] - - if state == 'query': - if l3out is None: - mso.existing = schema_obj.get('templates')[template_idx]['intersiteL3outs'] - elif not mso.existing: - mso.fail_json(msg="L3out '{l3out}' not found".format(l3out=l3out)) - mso.exit_json() - - l3outs_path = '/templates/{0}/intersiteL3outs'.format(template) - l3out_path = '/templates/{0}/intersiteL3outs/{1}'.format(template, l3out) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=l3out_path)) - - elif state == 'present': - vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) - - if display_name is None and not mso.existing: - display_name = l3out - - payload = dict( - name=l3out, - displayName=display_name, - vrfRef=vrf_ref, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=l3out_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=l3outs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_vrf.py b/lib/ansible/modules/network/aci/mso_schema_template_vrf.py deleted file mode 100644 index 9f8ac4373be..00000000000 --- a/lib/ansible/modules/network/aci/mso_schema_template_vrf.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_schema_template_vrf -short_description: Manage VRFs in schema templates -description: -- Manage VRFs in schema templates on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template. - type: str - required: yes - vrf: - description: - - The name of the VRF to manage. - type: str - aliases: [ name ] - display_name: - description: - - The name as displayed on the MSO web interface. - type: str - layer3_multicast: - description: - - Whether to enable L3 multicast. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new VRF - mso_schema_template_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - vrf: VRF 1 - state: present - delegate_to: localhost - -- name: Remove an VRF - mso_schema_template_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - vrf: VRF1 - state: absent - delegate_to: localhost - -- name: Query a specific VRFs - mso_schema_template_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - vrf: VRF1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all VRFs - mso_schema_template_vrf: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_reference_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - vrf=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects - display_name=dict(type='str'), - layer3_multicast=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['vrf']], - ['state', 'present', ['vrf']], - ], - ) - - schema = module.params.get('schema') - template = module.params.get('template') - vrf = module.params.get('vrf') - display_name = module.params.get('display_name') - layer3_multicast = module.params.get('layer3_multicast') - state = module.params.get('state') - - mso = MSOModule(module) - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t.get('name') for t in schema_obj.get('templates')] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get ANP - vrfs = [v.get('name') for v in schema_obj.get('templates')[template_idx]['vrfs']] - - if vrf is not None and vrf in vrfs: - vrf_idx = vrfs.index(vrf) - mso.existing = schema_obj.get('templates')[template_idx]['vrfs'][vrf_idx] - - if state == 'query': - if vrf is None: - mso.existing = schema_obj.get('templates')[template_idx]['vrfs'] - elif not mso.existing: - mso.fail_json(msg="VRF '{vrf}' not found".format(vrf=vrf)) - mso.exit_json() - - vrfs_path = '/templates/{0}/vrfs'.format(template) - vrf_path = '/templates/{0}/vrfs/{1}'.format(template, vrf) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=vrf_path)) - - elif state == 'present': - if display_name is None and not mso.existing: - display_name = vrf - - payload = dict( - name=vrf, - displayName=display_name, - l3MCast=layer3_multicast, - # FIXME - regions=[], - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=vrf_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=vrfs_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_site.py b/lib/ansible/modules/network/aci/mso_site.py deleted file mode 100644 index 64dd43fc815..00000000000 --- a/lib/ansible/modules/network/aci/mso_site.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_site -short_description: Manage sites -description: -- Manage sites on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - apic_password: - description: - - The password for the APICs. - type: str - required: yes - apic_site_id: - description: - - The site ID of the APICs. - type: str - required: yes - apic_username: - description: - - The username for the APICs. - type: str - required: yes - default: admin - site: - description: - - The name of the site. - type: str - required: yes - aliases: [ name ] - labels: - description: - - The labels for this site. - - Labels that do not already exist will be automatically created. - type: list - location: - description: - - Location of the site. - type: dict - suboptions: - latitude: - description: - - The latitude of the location of the site. - type: float - longitude: - description: - - The longitude of the location of the site. - type: float - urls: - description: - - A list of URLs to reference the APICs. - type: list - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new site - mso_site: - host: mso_host - username: admin - password: SomeSecretPassword - site: north_europe - description: North European Datacenter - apic_username: mso_admin - apic_password: AnotherSecretPassword - apic_site_id: 12 - urls: - - 10.2.3.4 - - 10.2.4.5 - - 10.3.5.6 - labels: - - NEDC - - Europe - - Diegem - location: - latitude: 50.887318 - longitude: 4.447084 - state: present - delegate_to: localhost - -- name: Remove a site - mso_site: - host: mso_host - username: admin - password: SomeSecretPassword - site: north_europe - state: absent - delegate_to: localhost - -- name: Query a site - mso_site: - host: mso_host - username: admin - password: SomeSecretPassword - site: north_europe - state: query - delegate_to: localhost - register: query_result - -- name: Query all sites - mso_site: - host: mso_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.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - location_arg_spec = dict( - latitude=dict(type='float'), - longitude=dict(type='float'), - ) - - argument_spec = mso_argument_spec() - argument_spec.update( - apic_password=dict(type='str', no_log=True), - apic_site_id=dict(type='str'), - apic_username=dict(type='str', default='admin'), - labels=dict(type='list'), - location=dict(type='dict', options=location_arg_spec), - site=dict(type='str', aliases=['name']), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - urls=dict(type='list'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['site']], - ['state', 'present', ['apic_site_id', 'site']], - ], - ) - - apic_username = module.params.get('apic_username') - apic_password = module.params.get('apic_password') - apic_site_id = module.params.get('apic_site_id') - site = module.params.get('site') - location = module.params.get('location') - if location is not None: - latitude = module.params.get('location')['latitude'] - longitude = module.params.get('location')['longitude'] - state = module.params.get('state') - urls = module.params.get('urls') - - mso = MSOModule(module) - - site_id = None - path = 'sites' - - # Convert labels - labels = mso.lookup_labels(module.params.get('labels'), 'site') - - # Query for mso.existing object(s) - if site: - mso.existing = mso.get_obj(path, name=site) - if mso.existing: - site_id = mso.existing.get('id') - # If we found an existing object, continue with it - path = 'sites/{id}'.format(id=site_id) - else: - mso.existing = mso.query_objs(path) - - if state == 'query': - pass - - elif state == 'absent': - mso.previous = mso.existing - if mso.existing: - if module.check_mode: - mso.existing = {} - else: - mso.existing = mso.request(path, method='DELETE', qs=dict(force='true')) - - elif state == 'present': - mso.previous = mso.existing - - payload = dict( - apicSiteId=apic_site_id, - id=site_id, - name=site, - urls=urls, - labels=labels, - username=apic_username, - password=apic_password, - ) - - if location is not None: - payload['location'] = dict( - lat=latitude, - long=longitude, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - if not issubset(mso.sent, mso.existing): - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='PUT', data=mso.sent) - else: - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='POST', data=mso.sent) - - if 'password' in mso.existing: - mso.existing['password'] = '******' - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_tenant.py b/lib/ansible/modules/network/aci/mso_tenant.py deleted file mode 100644 index 92db65ce85d..00000000000 --- a/lib/ansible/modules/network/aci/mso_tenant.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_tenant -short_description: Manage tenants -description: -- Manage tenants on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - tenant: - description: - - The name of the tenant. - type: str - required: yes - aliases: [ name ] - display_name: - description: - - The name of the tenant to be displayed in the web UI. - type: str - required: yes - description: - description: - - The description for this tenant. - type: str - users: - description: - - A list of associated users for this tenant. - - Using this property will replace any existing associated users. - type: list - sites: - description: - - A list of associated sites for this tenant. - - Using this property will replace any existing associated sites. - type: list - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Add a new tenant - mso_tenant: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: north_europe - display_name: North European Datacenter - description: This tenant manages the NEDC environment. - state: present - delegate_to: localhost - -- name: Remove a tenant - mso_tenant: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: north_europe - state: absent - delegate_to: localhost - -- name: Query a tenant - mso_tenant: - host: mso_host - username: admin - password: SomeSecretPassword - tenant: north_europe - state: query - delegate_to: localhost - register: query_result - -- name: Query all tenants - mso_tenant: - host: mso_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.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - description=dict(type='str'), - display_name=dict(type='str'), - tenant=dict(type='str', aliases=['name']), - users=dict(type='list'), - sites=dict(type='list'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['tenant']], - ['state', 'present', ['tenant']], - ], - ) - - description = module.params.get('description') - display_name = module.params.get('display_name') - tenant = module.params.get('tenant') - state = module.params.get('state') - - mso = MSOModule(module) - - # Convert sites and users - sites = mso.lookup_sites(module.params.get('sites')) - users = mso.lookup_users(module.params.get('users')) - - tenant_id = None - path = 'tenants' - - # Query for existing object(s) - if tenant: - mso.existing = mso.get_obj(path, name=tenant) - if mso.existing: - tenant_id = mso.existing.get('id') - # If we found an existing object, continue with it - path = 'tenants/{id}'.format(id=tenant_id) - else: - mso.existing = mso.query_objs(path) - - if state == 'query': - pass - - elif state == 'absent': - mso.previous = mso.existing - if mso.existing: - if module.check_mode: - mso.existing = {} - else: - mso.existing = mso.request(path, method='DELETE') - - elif state == 'present': - mso.previous = mso.existing - - payload = dict( - description=description, - id=tenant_id, - name=tenant, - displayName=display_name, - siteAssociations=sites, - userAssociations=users, - ) - - mso.sanitize(payload, collate=True) - - # Ensure displayName is not undefined - if mso.sent.get('displayName') is None: - mso.sent['displayName'] = tenant - - # Ensure tenant has at least admin user - if mso.sent.get('userAssociations') is None: - mso.sent['userAssociations'] = [dict(userId="0000ffff0000000000000020")] - - if mso.existing: - if not issubset(mso.sent, mso.existing): - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='PUT', data=mso.sent) - else: - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='POST', data=mso.sent) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/aci/mso_user.py b/lib/ansible/modules/network/aci/mso_user.py deleted file mode 100644 index 5a4621d7ed3..00000000000 --- a/lib/ansible/modules/network/aci/mso_user.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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: mso_user -short_description: Manage users -description: -- Manage users on Cisco ACI Multi-Site. -author: -- Dag Wieers (@dagwieers) -version_added: '2.8' -options: - user: - description: - - The name of the user. - type: str - required: yes - aliases: [ name ] - user_password: - description: - - The password of the user. - type: str - first_name: - description: - - The first name of the user. - - This parameter is required when creating new users. - type: str - last_name: - description: - - The last name of the user. - - This parameter is required when creating new users. - type: str - email: - description: - - The email address of the user. - - This parameter is required when creating new users. - type: str - phone: - description: - - The phone number of the user. - - This parameter is required when creating new users. - type: str - account_status: - description: - - The status of the user account. - type: str - choices: [ active ] - domain: - description: - - The domain this user belongs to. - - When creating new users, this defaults to C(Local). - type: str - roles: - description: - - The roles for this user. - type: list - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- A default installation of ACI Multi-Site ships with admin password 'we1come!' which requires a password change on first login. - See the examples of how to change the 'admin' password using Ansible. -extends_documentation_fragment: mso -''' - -EXAMPLES = r''' -- name: Update initial admin password - mso_user: - host: mso_host - username: admin - password: we1come! - user_name: admin - user_password: SomeSecretPassword - state: present - delegate_to: localhost - -- name: Add a new user - mso_user: - host: mso_host - username: admin - password: SomeSecretPassword - user_name: dag - description: Test user - first_name: Dag - last_name: Wieers - email: dag@wieers.com - phone: +32 478 436 299 - state: present - delegate_to: localhost - -- name: Remove a user - mso_user: - host: mso_host - username: admin - password: SomeSecretPassword - user_name: dag - state: absent - delegate_to: localhost - -- name: Query a user - mso_user: - host: mso_host - username: admin - password: SomeSecretPassword - user_name: dag - state: query - delegate_to: localhost - register: query_result - -- name: Query all users - mso_user: - host: mso_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.mso import MSOModule, mso_argument_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - user=dict(type='str', aliases=['name']), - user_password=dict(type='str', no_log=True), - first_name=dict(type='str'), - last_name=dict(type='str'), - email=dict(type='str'), - phone=dict(type='str'), - # TODO: What possible options do we have ? - account_status=dict(type='str', choices=['active']), - domain=dict(type='str'), - roles=dict(type='list'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['user']], - ['state', 'present', ['user']], - ], - ) - - user_name = module.params.get('user') - user_password = module.params.get('user_password') - first_name = module.params.get('first_name') - last_name = module.params.get('last_name') - email = module.params.get('email') - phone = module.params.get('phone') - account_status = module.params.get('account_status') - state = module.params.get('state') - - mso = MSOModule(module) - - roles = mso.lookup_roles(module.params.get('roles')) - domain = mso.lookup_domain(module.params.get('domain')) - - user_id = None - path = 'users' - - # Query for existing object(s) - if user_name: - mso.existing = mso.get_obj(path, username=user_name) - if mso.existing: - user_id = mso.existing.get('id') - # If we found an existing object, continue with it - path = 'users/{id}'.format(id=user_id) - else: - mso.existing = mso.query_objs(path) - - if state == 'query': - pass - - elif state == 'absent': - mso.previous = mso.existing - if mso.existing: - if module.check_mode: - mso.existing = {} - else: - mso.existing = mso.request(path, method='DELETE') - - elif state == 'present': - mso.previous = mso.existing - - payload = dict( - id=user_id, - username=user_name, - password=user_password, - firstName=first_name, - lastName=last_name, - emailAddress=email, - phoneNumber=phone, - accountStatus=account_status, - domainId=domain, - roles=roles, - # active=True, - # remote=True, - ) - - mso.sanitize(payload, collate=True) - - if mso.sent.get('accountStatus') is None: - mso.sent['accountStatus'] = 'active' - - if mso.existing: - if not issubset(mso.sent, mso.existing): - # NOTE: Since MSO always returns '******' as password, we need to assume a change - if 'password' in mso.proposed: - mso.module.warn("A password change is assumed, as the MSO REST API does not return passwords we do not know.") - mso.result['changed'] = True - - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='PUT', data=mso.sent) - else: - if module.check_mode: - mso.existing = mso.proposed - else: - mso.existing = mso.request(path, method='POST', data=mso.sent) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/plugins/doc_fragments/mso.py b/lib/ansible/plugins/doc_fragments/mso.py deleted file mode 100644 index 6548859a026..00000000000 --- a/lib/ansible/plugins/doc_fragments/mso.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Dag Wieers (@dagwieers) -# 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 - - -class ModuleDocFragment(object): - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - host: - description: - - IP Address or hostname of the ACI Multi Site Orchestrator host. - type: str - required: yes - aliases: [ hostname ] - port: - description: - - Port number to be used for the REST connection. - - The default value depends on parameter `use_ssl`. - type: int - username: - description: - - The username to use for authentication. - type: str - default: admin - password: - description: - - The password to use for authentication. - - This option is mutual exclusive with C(private_key). If C(private_key) is provided too, it will be used instead. - type: str - required: yes - output_level: - description: - - Influence the output of this ACI module. - - C(normal) means the standard output, incl. C(current) dict - - C(info) adds informational output, incl. C(previous), C(proposed) and C(sent) dicts - - C(debug) adds debugging output, incl. C(filter_string), C(method), C(response), C(status) and C(url) information - type: str - choices: [ debug, info, normal ] - default: normal - timeout: - description: - - The socket level timeout in seconds. - type: int - default: 30 - use_proxy: - description: - - If C(no), it will not use a proxy, even if one is defined in an environment variable on the target hosts. - type: bool - default: yes - use_ssl: - description: - - If C(no), an HTTP connection will be used instead of the default HTTPS connection. - type: bool - default: yes - validate_certs: - description: - - If C(no), SSL certificates will not be validated. - - This should only set to C(no) when used on personally controlled sites using self-signed certificates. - type: bool - default: yes -requirements: -- Multi Site Orchestrator v2.1 or newer -notes: -- Please read the :ref:`aci_guide` for more detailed information on how to manage your ACI infrastructure using Ansible. -- This module was written to support ACI Multi Site Orchestrator v2.1 or newer. Some or all functionality may not work on earlier versions. -''' diff --git a/test/integration/targets/mso_label/aliases b/test/integration/targets/mso_label/aliases deleted file mode 100644 index cd28d3c3621..00000000000 --- a/test/integration/targets/mso_label/aliases +++ /dev/null @@ -1,2 +0,0 @@ -# No ACI MultiSite infrastructure, so not enabled -unsupported diff --git a/test/integration/targets/mso_label/tasks/main.yml b/test/integration/targets/mso_label/tasks/main.yml deleted file mode 100644 index 9246ce1fbac..00000000000 --- a/test/integration/targets/mso_label/tasks/main.yml +++ /dev/null @@ -1,283 +0,0 @@ -# Test code for the MSO 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: mso_hostname, mso_username and mso_password.' - when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined - - -# CLEAN ENVIRONMENT -- name: Remove label ansible_test2 - mso_label: &label_absent - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - label: ansible_test2 - state: absent - -- name: Remove label ansible_test - mso_label: - <<: *label_absent - label: ansible_test - register: cm_remove_label - - -# ADD LABEL -- name: Add label (check_mode) - mso_label: &label_present - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_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.displayName == 'ansible_test' - - cm_add_label.current.id is not defined - - cm_add_label.current.type == 'site' - -- name: Add label (normal mode) - mso_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.displayName == 'ansible_test' - - nm_add_label.current.id is defined - - nm_add_label.current.type == 'site' - -- name: Add label again (check_mode) - mso_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.displayName == 'ansible_test' - - cm_add_label_again.current.id == nm_add_label.current.id - - cm_add_label_again.current.type == 'site' - -- name: Add label again (normal mode) - mso_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.displayName == 'ansible_test' - - nm_add_label_again.current.id == nm_add_label.current.id - - nm_add_label_again.current.type == 'site' - - -# CHANGE LABEL -- name: Change label (check_mode) - mso_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.displayName == 'ansible_test2' - - cm_change_label.current.id == nm_add_label.current.id - - cm_change_label.current.type == 'site' - -- name: Change label (normal mode) - mso_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 - - cm_change_label.current.displayName == 'ansible_test2' - - nm_change_label.current.id == nm_add_label.current.id - - nm_change_label.current.type == 'site' - -- name: Change label again (check_mode) - mso_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.displayName == 'ansible_test2' - - cm_change_label_again.current.id == nm_add_label.current.id - - cm_change_label_again.current.type == 'site' - -- name: Change label again (normal mode) - mso_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.displayName == 'ansible_test2' - - nm_change_label_again.current.id == nm_add_label.current.id - - nm_change_label_again.current.type == 'site' - - -# QUERY ALL LABELS -- name: Query all labels (check_mode) - mso_label: &label_query - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - state: query - check_mode: yes - register: cm_query_all_labels - -- name: Query all labels (normal mode) - mso_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 - mso_label: - <<: *label_query - label: ansible_test2 - check_mode: yes - register: cm_query_label - -- name: Query our label - mso_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.displayName == 'ansible_test2' - - cm_query_label.current.id == nm_add_label.current.id - - cm_query_label.current.type == 'site' - - nm_query_label is not changed - - nm_query_label.current.displayName == 'ansible_test2' - - nm_query_label.current.id == nm_add_label.current.id - - nm_query_label.current.type == 'site' - - cm_query_label == nm_query_label - - -# REMOVE LABEL -- name: Remove label (check_mode) - mso_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) - mso_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) - mso_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) - mso_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) - mso_label: - <<: *label_query - label: ansible_test - check_mode: yes - register: cm_query_non_label - -- name: Query non-existing label (normal mode) - mso_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 diff --git a/test/integration/targets/mso_role/aliases b/test/integration/targets/mso_role/aliases deleted file mode 100644 index cd28d3c3621..00000000000 --- a/test/integration/targets/mso_role/aliases +++ /dev/null @@ -1,2 +0,0 @@ -# No ACI MultiSite infrastructure, so not enabled -unsupported diff --git a/test/integration/targets/mso_role/tasks/main.yml b/test/integration/targets/mso_role/tasks/main.yml deleted file mode 100644 index 6f4ed2b0bda..00000000000 --- a/test/integration/targets/mso_role/tasks/main.yml +++ /dev/null @@ -1,289 +0,0 @@ -# Test code for the MSO 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: mso_hostname, mso_username and mso_password.' - when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined - - -# CLEAN ENVIRONMENT -- name: Remove role ansible_test2 - mso_role: &role_absent - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - role: ansible_test2 - state: absent - -- name: Remove role ansible_test - mso_role: - <<: *role_absent - role: ansible_test - register: cm_remove_role - - -# ADD ROLE -- name: Add role (check_mode) - mso_role: &role_present - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - role: ansible_test - description: Ansible test role - permissions: view-sites - state: present - check_mode: yes - register: cm_add_role - -- name: Verify cm_add_role - assert: - that: - - cm_add_role is changed - - cm_add_role.previous == {} - - cm_add_role.current.description == 'Ansible test role' - - cm_add_role.current.displayName == 'ansible_test' - - cm_add_role.current.id is not defined - -- name: Add role (normal mode) - mso_role: *role_present - register: nm_add_role - -- name: nm_Verify add_role - assert: - that: - - nm_add_role is changed - - nm_add_role.previous == {} - - nm_add_role.current.description == 'Ansible test role' - - nm_add_role.current.displayName == 'ansible_test' - - nm_add_role.current.id is defined - -- name: Add role again (check_mode) - mso_role: *role_present - check_mode: yes - register: cm_add_role_again - -- name: Verify cm_add_role_again - assert: - that: - - cm_add_role_again is not changed - - cm_add_role_again.previous.description == 'Ansible test role' - - cm_add_role_again.previous.displayName == 'ansible_test' - - cm_add_role_again.current.description == 'Ansible test role' - - cm_add_role_again.current.displayName == 'ansible_test' - - cm_add_role_again.current.id == nm_add_role.current.id - -- name: Add role again (normal mode) - mso_role: *role_present - register: nm_add_role_again - -- name: Verify nm_add_role_again - assert: - that: - - nm_add_role_again is not changed - - nm_add_role_again.previous.description == 'Ansible test role' - - nm_add_role_again.previous.displayName == 'ansible_test' - - nm_add_role_again.current.description == 'Ansible test role' - - nm_add_role_again.current.displayName == 'ansible_test' - - nm_add_role_again.current.id == nm_add_role.current.id - - -# CHANGE ROLE -- name: Change role (check_mode) - mso_role: - <<: *role_present - role_id: '{{ nm_add_role.current.id }}' - role: ansible_test2 - description: Ansible test role 2 - check_mode: yes - register: cm_change_role - -- name: Verify cm_change_role - assert: - that: - - cm_change_role is changed - - cm_change_role.current.description == 'Ansible test role 2' - - cm_change_role.current.displayName == 'ansible_test2' - - cm_change_role.current.id == nm_add_role.current.id - -- name: Change role (normal mode) - mso_role: - <<: *role_present - role_id: '{{ nm_add_role.current.id }}' - role: ansible_test2 - description: Ansible test role 2 - output_level: debug - register: nm_change_role - -- name: Verify nm_change_role - assert: - that: - - nm_change_role is changed - - nm_change_role.current.description == 'Ansible test role 2' - - nm_change_role.current.displayName == 'ansible_test2' - - nm_change_role.current.id == nm_add_role.current.id - -- name: Change role again (check_mode) - mso_role: - <<: *role_present - role_id: '{{ nm_add_role.current.id }}' - role: ansible_test2 - description: Ansible test role 2 - check_mode: yes - register: cm_change_role_again - -- name: Verify cm_change_role_again - assert: - that: - - cm_change_role_again is not changed - - cm_change_role_again.current.description == 'Ansible test role 2' - - cm_change_role_again.current.displayName == 'ansible_test2' - - cm_change_role_again.current.id == nm_add_role.current.id - -- name: Change role again (normal mode) - mso_role: - <<: *role_present - role_id: '{{ nm_add_role.current.id }}' - role: ansible_test2 - description: Ansible test role 2 - register: nm_change_role_again - -- name: Verify nm_change_role_again - assert: - that: - - nm_change_role_again is not changed - - nm_change_role_again.current.description == 'Ansible test role 2' - - nm_change_role_again.current.displayName == 'ansible_test2' - - nm_change_role_again.current.id == nm_add_role.current.id - - -# QUERY ALL ROLES -- name: Query all roles (check_mode) - mso_role: &role_query - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - state: query - check_mode: yes - register: cm_query_all_roles - -- name: Query all roles (normal mode) - mso_role: *role_query - register: nm_query_all_roles - -- name: Verify query_all_roles - assert: - that: - - cm_query_all_roles is not changed - - nm_query_all_roles is not changed - # NOTE: Order of roles is not stable between calls - #- cm_query_all_roles == nm_query_all_roles - - -# QUERY A ROLE -- name: Query our role - mso_role: - <<: *role_query - role: ansible_test2 - check_mode: yes - register: cm_query_role - -- name: Query our role - mso_role: - <<: *role_query - role: ansible_test2 - register: nm_query_role - -- name: Verify query_role - assert: - that: - - cm_query_role is not changed - - cm_query_role.current.description == 'Ansible test role 2' - - cm_query_role.current.displayName == 'ansible_test2' - - cm_query_role.current.id == nm_add_role.current.id - - nm_query_role is not changed - - nm_query_role.current.description == 'Ansible test role 2' - - nm_query_role.current.displayName == 'ansible_test2' - - nm_query_role.current.id == nm_add_role.current.id - - cm_query_role == nm_query_role - - -# REMOVE ROLE -- name: Remove role (check_mode) - mso_role: *role_absent - check_mode: yes - register: cm_remove_role - -- name: Verify cm_remove_role - assert: - that: - - cm_remove_role is changed - - cm_remove_role.current == {} - -- name: Remove role (normal mode) - mso_role: *role_absent - register: nm_remove_role - -- name: Verify nm_remove_role - assert: - that: - - nm_remove_role is changed - - nm_remove_role.current == {} - -- name: Remove role again (check_mode) - mso_role: *role_absent - check_mode: yes - register: cm_remove_role_again - -- name: Verify cm_remove_role_again - assert: - that: - - cm_remove_role_again is not changed - - cm_remove_role_again.current == {} - -- name: Remove role again (normal mode) - mso_role: *role_absent - register: nm_remove_role_again - -- name: Verify nm_remove_role_again - assert: - that: - - nm_remove_role_again is not changed - - nm_remove_role_again.current == {} - - -# QUERY NON-EXISTING ROLE -- name: Query non-existing role (check_mode) - mso_role: - <<: *role_query - role: ansible_test - check_mode: yes - register: cm_query_non_role - -- name: Query non-existing role (normal mode) - mso_role: - <<: *role_query - role: ansible_test - register: nm_query_non_role - -# TODO: Implement more tests -- name: Verify query_non_role - assert: - that: - - cm_query_non_role is not changed - - nm_query_non_role is not changed - - cm_query_non_role == nm_query_non_role diff --git a/test/integration/targets/mso_site/aliases b/test/integration/targets/mso_site/aliases deleted file mode 100644 index cd28d3c3621..00000000000 --- a/test/integration/targets/mso_site/aliases +++ /dev/null @@ -1,2 +0,0 @@ -# No ACI MultiSite infrastructure, so not enabled -unsupported diff --git a/test/integration/targets/mso_site/tasks/main.yml b/test/integration/targets/mso_site/tasks/main.yml deleted file mode 100644 index 2c0092edfea..00000000000 --- a/test/integration/targets/mso_site/tasks/main.yml +++ /dev/null @@ -1,283 +0,0 @@ -# Test code for the MSO 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: mso_hostname, mso_username and mso_password.' - when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined - - -# CLEAN ENVIRONMENT -- name: Remove site 2 - mso_site: &site_absent - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - site: '{{ mso_site | default("ansible_test") }}_2' - state: absent - -- name: Remove site - mso_site: - <<: *site_absent - site: '{{ mso_site | default("ansible_test") }}' - register: cm_remove_site - - -# ADD SITE -- name: Add site (check_mode) - mso_site: &site_present - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - site: '{{ mso_site | default("ansible_test") }}' - apic_username: admin - apic_password: '{{ apic_password }}' - apic_site_id: 101 - urls: - - https://{{ apic_hostname }} - location: - latitude: 50.887318 - longitude: 4.447084 - labels: - - Diegem - - EMEA - - POD51 - state: present - check_mode: yes - register: cm_add_site - -- name: Verify cm_add_site - assert: - that: - - cm_add_site is changed - - cm_add_site.previous == {} - - cm_add_site.current.id is not defined - - cm_add_site.current.name == mso_site|default("ansible_test") - -- name: Add site (normal mode) - mso_site: *site_present - register: nm_add_site - -- name: nm_Verify add_site - assert: - that: - - nm_add_site is changed - - nm_add_site.previous == {} - - nm_add_site.current.id is defined - - nm_add_site.current.name == mso_site|default("ansible_test") - -- name: Add site again (check_mode) - mso_site: *site_present - check_mode: yes - register: cm_add_site_again - -- name: Verify cm_add_site_again - assert: - that: - - cm_add_site_again is not changed - - cm_add_site_again.previous.name == mso_site|default("ansible_test") - - cm_add_site_again.current.id == nm_add_site.current.id - - cm_add_site_again.current.name == mso_site|default("ansible_test") - -- name: Add site again (normal mode) - mso_site: *site_present - register: nm_add_site_again - -- name: Verify nm_add_site_again - assert: - that: - - nm_add_site_again is not changed - - nm_add_site_again.previous.name == mso_site|default("ansible_test") - - nm_add_site_again.current.id == nm_add_site.current.id - - nm_add_site_again.current.name == mso_site|default("ansible_test") - - -# CHANGE SITE -- name: Change site (check_mode) - mso_site: - <<: *site_present - site_id: '{{ nm_add_site.current.id }}' - site: '{{ mso_site | default("ansible_test") }}_2' - check_mode: yes - register: cm_change_site - -- name: Verify cm_change_site - assert: - that: - - cm_change_site is changed - - cm_change_site.current.id == nm_add_site.current.id - - cm_change_site.current.name == '{{ mso_site | default("ansible_test") }}_2' - -- name: Change site (normal mode) - mso_site: - <<: *site_present - site_id: '{{ nm_add_site.current.id }}' - site: '{{ mso_site | default("ansible_test") }}_2' - output_level: debug - register: nm_change_site - -- name: Verify nm_change_site - assert: - that: - - nm_change_site is changed - - nm_change_site.current.id == nm_add_site.current.id - - nm_change_site.current.name == '{{ mso_site | default("ansible_test") }}_2' - -- name: Change site again (check_mode) - mso_site: - <<: *site_present - site_id: '{{ nm_add_site.current.id }}' - site: '{{ mso_site | default("ansible_test") }}_2' - check_mode: yes - register: cm_change_site_again - -- name: Verify cm_change_site_again - assert: - that: - - cm_change_site_again is not changed - - cm_change_site_again.current.id == nm_add_site.current.id - - cm_change_site_again.current.name == '{{ mso_site | default("ansible_test") }}_2' - -- name: Change site again (normal mode) - mso_site: - <<: *site_present - site_id: '{{ nm_add_site.current.id }}' - site: '{{ mso_site | default("ansible_test") }}_2' - register: nm_change_site_again - -- name: Verify nm_change_site_again - assert: - that: - - nm_change_site_again is not changed - - nm_change_site_again.current.id == nm_add_site.current.id - - nm_change_site_again.current.name == '{{ mso_site | default("ansible_test") }}_2' - - -# QUERY ALL SITES -- name: Query all sites (check_mode) - mso_site: &site_query - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - state: query - check_mode: yes - register: cm_query_all_sites - -- name: Query all sites (normal mode) - mso_site: *site_query - register: nm_query_all_sites - -- name: Verify query_all_sites - assert: - that: - - cm_query_all_sites is not changed - - nm_query_all_sites is not changed - # NOTE: Order of sites is not stable between calls - #- cm_query_all_sites == nm_query_all_sites - - -# QUERY A SITE -- name: Query our site - mso_site: - <<: *site_query - site: '{{ mso_site | default("ansible_test") }}_2' - check_mode: yes - register: cm_query_site - -- name: Query our site - mso_site: - <<: *site_query - site: '{{ mso_site | default("ansible_test") }}_2' - register: nm_query_site - -- name: Verify query_site - assert: - that: - - cm_query_site is not changed - - cm_query_site.current.id == nm_add_site.current.id - - cm_query_site.current.name == '{{ mso_site | default("ansible_test") }}_2' - - nm_query_site is not changed - - nm_query_site.current.id == nm_add_site.current.id - - nm_query_site.current.name == '{{ mso_site | default("ansible_test") }}_2' - - cm_query_site == nm_query_site - - -# REMOVE SITE -- name: Remove site (check_mode) - mso_site: *site_absent - check_mode: yes - register: cm_remove_site - -- name: Verify cm_remove_site - assert: - that: - - cm_remove_site is changed - - cm_remove_site.current == {} - -- name: Remove site (normal mode) - mso_site: *site_absent - register: nm_remove_site - -- name: Verify nm_remove_site - assert: - that: - - nm_remove_site is changed - - nm_remove_site.current == {} - -- name: Remove site again (check_mode) - mso_site: *site_absent - check_mode: yes - register: cm_remove_site_again - -- name: Verify cm_remove_site_again - assert: - that: - - cm_remove_site_again is not changed - - cm_remove_site_again.current == {} - -- name: Remove site again (normal mode) - mso_site: *site_absent - register: nm_remove_site_again - -- name: Verify nm_remove_site_again - assert: - that: - - nm_remove_site_again is not changed - - nm_remove_site_again.current == {} - - -# QUERY NON-EXISTING SITE -- name: Query non-existing site (check_mode) - mso_site: - <<: *site_query - site: '{{ mso_site | default("ansible_test") }}' - check_mode: yes - register: cm_query_non_site - -- name: Query non-existing site (normal mode) - mso_site: - <<: *site_query - site: '{{ mso_site | default("ansible_test") }}' - register: nm_query_non_site - -# TODO: Implement more tests -- name: Verify query_non_site - assert: - that: - - cm_query_non_site is not changed - - nm_query_non_site is not changed - - cm_query_non_site == nm_query_non_site diff --git a/test/integration/targets/mso_tenant/aliases b/test/integration/targets/mso_tenant/aliases deleted file mode 100644 index cd28d3c3621..00000000000 --- a/test/integration/targets/mso_tenant/aliases +++ /dev/null @@ -1,2 +0,0 @@ -# No ACI MultiSite infrastructure, so not enabled -unsupported diff --git a/test/integration/targets/mso_tenant/tasks/main.yml b/test/integration/targets/mso_tenant/tasks/main.yml deleted file mode 100644 index fb6be21a15c..00000000000 --- a/test/integration/targets/mso_tenant/tasks/main.yml +++ /dev/null @@ -1,289 +0,0 @@ -# Test code for the MSO 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: mso_hostname, mso_username and mso_password.' - when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined - - -# CLEAN ENVIRONMENT -- name: Remove tenant ansible_test2 - mso_tenant: &tenant_absent - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - tenant: ansible_test2 - state: absent - -- name: Remove tenant ansible_test - mso_tenant: - <<: *tenant_absent - tenant: ansible_test - register: cm_remove_tenant - - -# ADD TENANT -- name: Add tenant (check_mode) - mso_tenant: &tenant_present - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - tenant: ansible_test - display_name: Ansible test title - description: Ansible test tenant - state: present - check_mode: yes - register: cm_add_tenant - -- name: Verify cm_add_tenant - assert: - that: - - cm_add_tenant is changed - - cm_add_tenant.previous == {} - - cm_add_tenant.current.id is not defined - - cm_add_tenant.current.name == 'ansible_test' - - cm_add_tenant.current.description == 'Ansible test tenant' - -- name: Add tenant (normal mode) - mso_tenant: *tenant_present - register: nm_add_tenant - -- name: nm_Verify add_tenant - assert: - that: - - nm_add_tenant is changed - - nm_add_tenant.previous == {} - - nm_add_tenant.current.id is defined - - nm_add_tenant.current.name == 'ansible_test' - - nm_add_tenant.current.description == 'Ansible test tenant' - -- name: Add tenant again (check_mode) - mso_tenant: *tenant_present - check_mode: yes - register: cm_add_tenant_again - -- name: Verify cm_add_tenant_again - assert: - that: - - cm_add_tenant_again is not changed - - cm_add_tenant_again.previous.name == 'ansible_test' - - cm_add_tenant_again.previous.description == 'Ansible test tenant' - - cm_add_tenant_again.current.id == nm_add_tenant.current.id - - cm_add_tenant_again.current.name == 'ansible_test' - - cm_add_tenant_again.current.description == 'Ansible test tenant' - -- name: Add tenant again (normal mode) - mso_tenant: *tenant_present - register: nm_add_tenant_again - -- name: Verify nm_add_tenant_again - assert: - that: - - nm_add_tenant_again is not changed - - nm_add_tenant_again.previous.name == 'ansible_test' - - nm_add_tenant_again.previous.description == 'Ansible test tenant' - - nm_add_tenant_again.current.id == nm_add_tenant.current.id - - nm_add_tenant_again.current.name == 'ansible_test' - - nm_add_tenant_again.current.description == 'Ansible test tenant' - - -# CHANGE TENANT -- name: Change tenant (check_mode) - mso_tenant: - <<: *tenant_present - tenant_id: '{{ nm_add_tenant.current.id }}' - tenant: ansible_test2 - description: Ansible test tenant 2 - check_mode: yes - register: cm_change_tenant - -- name: Verify cm_change_tenant - assert: - that: - - cm_change_tenant is changed - - cm_change_tenant.current.id == nm_add_tenant.current.id - - cm_change_tenant.current.name == 'ansible_test2' - - cm_change_tenant.current.description == 'Ansible test tenant 2' - -- name: Change tenant (normal mode) - mso_tenant: - <<: *tenant_present - tenant_id: '{{ nm_add_tenant.current.id }}' - tenant: ansible_test2 - description: Ansible test tenant 2 - output_level: debug - register: nm_change_tenant - -- name: Verify nm_change_tenant - assert: - that: - - nm_change_tenant is changed - - nm_change_tenant.current.id == nm_add_tenant.current.id - - nm_change_tenant.current.name == 'ansible_test2' - - nm_change_tenant.current.description == 'Ansible test tenant 2' - -- name: Change tenant again (check_mode) - mso_tenant: - <<: *tenant_present - tenant_id: '{{ nm_add_tenant.current.id }}' - tenant: ansible_test2 - description: Ansible test tenant 2 - check_mode: yes - register: cm_change_tenant_again - -- name: Verify cm_change_tenant_again - assert: - that: - - cm_change_tenant_again is not changed - - cm_change_tenant_again.current.id == nm_add_tenant.current.id - - cm_change_tenant_again.current.name == 'ansible_test2' - - cm_change_tenant_again.current.description == 'Ansible test tenant 2' - -- name: Change tenant again (normal mode) - mso_tenant: - <<: *tenant_present - tenant_id: '{{ nm_add_tenant.current.id }}' - tenant: ansible_test2 - description: Ansible test tenant 2 - register: nm_change_tenant_again - -- name: Verify nm_change_tenant_again - assert: - that: - - nm_change_tenant_again is not changed - - nm_change_tenant_again.current.id == nm_add_tenant.current.id - - nm_change_tenant_again.current.name == 'ansible_test2' - - nm_change_tenant_again.current.description == 'Ansible test tenant 2' - - -# QUERY ALL TENANTS -- name: Query all tenants (check_mode) - mso_tenant: &tenant_query - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - state: query - check_mode: yes - register: cm_query_all_tenants - -- name: Query all tenants (normal mode) - mso_tenant: *tenant_query - register: nm_query_all_tenants - -- name: Verify query_all_tenants - assert: - that: - - cm_query_all_tenants is not changed - - nm_query_all_tenants is not changed - # NOTE: Order of tenants is not stable between calls - #- cm_query_all_tenants == nm_query_all_tenants - - -# QUERY A TENANT -- name: Query our tenant - mso_tenant: - <<: *tenant_query - tenant: ansible_test2 - check_mode: yes - register: cm_query_tenant - -- name: Query our tenant - mso_tenant: - <<: *tenant_query - tenant: ansible_test2 - register: nm_query_tenant - -- name: Verify query_tenant - assert: - that: - - cm_query_tenant is not changed - - cm_query_tenant.current.id == nm_add_tenant.current.id - - cm_query_tenant.current.name == 'ansible_test2' - - cm_query_tenant.current.description == 'Ansible test tenant 2' - - nm_query_tenant is not changed - - nm_query_tenant.current.id == nm_add_tenant.current.id - - nm_query_tenant.current.name == 'ansible_test2' - - nm_query_tenant.current.description == 'Ansible test tenant 2' - - cm_query_tenant == nm_query_tenant - - -# REMOVE TENANT -- name: Remove tenant (check_mode) - mso_tenant: *tenant_absent - check_mode: yes - register: cm_remove_tenant - -- name: Verify cm_remove_tenant - assert: - that: - - cm_remove_tenant is changed - - cm_remove_tenant.current == {} - -- name: Remove tenant (normal mode) - mso_tenant: *tenant_absent - register: nm_remove_tenant - -- name: Verify nm_remove_tenant - assert: - that: - - nm_remove_tenant is changed - - nm_remove_tenant.current == {} - -- name: Remove tenant again (check_mode) - mso_tenant: *tenant_absent - check_mode: yes - register: cm_remove_tenant_again - -- name: Verify cm_remove_tenant_again - assert: - that: - - cm_remove_tenant_again is not changed - - cm_remove_tenant_again.current == {} - -- name: Remove tenant again (normal mode) - mso_tenant: *tenant_absent - register: nm_remove_tenant_again - -- name: Verify nm_remove_tenant_again - assert: - that: - - nm_remove_tenant_again is not changed - - nm_remove_tenant_again.current == {} - - -# QUERY NON-EXISTING TENANT -- name: Query non-existing tenant (check_mode) - mso_tenant: - <<: *tenant_query - tenant: ansible_test - check_mode: yes - register: cm_query_non_tenant - -- name: Query non-existing tenant (normal mode) - mso_tenant: - <<: *tenant_query - tenant: ansible_test - register: nm_query_non_tenant - -# TODO: Implement more tests -- name: Verify query_non_tenant - assert: - that: - - cm_query_non_tenant is not changed - - nm_query_non_tenant is not changed - - cm_query_non_tenant == nm_query_non_tenant diff --git a/test/integration/targets/mso_user/aliases b/test/integration/targets/mso_user/aliases deleted file mode 100644 index cd28d3c3621..00000000000 --- a/test/integration/targets/mso_user/aliases +++ /dev/null @@ -1,2 +0,0 @@ -# No ACI MultiSite infrastructure, so not enabled -unsupported diff --git a/test/integration/targets/mso_user/tasks/main.yml b/test/integration/targets/mso_user/tasks/main.yml deleted file mode 100644 index 2bddd7ac341..00000000000 --- a/test/integration/targets/mso_user/tasks/main.yml +++ /dev/null @@ -1,292 +0,0 @@ -# Test code for the MSO 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: mso_hostname, mso_username and mso_password.' - when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined - - -# CLEAN ENVIRONMENT -- name: Remove user ansible_test2 - mso_user: &user_absent - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - user_name: ansible_test2 - state: absent - -- name: Remove user ansible_test - mso_user: - <<: *user_absent - user_name: ansible_test - register: cm_remove_user - - -# ADD USER -- name: Add user (check_mode) - mso_user: &user_present - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - user_name: ansible_test - user_password: 'S0m3!1n1t14l!p455w0rd' - # NOTE: First name, last name, phone and email are mandatory on creation - first_name: Ansible - last_name: Test - email: msc@cisco.com - phone: +32 478 436 299 - account_status: active - roles: - - powerUser - domain: Local - - state: present - check_mode: yes - register: cm_add_user - -- name: Verify cm_add_user - assert: - that: - - cm_add_user is changed - - cm_add_user.previous == {} - - cm_add_user.current.id is not defined - - cm_add_user.current.username == 'ansible_test' - -- name: Add user (normal mode) - mso_user: *user_present - register: nm_add_user - -- name: nm_Verify add_user - assert: - that: - - nm_add_user is changed - - nm_add_user.previous == {} - - nm_add_user.current.id is defined - - nm_add_user.current.username == 'ansible_test' - -- name: Add user again (check_mode) - mso_user: - <<: *user_present - # NOTE: We need to modify the password for a new user - user_password: 'S0m3!n3w!p455w0rd' - check_mode: yes - register: cm_add_user_again - -- name: Verify cm_add_user_again - assert: - that: - - cm_add_user_again is changed - - cm_add_user_again.previous.username == 'ansible_test' - - cm_add_user_again.current.id == nm_add_user.current.id - - cm_add_user_again.current.username == 'ansible_test' - -- name: Add user again (normal mode) - mso_user: - <<: *user_present - # NOTE: We need to modify the password for a new user - user_password: 'S0m3!n3w!p455w0rd' - register: nm_add_user_again - -- name: Verify nm_add_user_again - assert: - that: - - nm_add_user_again is changed - - nm_add_user_again.previous.username == 'ansible_test' - - nm_add_user_again.current.id == nm_add_user.current.id - - nm_add_user_again.current.username == 'ansible_test' - - -# CHANGE USER -- name: Change user (check_mode) - mso_user: - <<: *user_present - user_id: '{{ nm_add_user.current.id }}' - user_name: ansible_test2 - user_password: null - check_mode: yes - register: cm_change_user - -- name: Verify cm_change_user - assert: - that: - - cm_change_user is changed - - cm_change_user.current.id == nm_add_user.current.id - - cm_change_user.current.username == 'ansible_test2' - -- name: Change user (normal mode) - mso_user: - <<: *user_present - user_id: '{{ nm_add_user.current.id }}' - user_name: ansible_test2 - user_password: null - output_level: debug - register: nm_change_user - -- name: Verify nm_change_user - assert: - that: - - nm_change_user is changed - - nm_change_user.current.id == nm_add_user.current.id - - nm_change_user.current.username == 'ansible_test2' - -- name: Change user again (check_mode) - mso_user: - <<: *user_present - user_id: '{{ nm_add_user.current.id }}' - user_name: ansible_test2 - user_password: null - check_mode: yes - register: cm_change_user_again - -- name: Verify cm_change_user_again - assert: - that: - - cm_change_user_again is not changed - - cm_change_user_again.current.id == nm_add_user.current.id - - cm_change_user_again.current.username == 'ansible_test2' - -- name: Change user again (normal mode) - mso_user: - <<: *user_present - user_id: '{{ nm_add_user.current.id }}' - user_name: ansible_test2 - user_password: null - register: nm_change_user_again - -- name: Verify nm_change_user_again - assert: - that: - - nm_change_user_again is not changed - - nm_change_user_again.current.id == nm_add_user.current.id - - nm_change_user_again.current.username == 'ansible_test2' - - -# QUERY ALL USERS -- name: Query all users (check_mode) - mso_user: &user_query - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' - state: query - check_mode: yes - register: cm_query_all_users - -- name: Query all users (normal mode) - mso_user: *user_query - register: nm_query_all_users - -- name: Verify query_all_users - assert: - that: - - cm_query_all_users is not changed - - nm_query_all_users is not changed - # NOTE: Order of users is not stable between calls - #- cm_query_all_users == nm_query_all_users - - -# QUERY A USER -- name: Query our user - mso_user: - <<: *user_query - user_name: ansible_test2 - check_mode: yes - register: cm_query_user - -- name: Query our user - mso_user: - <<: *user_query - user_name: ansible_test2 - register: nm_query_user - -- name: Verify query_user - assert: - that: - - cm_query_user is not changed - - cm_query_user.current.id == nm_add_user.current.id - - cm_query_user.current.username == 'ansible_test2' - - nm_query_user is not changed - - nm_query_user.current.id == nm_add_user.current.id - - nm_query_user.current.username == 'ansible_test2' - - cm_query_user == nm_query_user - - -# REMOVE USER -- name: Remove user (check_mode) - mso_user: *user_absent - check_mode: yes - register: cm_remove_user - -- name: Verify cm_remove_user - assert: - that: - - cm_remove_user is changed - - cm_remove_user.current == {} - -- name: Remove user (normal mode) - mso_user: *user_absent - register: nm_remove_user - -- name: Verify nm_remove_user - assert: - that: - - nm_remove_user is changed - - nm_remove_user.current == {} - -- name: Remove user again (check_mode) - mso_user: *user_absent - check_mode: yes - register: cm_remove_user_again - -- name: Verify cm_remove_user_again - assert: - that: - - cm_remove_user_again is not changed - - cm_remove_user_again.current == {} - -- name: Remove user again (normal mode) - mso_user: *user_absent - register: nm_remove_user_again - -- name: Verify nm_remove_user_again - assert: - that: - - nm_remove_user_again is not changed - - nm_remove_user_again.current == {} - - -# QUERY NON-EXISTING USER -- name: Query non-existing user (check_mode) - mso_user: - <<: *user_query - user_name: ansible_test - check_mode: yes - register: cm_query_non_user - -- name: Query non-existing user (normal mode) - mso_user: - <<: *user_query - user_name: ansible_test - register: nm_query_non_user - -# TODO: Implement more tests -- name: Verify query_non_user - assert: - that: - - cm_query_non_user is not changed - - nm_query_non_user is not changed - - cm_query_non_user == nm_query_non_user diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index bec92ee02d1..4e163869ff9 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -1456,34 +1456,6 @@ lib/ansible/modules/net_tools/basics/uri.py pylint:blacklisted-name lib/ansible/modules/net_tools/basics/uri.py validate-modules:doc-required-mismatch lib/ansible/modules/net_tools/basics/uri.py validate-modules:parameter-list-no-elements lib/ansible/modules/net_tools/basics/uri.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/network/aci/mso_label.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_role.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_role.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_schema.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_schema_site.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_anp_epg.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_anp_epg_domain.py pylint:ansible-bad-function -lib/ansible/modules/network/aci/mso_schema_site_anp_epg_domain.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticleaf.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_anp_epg_staticport.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_anp_epg_subnet.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_bd_l3out.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_vrf_region.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_site_vrf_region_cidr_subnet.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_schema_template_bd.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py validate-modules:invalid-ansiblemodule-schema -lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_schema_template_deploy.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_site.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_site.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_tenant.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_tenant.py validate-modules:parameter-list-no-elements -lib/ansible/modules/network/aci/mso_user.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/aci/mso_user.py validate-modules:parameter-list-no-elements lib/ansible/modules/network/dellos10/dellos10_command.py validate-modules:doc-default-does-not-match-spec lib/ansible/modules/network/dellos10/dellos10_command.py validate-modules:doc-missing-type lib/ansible/modules/network/dellos10/dellos10_command.py validate-modules:doc-required-mismatch