From 2413169b25a1bd75da975a4ef2008e5188f6a77f Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Thu, 24 Jan 2019 17:38:39 +0100 Subject: [PATCH] mso_st_filter_entry: Manage filter entries in templates (#51290) A new module to manage filter entries in MSO schema templates. --- .../aci/mso_schema_template_filter_entry.py | 374 ++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py 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 new file mode 100644 index 0000000000..07afbcb4b7 --- /dev/null +++ b/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py @@ -0,0 +1,374 @@ +#!/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: list + filter: + description: + - The name of the filter to manage. + type: str + 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 +extends_documentation_fragment: mso +''' + +EXAMPLES = r''' +- name: Add a new filter + mso_schema_template_filter: + 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 + mso_schema_template_filter: + 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 filters + mso_schema_template_filter: + 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 filters + mso_schema_template_filter: + 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), # This parameter is not required for querying all objects + filter_display_name=dict(type='str', aliases=['filter_display_name']), + entry=dict(type='str', required=True, aliases=['name']), + 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['schema'] + template = module.params['template'] + filter_name = module.params['filter'] + filter_display_name = module.params['filter_display_name'] + entry = module.params['entry'] + display_name = module.params['display_name'] + description = module.params['description'] + ethertype = module.params['ethertype'] + ip_protocol = module.params['ip_protocol'] + tcp_session_rules = module.params['tcp_session_rules'] + source_from = module.params['source_from'] + source_to = module.params['source_to'] + destination_from = module.params['destination_from'] + destination_to = module.params['destination_to'] + arp_flag = module.params['arp_flag'] + stateful = module.params['stateful'] + fragments_only = module.params['fragments_only'] + state = module.params['state'] + + mso = MSOModule(module) + + # Get schema_id + schema_obj = mso.get_obj('schemas', displayName=schema) + if schema_obj: + schema_id = schema_obj['id'] + else: + mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) + + path = 'schemas/{id}'.format(id=schema_id) + + # Get template + templates = [t['name'] for t in schema_obj['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['name'] for f in schema_obj['templates'][template_idx]['filters']] + if filter_name in filters: + filter_idx = filters.index(filter_name) + + entries = [f['name'] for f in schema_obj['templates'][template_idx]['filters'][filter_idx]['entries']] + if entry in entries: + entry_idx = entries.index(entry) + mso.existing = schema_obj['templates'][template_idx]['filters'][filter_idx]['entries'][entry_idx] + + if state == 'query': + if entry is None: + mso.existing = schema_obj['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() + + 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 = {} + operations = [ + dict(op='remove', path='/templates/{template}/filters/{filter}'.format(template=template, filter=filter_name)), + ] + if not module.check_mode: + mso.request(path, method='PATCH', data=operations) + else: + mso.existing = {} + operations = [ + dict(op='remove', path='/templates/{template}/filters/{filter}/entries/{entry}'.format(template=template, filter=filter_name, entry=entry)), + ] + if not module.check_mode: + mso.request(path, method='PATCH', data=operations) + + elif state == 'present': + + if display_name is None: + display_name = mso.existing.get('displayName', entry) + if description is None: + description = mso.existing.get('description', '') + if ethertype is None: + ethertype = mso.existing.get('etherType', 'unspecified') + if ip_protocol is None: + ip_protocol = mso.existing.get('ipProtocol', 'unspecified') + if tcp_session_rules is None: + tcp_session_rules = mso.existing.get('tcpSessionRules', ['unspecified']) + if source_from is None: + source_from = mso.existing.get('sourceFrom', 'unspecified') + if source_to is None: + source_to = mso.existing.get('sourceTo', 'unspecified') + if destination_from is None: + destination_from = mso.existing.get('destinationFrom', 'unspecified') + if destination_to is None: + destination_to = mso.existing.get('destinationTo', 'unspecified') + if arp_flag is None: + arp_flag = mso.existing.get('arpFlag', 'unspecified') + if stateful is None: + stateful = mso.existing.get('stateful', False) + if fragments_only is None: + fragments_only = mso.existing.get('matchOnlyFragments', 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) + mso.existing = mso.sent + + 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], + ) + + operations = [ + dict(op='add', path='/templates/{template}/filters/-'.format(template=template), value=payload), + ] + + elif entry_idx is None: + # Entry does not exist, so we have to add it + operations = [ + dict(op='add', path='/templates/{template}/filters/{filter}/entries/-'.format(template=template, filter=filter_name), value=mso.sent) + ] + + else: + # Entry exists, we have to update it + alias = '/templates/{template}/filters/{filter}/entries/{entry}'.format(template=template, filter=filter_name, entry=entry) + operations = [ + dict(op='replace', path='{alias}/name'.format(alias=alias), value=entry), + dict(op='replace', path='{alias}/displayName'.format(alias=alias), value=display_name), + dict(op='replace', path='{alias}/description'.format(alias=alias), value=description), + dict(op='replace', path='{alias}/etherType'.format(alias=alias), value=ethertype), + dict(op='replace', path='{alias}/ipProtocol'.format(alias=alias), value=ip_protocol), + dict(op='replace', path='{alias}/tcpSessionRules'.format(alias=alias), value=tcp_session_rules), + dict(op='replace', path='{alias}/sourceFrom'.format(alias=alias), value=source_from), + dict(op='replace', path='{alias}/sourceTo'.format(alias=alias), value=source_to), + dict(op='replace', path='{alias}/destinationFrom'.format(alias=alias), value=destination_from), + dict(op='replace', path='{alias}/destinationTo'.format(alias=alias), value=destination_to), + dict(op='replace', path='{alias}/arpFlag'.format(alias=alias), value=arp_flag), + dict(op='replace', path='{alias}/stateful'.format(alias=alias), value=stateful), + dict(op='replace', path='{alias}/matchOnlyFragments'.format(alias=alias), value=fragments_only), + ] + + if not module.check_mode: + mso.request(path, method='PATCH', data=operations) + + mso.exit_json() + + +if __name__ == "__main__": + main()