From b7ec254816f6d93a6a43fa7fce55f95617cbcd9b Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Fri, 25 Jan 2019 15:27:55 +0100 Subject: [PATCH] mso_schema_site: Add sites to a schema template (#51332) A new module to associate sites to schema templates --- lib/ansible/module_utils/network/aci/mso.py | 34 ++- lib/ansible/modules/network/aci/mso_schema.py | 1 + .../modules/network/aci/mso_schema_site.py | 200 ++++++++++++++++++ .../network/aci/mso_schema_template.py | 1 + 4 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 lib/ansible/modules/network/aci/mso_schema_site.py diff --git a/lib/ansible/module_utils/network/aci/mso.py b/lib/ansible/module_utils/network/aci/mso.py index cc51d292c3..a6e1d27934 100644 --- a/lib/ansible/module_utils/network/aci/mso.py +++ b/lib/ansible/module_utils/network/aci/mso.py @@ -281,7 +281,7 @@ class MSOModule(object): 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 '%s': %s" % (schema, s)) + self.module.fail_json(msg="Schema lookup failed for schema '%s': %s" % (schema, s)) return s['id'] def lookup_domain(self, domain): @@ -293,7 +293,7 @@ class MSOModule(object): 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 '%s': %s" % (domain, d)) + self.module.fail_json(msg="Domain lookup failed for domain '%s': %s" % (domain, d)) return d['id'] def lookup_roles(self, roles): @@ -305,12 +305,24 @@ class MSOModule(object): 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." % role) + 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 '%s': %s" % (role, r)) + self.module.fail_json(msg="Role lookup failed for role '%s': %s" % (role, r)) ids.append(dict(roleId=r['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['id'] + def lookup_sites(self, sites): ''' Look up sites and return their ids ''' if sites is None: @@ -320,9 +332,9 @@ class MSOModule(object): for site in sites: s = self.get_obj('sites', name=site) if not s: - self.module.fail_json(msg="Site '%s' is not valid." % site) + 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 '%s': %s" % (site, s)) + self.module.fail_json(msg="Site lookup failed for site '%s': %s" % (site, s)) ids.append(dict(siteId=s['id'], securityDomains=[])) return ids @@ -333,9 +345,9 @@ class MSOModule(object): t = self.get_obj('tenants', key='tenants', name=tenant) if not t: - self.module.fail_json(msg="Tenant '%s' is not valid." % tenant) + 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 '%s': %s" % (tenant, t)) + self.module.fail_json(msg="Tenant lookup failed for tenant '%s': %s" % (tenant, t)) return t['id'] def lookup_users(self, users): @@ -347,9 +359,9 @@ class MSOModule(object): for user in users: u = self.get_obj('users', username=user) if not u: - self.module.fail_json(msg="User '%s' is not valid." % user) + 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 '%s': %s" % (user, u)) + self.module.fail_json(msg="User lookup failed for user '%s': %s" % (user, u)) ids.append(dict(userId=u['id'])) return ids @@ -368,7 +380,7 @@ class MSOModule(object): if not l: l = self.create_label(label, label_type) if 'id' not in l: - self.module.fail_json(msg="Label lookup failed for '%s': %s" % (label, l)) + self.module.fail_json(msg="Label lookup failed for label '%s': %s" % (label, l)) ids.append(l['id']) return ids diff --git a/lib/ansible/modules/network/aci/mso_schema.py b/lib/ansible/modules/network/aci/mso_schema.py index da6288cc01..1026ea7f21 100644 --- a/lib/ansible/modules/network/aci/mso_schema.py +++ b/lib/ansible/modules/network/aci/mso_schema.py @@ -1,6 +1,7 @@ #!/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 diff --git a/lib/ansible/modules/network/aci/mso_schema_site.py b/lib/ansible/modules/network/aci/mso_schema_site.py new file mode 100644 index 0000000000..676b51181a --- /dev/null +++ b/lib/ansible/modules/network/aci/mso_schema_site.py @@ -0,0 +1,200 @@ +#!/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: + 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: + 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: + 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', required=False, aliases=['name']), + template=dict(type='str', required=False), + 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['schema'] + site = module.params['site'] + template = module.params['template'] + state = module.params['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_id = schema_obj['id'] + schema_path = 'schemas/{id}'.format(**schema_obj) + + # Get site + site_id = mso.lookup_site(site) + + mso.existing = {} + if 'sites' in schema_obj: + sites = [(s['siteId'], s['templateName']) for s in schema_obj['sites']] + if template: + if (site_id, template) in sites: + site_idx = sites.index((site_id, template)) + mso.existing = schema_obj['sites'][site_idx] + else: + mso.existing = schema_obj['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() + + ops = [] + + mso.previous = mso.existing + if state == 'absent': + mso.proposed = mso.sent = {} + + if mso.existing: + # Remove existing site + mso.existing = {} + ops.append(dict(op='remove', path='/sites/{0}'.format(site_idx))) + + 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) + + mso.existing = mso.proposed = mso.sent + ops.append(dict(op='add', path='/sites/-', 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.py b/lib/ansible/modules/network/aci/mso_schema_template.py index be2fe8e3c3..e77ef837de 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template.py +++ b/lib/ansible/modules/network/aci/mso_schema_template.py @@ -1,6 +1,7 @@ #!/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