From aedad0e73386536fb70ba43b082e408efe8d279e Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Wed, 17 Apr 2019 21:37:11 -0500 Subject: [PATCH 01/10] Initial commit for meraki_mx_l7_firewall module --- .../network/meraki/meraki_mx_l7_firewall.py | 408 ++++++++++++++++++ .../targets/meraki_mx_l7_firewall/aliases | 2 + .../meraki_mx_l7_firewall/tasks/main.yml | 333 ++++++++++++++ 3 files changed, 743 insertions(+) create mode 100644 lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py create mode 100644 test/integration/targets/meraki_mx_l7_firewall/aliases create mode 100644 test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py new file mode 100644 index 00000000000000..2f27dc19a3d673 --- /dev/null +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -0,0 +1,408 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Kevin Breit (@kbreit) +# 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: meraki_mx_l7_firewall +short_description: Manage MX appliance layer 7 firewalls in the Meraki cloud +version_added: "2.9" +description: +- Allows for creation, management, and visibility into layer 7 firewalls implemented on Meraki MX firewalls. +notes: +- Module assumes a complete list of firewall rules are passed as a parameter. +- If there is interest in this module allowing manipulation of a single firewall rule, please submit an issue against this module. +options: + state: + description: + - Create or modify an organization. + choices: ['present', 'query'] + default: present + org_name: + description: + - Name of organization. + - If C(clone) is specified, C(org_name) is the name of the new organization. + org_id: + description: + - ID of organization. + type: str + net_name: + description: + - Name of network which MX firewall is in. + net_id: + description: + - ID of network which MX firewall is in. + rules: + description: + - List of layer 7 firewall rules. + suboptions: + policy: + description: + - Policy to apply if rule is hit. + choices: [deny] + default: deny + type: str + type: + description: + - Type of policy to apply. + choices: [application, + application_category, + blacklisted_countries, + host, + ip_range, + port, + whitelisted_countries] + type: str + application: + description: + - Application to filter. + suboptions: + name: + description: + - Name of application to filter as defined by Meraki. + type: str + id: + description: + - URI of application as defined by Meraki. + type: str + application_category: + description: + - Category of applications to filter. + suboptions: + name: + description: + - Name of application category to filter as defined by Meraki. + type: str + id: + description: + - URI of application category as defined by Meraki. + type: str + host: + description: + - FQDN of host to filter. + type: str + ip_range: + description: + - CIDR notation range of IP addresses to apply rule to. + type: str + port: + description: + - TCP or UDP based port to filter. + type: str + countries: + description: + - List of countries to whitelist or blacklist. + - The countries follow the two-letter ISO 3166-1 alpha-2 format. + type: list + categories: + description: + - When C(True), specifies that applications and application categories should be queried instead of firewall rules. + type: bool +author: +- Kevin Breit (@kbreit) +extends_documentation_fragment: meraki +''' + +EXAMPLES = r''' +- name: Query firewall rules + meraki_mx_l7_firewall: + auth_key: abc123 + org_name: YourOrg + net_name: YourNet + state: query + delegate_to: localhost + +- name: Query applications and application categories + meraki_mx_l7_firewall: + auth_key: abc123 + org_name: YourOrg + net_name: YourNet + categories: yes + state: query + delegate_to: localhost + +- name: Set firewall rules + meraki_mx_l7_firewall: + auth_key: abc123 + org_name: YourOrg + net_name: YourNet + state: present + rules: + - type: whitelisted_countries + countries: + - US + - FR + - type: blacklisted_countries + countries: + - CN + - policy: deny + type: port + port: 8080 + - type: port + port: 1234 + - type: host + host: asdf.com + - type: application + application: + id: meraki:layer7/application/205 + - type: application_category + application: + id: meraki:layer7/category/24 + delegate_to: localhost +''' + +RETURN = r''' +data: + description: Firewall rules associated to network. + returned: success + type: complex + contains: + rules: + description: Ordered list of firewall rules. + returned: success, when not querying applications + type: list + contains: + policy: + description: Action to apply when rule is hit. + returned: success + type: string + sample: deny + type: + description: Type of rule category. + returned: success + type: string + sample: applications + value: + description: + - Matching value based on type. + - Returned type will vary. + returned: success + type: string + sample: 80 + applicationCategories: + description: List of application categories and applications. + type: list + returned: success, when querying applications + contains: + applications: + description: List of applications within a category. + type: list + contains: + id: + description: URI of application. + returned: success + type: string + sample: Gmail + name: + description: Descriptive name of application. + returned: success + type: string + sample: meraki:layer7/application/4 + id: + description: URI of application category. + returned: success + type: string + sample: Email + name: + description: Descriptive name of application category. + returned: success + type: string + sample: layer7/category/1 +''' + +import os +from ansible.module_utils.basic import AnsibleModule, json, env_fallback +from ansible.module_utils.urls import fetch_url +from ansible.module_utils._text import to_native +from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec + + +def get_applications(meraki, net_id): + path = meraki.construct_path('get_categories', net_id=net_id) + return meraki.request(path, method='GET') + + +def lookup_application(meraki, net_id, application): + response = get_applications(meraki, net_id) + for category in response['applicationCategories']: + if category['name'].lower() == application.lower(): + return category['id'] + for app in category['applications']: + if app['name'].lower() == application.lower(): + return app['id'] + meraki.fail_json(msg="No application or category named {0} found".format(application)) + + +def assemble_payload(meraki, net_id, rule): + if rule['type'] == 'application': + new_rule = {'policy': rule['policy'], + 'type': 'application', + } + if rule['application']['id']: + new_rule['value'] = {'id': rule['application']['id']} + elif rule['application']['name']: + new_rule['value'] = {'id': lookup_application(meraki, net_id, rule['application']['name'])} + elif rule['type'] == 'application_category': + new_rule = {'policy': rule['policy'], + 'type': 'applicationCategory', + } + if rule['application']['id']: + new_rule['value'] = {'id': rule['application']['id']} + elif rule['application']['name']: + new_rule['value'] = {'id': lookup_application(meraki, net_id, rule['application']['name'])} + elif rule['type'] == 'ip_range': + new_rule = {'policy': rule['policy'], + 'type': 'ipRange', + 'value': rule['ip_range']} + elif rule['type'] == 'host': + new_rule = {'policy': rule['policy'], + 'type': rule['type'], + 'value': rule['host']} + elif rule['type'] == 'port': + new_rule = {'policy': rule['policy'], + 'type': rule['type'], + 'value': rule['port']} + elif rule['type'] == 'blacklisted_countries': + new_rule = {'policy': rule['policy'], + 'type': 'blacklistedCountries', + 'value': rule['countries'] + } + elif rule['type'] == 'whitelisted_countries': + new_rule = {'policy': rule['policy'], + 'type': 'whitelistedCountries', + 'value': rule['countries'] + } + return new_rule + + +def get_rules(meraki, net_id): + path = meraki.construct_path('get_all', net_id=net_id) + response = meraki.request(path, method='GET') + if meraki.status == 200: + return response + + +def main(): + # define the available arguments/parameters that a user can pass to + # the module + + application_arg_spec = dict(id=dict(type='str'), + name=dict(type='str'), + ) + + rule_arg_spec = dict(policy=dict(type='str', choices=['deny'], default='deny'), + type=dict(type='str', choices=['application', + 'application_category', + 'blacklisted_countries', + 'host', + 'ip_range', + 'port', + 'whitelisted_countries']), + ip_range=dict(type='str'), + application=dict(type='dict', default=None, options=application_arg_spec), + host=dict(type='str'), + port=dict(type='str'), + countries=dict(type='list'), + ) + + argument_spec = meraki_argument_spec() + argument_spec.update(state=dict(type='str', choices=['present', 'query'], default='present'), + net_name=dict(type='str'), + net_id=dict(type='str'), + rules=dict(type='list', default=None, elements='dict', options=rule_arg_spec), + categories=dict(type='bool'), + ) + + # seed the result dict in the object + # we primarily care about changed and state + # change is if this module effectively modified the target + # state will include any data that you want your module to pass back + # for consumption, for example, in a subsequent task + result = dict( + changed=False, + ) + # the AnsibleModule object will be our abstraction working with Ansible + # this includes instantiation, a couple of common attr would be the + # args/params passed to the execution, as well as if the module + # supports check mode + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True, + ) + meraki = MerakiModule(module, function='mx_l7_firewall') + + meraki.params['follow_redirects'] = 'all' + + query_urls = {'mx_l7_firewall': '/networks/{net_id}/l7FirewallRules/'} + query_category_urls = {'mx_l7_firewall': '/networks/{net_id}/l7FirewallRules/applicationCategories'} + update_urls = {'mx_l7_firewall': '/networks/{net_id}/l7FirewallRules/'} + + meraki.url_catalog['get_all'].update(query_urls) + meraki.url_catalog['get_categories'] = (query_category_urls) + meraki.url_catalog['update'] = update_urls + + payload = None + + # manipulate or modify the state as needed (this is going to be the + # part where your module will do what it needs to do) + org_id = meraki.params['org_id'] + orgs = None + if org_id is None: + orgs = meraki.get_orgs() + for org in orgs: + if org['name'] == meraki.params['org_name']: + org_id = org['id'] + net_id = meraki.params['net_id'] + if net_id is None: + if orgs is None: + orgs = meraki.get_orgs() + net_id = meraki.get_net_id(net_name=meraki.params['net_name'], + data=meraki.get_nets(org_id=org_id)) + + if meraki.params['state'] == 'query': + if meraki.params['categories'] is True: # Output only applications + meraki.result['data'] = get_applications(meraki, net_id) + else: + meraki.result['data'] = get_rules(meraki, net_id) + elif meraki.params['state'] == 'present': + rules = get_rules(meraki, net_id) + path = meraki.construct_path('get_all', net_id=net_id) + if meraki.params['rules']: + payload = {'rules': []} + for rule in meraki.params['rules']: + payload['rules'].append(assemble_payload(meraki, net_id, rule)) + else: + payload = dict() + if meraki.is_update_required(rules, payload): + if meraki.module.check_mode is True: + meraki.result['data'] = payload + meraki.result['changed'] = False + meraki.exit_json(**meraki.result) + response = meraki.request(path, method='PUT', payload=json.dumps(payload)) + if meraki.status == 200: + meraki.result['data'] = response + meraki.result['changed'] = True + else: + if meraki.module.check_mode is True: + meraki.result['data'] = rules + meraki.result['changed'] = False + meraki.exit_json(**meraki.result) + meraki.result['data'] = payload + + # in the event of a successful module execution, you will want to + # simple AnsibleModule.exit_json(), passing the key/value results + meraki.exit_json(**meraki.result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/meraki_mx_l7_firewall/aliases b/test/integration/targets/meraki_mx_l7_firewall/aliases new file mode 100644 index 00000000000000..06fe32bc66d06e --- /dev/null +++ b/test/integration/targets/meraki_mx_l7_firewall/aliases @@ -0,0 +1,2 @@ +unsupported + diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml new file mode 100644 index 00000000000000..fab5f77211b55b --- /dev/null +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -0,0 +1,333 @@ +# Test code for the Meraki Organization module +# Copyright: (c) 2018, Kevin Breit (@kbreit) + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- block: + # - name: Test an API key is provided + # fail: + # msg: Please define an API key + # when: auth_key is not defined + + # - name: Use an invalid domain + # meraki_organization: + # auth_key: '{{ auth_key }}' + # host: marrrraki.com + # state: present + # org_name: IntTestOrg + # output_level: debug + # delegate_to: localhost + # register: invalid_domain + # ignore_errors: yes + + # - name: Disable HTTP + # meraki_organization: + # auth_key: '{{ auth_key }}' + # use_https: false + # state: query + # output_level: debug + # delegate_to: localhost + # register: http + # ignore_errors: yes + + # - name: Connection assertions + # assert: + # that: + # - '"Failed to connect to" in invalid_domain.msg' + # - '"http" in http.url' + + - name: Create network + meraki_network: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + type: appliance + delegate_to: localhost + + - name: Query firewall rules + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: query + delegate_to: localhost + register: query + + - debug: + var: query + + - assert: + that: + - query.data is defined + + - name: Query firewall application categories + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: query + categories: yes + delegate_to: localhost + register: query_categories + + - debug: + var: query_categories + + - assert: + that: + - query_categories.data is defined + + - name: Create firewall rule for IP range in check mode + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range_check + check_mode: yes + + - debug: + var: create_ip_range_check + + - assert: + that: + - create_ip_range_check is not changed + + - name: Create firewall rule for IP range + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range + + - debug: + var: create_ip_range + + - assert: + that: + - create_ip_range is changed + - create_ip_range.data.rules | length == 1 + + - name: Create firewall rule for IP range with idempotency with check mode + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range_idempotent_check + check_mode: yes + + + - assert: + that: + - create_ip_range_idempotent_check is not changed + + - name: Create firewall rule for IP range with idempotency + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range_idempotent + + - assert: + that: + - create_ip_range_idempotent is not changed + + - name: Create firewall rule for application + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + application: + name: facebOOk + delegate_to: localhost + register: application_rule + + - assert: + that: + - application_rule is changed + - application_rule.data.rules is defined + + - name: Create firewall rule for application via ID + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + application: + id: meraki:layer7/application/205 + delegate_to: localhost + register: application_rule_id + + - assert: + that: + - application_rule_id is changed + + - name: Create firewall rule for invalid application + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + application: + name: ansible + delegate_to: localhost + register: application_rule_invalid + ignore_errors: yes + + - name: Create firewall rule for application category + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + id: meraki:layer7/category/24 + delegate_to: localhost + register: application_category_rule + + - assert: + that: + - application_category_rule is changed + + - name: Create firewall rule for application category with ID + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + id: meraki:layer7/application/205 + delegate_to: localhost + register: application_category_rule_id + + - assert: + that: + - application_category_rule_id is changed + + - name: Create firewall rule for host + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: host + host: asdf.com + delegate_to: localhost + register: host_rule + + - assert: + that: + - host_rule is changed + + - name: Create firewall rule for port + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: port + port: 1234 + delegate_to: localhost + register: port_rule + + - assert: + that: + - port_rule is changed + + - name: Create firewall rule for blacklisted countries + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: blacklisted_countries + countries: + - CA + - AX + delegate_to: localhost + register: blacklist_countries + + - assert: + that: + - blacklist_countries is changed + + - name: Create firewall rule for whitelisted countries + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + countries: + - US + - FR + delegate_to: localhost + register: whitelist_countries + + - assert: + that: + - whitelist_countries is changed + + - name: Create multiple firewall rules + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + countries: + - US + - FR + - type: blacklisted_countries + countries: + - CN + - policy: deny + type: port + port: 8080 + delegate_to: localhost + register: multiple_rules + + - debug: + var: multiple_rules + + - assert: + that: + - multiple_rules.data.rules | length == 3 + - multiple_rules is changed + From f8c13678b04076ca936708380115638536a72859 Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Thu, 18 Apr 2019 09:17:30 -0500 Subject: [PATCH 02/10] Add argument checking --- .../network/meraki/meraki_mx_l7_firewall.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index 2f27dc19a3d673..f447359ef56ab8 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -324,6 +324,7 @@ def main(): categories=dict(type='bool'), ) + # seed the result dict in the object # we primarily care about changed and state # change is if this module effectively modified the target @@ -336,11 +337,27 @@ def main(): # this includes instantiation, a couple of common attr would be the # args/params passed to the execution, as well as if the module # supports check mode - module = AnsibleModule(argument_spec=argument_spec, + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, ) meraki = MerakiModule(module, function='mx_l7_firewall') + # check for argument completeness + if meraki.params['rules']: + for rule in meraki.params['rules']: + if rule['type'] == 'application' and rule['application'] is None: + meraki.fail_json(msg="application argument is required when type is application.") + elif rule['type'] == 'application_category' and rule['application'] is None: + meraki.fail_json(msg="application argument is required when type is application_category.") + elif rule['type'] == 'blacklisted_countries' and rule['countries'] is None: + meraki.fail_json(msg="countries argument is required when type is blacklisted_countries.") + elif rule['type'] == 'host' and rule['host'] is None: + meraki.fail_json(msg="host argument is required when type is host.") + elif rule['type'] == 'port' and rule['port'] is None: + meraki.fail_json(msg="port argument is required when type is port.") + elif rule['whitelisted_countries'] == 'port' and rule['countries'] is None: + meraki.fail_json(msg="countries argument is required when type is whitelisted_countries.") + meraki.params['follow_redirects'] = 'all' query_urls = {'mx_l7_firewall': '/networks/{net_id}/l7FirewallRules/'} From 9698213b6ad335ed93921b794a1b4ded008d93ae Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Thu, 18 Apr 2019 09:34:29 -0500 Subject: [PATCH 03/10] Sanity fixes --- lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py | 3 +-- test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index f447359ef56ab8..5d27094f1109a1 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -324,7 +324,6 @@ def main(): categories=dict(type='bool'), ) - # seed the result dict in the object # we primarily care about changed and state # change is if this module effectively modified the target @@ -337,7 +336,7 @@ def main(): # this includes instantiation, a couple of common attr would be the # args/params passed to the execution, as well as if the module # supports check mode - module = AnsibleModule(argument_spec=argument_spec, + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, ) meraki = MerakiModule(module, function='mx_l7_firewall') diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml index fab5f77211b55b..6f263d663932c2 100644 --- a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -330,4 +330,3 @@ that: - multiple_rules.data.rules | length == 3 - multiple_rules is changed - From 53eb7bfd545053275dd0fee23a433043ada01f11 Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Fri, 24 May 2019 21:33:34 -0500 Subject: [PATCH 04/10] Fix crash and improve integration tests --- .../network/meraki/meraki_mx_l7_firewall.py | 13 +++- .../meraki_mx_l7_firewall/tasks/main.yml | 76 +++++++++++-------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index 5d27094f1109a1..23e1145794cc2c 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -190,7 +190,7 @@ returned: success type: string sample: 80 - applicationCategories: + application_categories: description: List of application categories and applications. type: list returned: success, when querying applications @@ -225,6 +225,7 @@ from ansible.module_utils.basic import AnsibleModule, json, env_fallback from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_native +from ansible.module_utils.common.dict_transformations import recursive_diff from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec @@ -354,7 +355,7 @@ def main(): meraki.fail_json(msg="host argument is required when type is host.") elif rule['type'] == 'port' and rule['port'] is None: meraki.fail_json(msg="port argument is required when type is port.") - elif rule['whitelisted_countries'] == 'port' and rule['countries'] is None: + elif rule['type'] == 'whitelisted_countries' and rule['countries'] is None: meraki.fail_json(msg="countries argument is required when type is whitelisted_countries.") meraki.params['follow_redirects'] = 'all' @@ -401,11 +402,19 @@ def main(): payload = dict() if meraki.is_update_required(rules, payload): if meraki.module.check_mode is True: + diff = recursive_diff(rules, payload) + meraki.result['diff'] = {'before': diff[0], + 'after': diff[1], + } meraki.result['data'] = payload meraki.result['changed'] = False meraki.exit_json(**meraki.result) response = meraki.request(path, method='PUT', payload=json.dumps(payload)) if meraki.status == 200: + diff = recursive_diff(rules, payload) + meraki.result['diff'] = {'before': diff[0], + 'after': diff[1], + } meraki.result['data'] = response meraki.result['changed'] = True else: diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml index 6f263d663932c2..d982db23d0096f 100644 --- a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -4,37 +4,37 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) --- - block: - # - name: Test an API key is provided - # fail: - # msg: Please define an API key - # when: auth_key is not defined + - name: Test an API key is provided + fail: + msg: Please define an API key + when: auth_key is not defined - # - name: Use an invalid domain - # meraki_organization: - # auth_key: '{{ auth_key }}' - # host: marrrraki.com - # state: present - # org_name: IntTestOrg - # output_level: debug - # delegate_to: localhost - # register: invalid_domain - # ignore_errors: yes + - name: Use an invalid domain + meraki_organization: + auth_key: '{{ auth_key }}' + host: marrrraki.com + state: present + org_name: IntTestOrg + output_level: debug + delegate_to: localhost + register: invalid_domain + ignore_errors: yes - # - name: Disable HTTP - # meraki_organization: - # auth_key: '{{ auth_key }}' - # use_https: false - # state: query - # output_level: debug - # delegate_to: localhost - # register: http - # ignore_errors: yes - - # - name: Connection assertions - # assert: - # that: - # - '"Failed to connect to" in invalid_domain.msg' - # - '"http" in http.url' + - name: Disable HTTP + meraki_organization: + auth_key: '{{ auth_key }}' + use_https: false + state: query + output_level: debug + delegate_to: localhost + register: http + ignore_errors: yes + + - name: Connection assertions + assert: + that: + - '"Failed to connect to" in invalid_domain.msg' + - '"http" in http.url' - name: Create network meraki_network: @@ -131,7 +131,6 @@ register: create_ip_range_idempotent_check check_mode: yes - - assert: that: - create_ip_range_idempotent_check is not changed @@ -161,7 +160,7 @@ rules: - type: application application: - name: facebOOk + name: facebook delegate_to: localhost register: application_rule @@ -210,10 +209,13 @@ rules: - type: application_category application: - id: meraki:layer7/category/24 + name: Advertising delegate_to: localhost register: application_category_rule + - debug: + var: application_category_rule + - assert: that: - application_category_rule is changed @@ -227,7 +229,7 @@ rules: - type: application_category application: - id: meraki:layer7/application/205 + id: meraki:layer7/category/27 delegate_to: localhost register: application_category_rule_id @@ -330,3 +332,11 @@ that: - multiple_rules.data.rules | length == 3 - multiple_rules is changed + always: + - name: Create network + meraki_network: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: absent + delegate_to: localhost From b2cc5eb1c89dbff210e1266916ee6e9635eb6f11 Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Fri, 31 May 2019 11:06:22 -0500 Subject: [PATCH 05/10] Improved integration tests and coverage --- .../network/meraki/meraki_mx_l7_firewall.py | 2 +- .../meraki_mx_l7_firewall/tasks/main.yml | 118 ++++++++++++++++-- 2 files changed, 112 insertions(+), 8 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index 23e1145794cc2c..dbd8ef7b36c5b7 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -407,7 +407,7 @@ def main(): 'after': diff[1], } meraki.result['data'] = payload - meraki.result['changed'] = False + meraki.result['changed'] = True meraki.exit_json(**meraki.result) response = meraki.request(path, method='PUT', payload=json.dumps(payload)) if meraki.status == 200: diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml index d982db23d0096f..52744790b7c4e3 100644 --- a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -86,7 +86,7 @@ state: present rules: - type: ip_range - ip_range: 10.11.12.0/24 + ip_range: 10.11.12.0/24 delegate_to: localhost register: create_ip_range_check check_mode: yes @@ -96,7 +96,7 @@ - assert: that: - - create_ip_range_check is not changed + - create_ip_range_check is changed - name: Create firewall rule for IP range meraki_mx_l7_firewall: @@ -312,10 +312,9 @@ net_name: TestNetAppliance state: present rules: - - type: whitelisted_countries - countries: - - US - - FR + - type: application_category + application: + id: meraki:layer7/category/27 - type: blacklisted_countries countries: - CN @@ -332,8 +331,113 @@ that: - multiple_rules.data.rules | length == 3 - multiple_rules is changed + + ######################################### + ## Tests for argument completeness ## + ######################################### + + - name: Test whitelisted_countries incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + delegate_to: localhost + register: error_whitelist + ignore_errors: yes + + - assert: + that: + - 'error_whitelist.msg == "countries argument is required when type is whitelisted_countries."' + + - name: Test blacklisted_countries incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: blacklisted_countries + delegate_to: localhost + register: error_blacklist + ignore_errors: yes + + - assert: + that: + - 'error_blacklist.msg == "countries argument is required when type is blacklisted_countries."' + + - name: Test application_category incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + delegate_to: localhost + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "application argument is required when type is application_category."' + + - name: Test application incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + delegate_to: localhost + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "application argument is required when type is application."' + + - name: Test host incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: host + delegate_to: localhost + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "host argument is required when type is host."' + + - name: Test port incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: port + delegate_to: localhost + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "port argument is required when type is port."' + + ################# + ## Cleanup ## + ################# + always: - - name: Create network + - name: Delete network meraki_network: auth_key: '{{ auth_key }}' org_name: '{{test_org_name}}' From 78917950528b6181b2049a611864382ec1c3c168 Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Sat, 8 Jun 2019 00:02:46 -0500 Subject: [PATCH 06/10] Reformat response data - Module does not match argument structure of API - PR reformats the response to match Ansible arg spec - Improved integration tests --- .../network/meraki/meraki_mx_l7_firewall.py | 72 ++++++++++++-- .../meraki_mx_l7_firewall/tasks/main.yml | 93 ++++++++++++++++++- 2 files changed, 153 insertions(+), 12 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index dbd8ef7b36c5b7..9d37105f3d6b1a 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -95,6 +95,7 @@ ip_range: description: - CIDR notation range of IP addresses to apply rule to. + - Port can be appended to range with a C(":"). type: str port: description: @@ -183,13 +184,54 @@ returned: success type: string sample: applications - value: - description: - - Matching value based on type. - - Returned type will vary. + applications: + description: List of applications within a category. + type: list + contains: + id: + description: URI of application. + returned: success + type: string + sample: Gmail + name: + description: Descriptive name of application. + returned: success + type: string + sample: meraki:layer7/application/4 + applicationCategory: + description: List of application categories within a category. + type: list + contains: + id: + description: URI of application. + returned: success + type: string + sample: Gmail + name: + description: Descriptive name of application. + returned: success + type: string + sample: meraki:layer7/application/4 + port: + description: Port number in rule. returned: success type: string - sample: 80 + sample: 23 + ipRange: + description: Range of IP addresses in rule. + returned: success + type: string + sample: 1.1.1.0/23 + whitelistedCountries: + description: Countries to be whitelisted. + returned: success + type: string + sample: CA + blacklistedCountries: + description: Countries to be blacklisted. + returned: success + type: string + sample: RU application_categories: description: List of application categories and applications. type: list @@ -221,6 +263,7 @@ sample: layer7/category/1 ''' +import copy import os from ansible.module_utils.basic import AnsibleModule, json, env_fallback from ansible.module_utils.urls import fetch_url @@ -287,6 +330,14 @@ def assemble_payload(meraki, net_id, rule): return new_rule +def restructure_response(rules): + for rule in rules['rules']: + type = rule['type'] + rule[type] = copy.deepcopy(rule['value']) + del rule['value'] + return rules + + def get_rules(meraki, net_id): path = meraki.construct_path('get_all', net_id=net_id) response = meraki.request(path, method='GET') @@ -390,7 +441,7 @@ def main(): if meraki.params['categories'] is True: # Output only applications meraki.result['data'] = get_applications(meraki, net_id) else: - meraki.result['data'] = get_rules(meraki, net_id) + meraki.result['data'] = restructure_response(get_rules(meraki, net_id)) elif meraki.params['state'] == 'present': rules = get_rules(meraki, net_id) path = meraki.construct_path('get_all', net_id=net_id) @@ -402,16 +453,19 @@ def main(): payload = dict() if meraki.is_update_required(rules, payload): if meraki.module.check_mode is True: - diff = recursive_diff(rules, payload) + response = restructure_response(payload) + # meraki.fail_json(msg="Payload", payload=response) + diff = recursive_diff(rules, response) meraki.result['diff'] = {'before': diff[0], 'after': diff[1], } - meraki.result['data'] = payload + meraki.result['data'] = response meraki.result['changed'] = True meraki.exit_json(**meraki.result) response = meraki.request(path, method='PUT', payload=json.dumps(payload)) + response = restructure_response(response) if meraki.status == 200: - diff = recursive_diff(rules, payload) + diff = recursive_diff(rules, response) meraki.result['diff'] = {'before': diff[0], 'after': diff[1], } diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml index 52744790b7c4e3..53579f750ddd3f 100644 --- a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -71,9 +71,6 @@ delegate_to: localhost register: query_categories - - debug: - var: query_categories - - assert: that: - query_categories.data is defined @@ -151,6 +148,78 @@ that: - create_ip_range_idempotent is not changed + - name: Create firewall rule for IP and port + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.1:23 + delegate_to: localhost + register: create_ip_range_port + + - debug: + var: create_ip_range_port + + - assert: + that: + - create_ip_range_port is changed + + - name: Create firewall rule for IP range + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range + + - debug: + var: create_ip_range + + - assert: + that: + - create_ip_range is changed + - create_ip_range.data.rules | length == 1 + + - name: Create firewall rule for IP range with idempotency with check mode + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range_idempotent_check + check_mode: yes + + - assert: + that: + - create_ip_range_idempotent_check is not changed + + - name: Create firewall rule for IP range with idempotency + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + delegate_to: localhost + register: create_ip_range_idempotent + + - assert: + that: + - create_ip_range_idempotent is not changed + - name: Create firewall rule for application meraki_mx_l7_firewall: auth_key: '{{ auth_key }}' @@ -305,6 +374,24 @@ that: - whitelist_countries is changed + - name: Create firewall rule for whitelisted countries with idempotency + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + countries: + - US + - FR + delegate_to: localhost + register: whitelist_countries_idempotent + + - assert: + that: + - whitelist_countries_idempotent is not changed + - name: Create multiple firewall rules meraki_mx_l7_firewall: auth_key: '{{ auth_key }}' From c7693938cdc2aefc599076ab3f0e044729e544f7 Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Sat, 8 Jun 2019 00:11:32 -0500 Subject: [PATCH 07/10] Fix reformatting for diffs --- lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index 9d37105f3d6b1a..9379ab50df2295 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -455,7 +455,7 @@ def main(): if meraki.module.check_mode is True: response = restructure_response(payload) # meraki.fail_json(msg="Payload", payload=response) - diff = recursive_diff(rules, response) + diff = recursive_diff(restructure_response(rules), response) meraki.result['diff'] = {'before': diff[0], 'after': diff[1], } @@ -465,7 +465,7 @@ def main(): response = meraki.request(path, method='PUT', payload=json.dumps(payload)) response = restructure_response(response) if meraki.status == 200: - diff = recursive_diff(rules, response) + diff = recursive_diff(restructure_response(rules), response) meraki.result['diff'] = {'before': diff[0], 'after': diff[1], } From e41dcaa5c7dfbe60091d7963bdc3e24c3cdf79de Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Sat, 8 Jun 2019 22:19:16 -0500 Subject: [PATCH 08/10] Create rename_* functions - is_update_required() ignores the key id - Meraki expects one of the keys to be id - Function temporarily renames key to appId to get around this --- .../network/meraki/meraki_mx_l7_firewall.py | 27 +++++++- .../meraki_mx_l7_firewall/tasks/main.yml | 62 ++++++++----------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index 9379ab50df2295..50210cb9313f09 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -345,6 +345,21 @@ def get_rules(meraki, net_id): return response +def rename_id_to_appid(rules): + for rule in rules['rules']: + print(rule['type']) + if rule['type'] == 'application' or rule['type'] == 'applicationCategory': + rule['value']['appId'] = rule['value'].pop('id') + return rules + + +def rename_appid_to_id(rules): + for rule in rules['rules']: + if rule['type'] == 'application' or rule['type'] == 'applicationCategory': + rule['value']['id'] = rule['value'].pop('appId') + return rules + + def main(): # define the available arguments/parameters that a user can pass to # the module @@ -451,10 +466,18 @@ def main(): payload['rules'].append(assemble_payload(meraki, net_id, rule)) else: payload = dict() + + ''' + The rename_* functions are needed because the key is id and + is_update_required() by default ignores id. + ''' + rules = rename_id_to_appid(rules) + payload = rename_id_to_appid(payload) if meraki.is_update_required(rules, payload): + rules = rename_appid_to_id(rules) + payload = rename_appid_to_id(payload) if meraki.module.check_mode is True: response = restructure_response(payload) - # meraki.fail_json(msg="Payload", payload=response) diff = recursive_diff(restructure_response(rules), response) meraki.result['diff'] = {'before': diff[0], 'after': diff[1], @@ -472,6 +495,8 @@ def main(): meraki.result['data'] = response meraki.result['changed'] = True else: + rules = rename_appid_to_id(rules) + payload = rename_appid_to_id(payload) if meraki.module.check_mode is True: meraki.result['data'] = rules meraki.result['changed'] = False diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml index 53579f750ddd3f..0750d13f1976a1 100644 --- a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -9,33 +9,6 @@ msg: Please define an API key when: auth_key is not defined - - name: Use an invalid domain - meraki_organization: - auth_key: '{{ auth_key }}' - host: marrrraki.com - state: present - org_name: IntTestOrg - output_level: debug - delegate_to: localhost - register: invalid_domain - ignore_errors: yes - - - name: Disable HTTP - meraki_organization: - auth_key: '{{ auth_key }}' - use_https: false - state: query - output_level: debug - delegate_to: localhost - register: http - ignore_errors: yes - - - name: Connection assertions - assert: - that: - - '"Failed to connect to" in invalid_domain.msg' - - '"http" in http.url' - - name: Create network meraki_network: auth_key: '{{ auth_key }}' @@ -289,7 +262,7 @@ that: - application_category_rule is changed - - name: Create firewall rule for application category with ID + - name: Create firewall rule for application category with ID and conflict meraki_mx_l7_firewall: auth_key: '{{ auth_key }}' org_name: '{{test_org_name}}' @@ -300,6 +273,23 @@ application: id: meraki:layer7/category/27 delegate_to: localhost + register: application_category_rule_id_conflict + + - assert: + that: + - application_category_rule_id_conflict is not changed + + - name: Create firewall rule for application category with ID + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + id: meraki:layer7/category/24 + delegate_to: localhost register: application_category_rule_id - assert: @@ -523,11 +513,11 @@ ## Cleanup ## ################# - always: - - name: Delete network - meraki_network: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: absent - delegate_to: localhost + # always: + # - name: Delete network + # meraki_network: + # auth_key: '{{ auth_key }}' + # org_name: '{{test_org_name}}' + # net_name: TestNetAppliance + # state: absent + # delegate_to: localhost From 380757c50cebcc70d65e52a210627222809d941a Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Wed, 19 Jun 2019 16:38:04 -0500 Subject: [PATCH 09/10] Tweak documentation and tests - Tests now do an include so each task doesn't need delegate_to --- .../meraki_mx_l7_firewall/tasks/tests.yml | 494 ++++++++++++++++++ .../targets/meraki_snmp/tasks/main.yml | 175 +------ 2 files changed, 496 insertions(+), 173 deletions(-) create mode 100644 test/integration/targets/meraki_mx_l7_firewall/tasks/tests.yml diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/tests.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/tests.yml new file mode 100644 index 00000000000000..72adef560ec07d --- /dev/null +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/tests.yml @@ -0,0 +1,494 @@ +# Test code for the Meraki Organization module +# Copyright: (c) 2018, Kevin Breit (@kbreit) + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- block: + - name: Test an API key is provided + fail: + msg: Please define an API key + when: auth_key is not defined + + - name: Create network + meraki_network: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + type: appliance + + - name: Query firewall rules + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: query + register: query + + - debug: + var: query + + - assert: + that: + - query.data is defined + + - name: Query firewall application categories + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: query + categories: yes + register: query_categories + + - assert: + that: + - query_categories.data is defined + + - name: Create firewall rule for IP range in check mode + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range_check + check_mode: yes + + - debug: + var: create_ip_range_check + + - assert: + that: + - create_ip_range_check is changed + + - name: Create firewall rule for IP range + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range + + - debug: + var: create_ip_range + + - assert: + that: + - create_ip_range is changed + - create_ip_range.data.rules | length == 1 + + - name: Create firewall rule for IP range with idempotency with check mode + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range_idempotent_check + check_mode: yes + + - assert: + that: + - create_ip_range_idempotent_check is not changed + + - name: Create firewall rule for IP range with idempotency + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range_idempotent + + - assert: + that: + - create_ip_range_idempotent is not changed + + - name: Create firewall rule for IP and port + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.1:23 + register: create_ip_range_port + + - debug: + var: create_ip_range_port + + - assert: + that: + - create_ip_range_port is changed + + - name: Create firewall rule for IP range + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range + + - debug: + var: create_ip_range + + - assert: + that: + - create_ip_range is changed + - create_ip_range.data.rules | length == 1 + + - name: Create firewall rule for IP range with idempotency with check mode + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range_idempotent_check + check_mode: yes + + - assert: + that: + - create_ip_range_idempotent_check is not changed + + - name: Create firewall rule for IP range with idempotency + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: ip_range + ip_range: 10.11.12.0/24 + register: create_ip_range_idempotent + + - assert: + that: + - create_ip_range_idempotent is not changed + + - name: Create firewall rule for application + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + application: + name: facebook + register: application_rule + + - assert: + that: + - application_rule is changed + - application_rule.data.rules is defined + + - name: Create firewall rule for application via ID + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + application: + id: meraki:layer7/application/205 + register: application_rule_id + + - assert: + that: + - application_rule_id is changed + + - name: Create firewall rule for invalid application + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + application: + name: ansible + register: application_rule_invalid + ignore_errors: yes + + - name: Create firewall rule for application category + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + name: Advertising + register: application_category_rule + + - debug: + var: application_category_rule + + - assert: + that: + - application_category_rule is changed + + - name: Create firewall rule for application category with ID and conflict + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + id: meraki:layer7/category/27 + register: application_category_rule_id_conflict + + - assert: + that: + - application_category_rule_id_conflict is not changed + + - name: Create firewall rule for application category with ID + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + id: meraki:layer7/category/24 + register: application_category_rule_id + + - assert: + that: + - application_category_rule_id is changed + + - name: Create firewall rule for host + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: host + host: asdf.com + register: host_rule + + - assert: + that: + - host_rule is changed + + - name: Create firewall rule for port + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: port + port: 1234 + register: port_rule + + - assert: + that: + - port_rule is changed + + - name: Create firewall rule for blacklisted countries + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: blacklisted_countries + countries: + - CA + - AX + register: blacklist_countries + + - assert: + that: + - blacklist_countries is changed + + - name: Create firewall rule for whitelisted countries + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + countries: + - US + - FR + register: whitelist_countries + + - assert: + that: + - whitelist_countries is changed + + - name: Create firewall rule for whitelisted countries with idempotency + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + countries: + - US + - FR + register: whitelist_countries_idempotent + + - assert: + that: + - whitelist_countries_idempotent is not changed + + - name: Create multiple firewall rules + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + application: + id: meraki:layer7/category/27 + - type: blacklisted_countries + countries: + - CN + - policy: deny + type: port + port: 8080 + register: multiple_rules + + - debug: + var: multiple_rules + + - assert: + that: + - multiple_rules.data.rules | length == 3 + - multiple_rules is changed + + ######################################### + ## Tests for argument completeness ## + ######################################### + + - name: Test whitelisted_countries incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: whitelisted_countries + register: error_whitelist + ignore_errors: yes + + - assert: + that: + - 'error_whitelist.msg == "countries argument is required when type is whitelisted_countries."' + + - name: Test blacklisted_countries incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: blacklisted_countries + register: error_blacklist + ignore_errors: yes + + - assert: + that: + - 'error_blacklist.msg == "countries argument is required when type is blacklisted_countries."' + + - name: Test application_category incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application_category + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "application argument is required when type is application_category."' + + - name: Test application incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: application + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "application argument is required when type is application."' + + - name: Test host incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: host + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "host argument is required when type is host."' + + - name: Test port incomplete arguments + meraki_mx_l7_firewall: + auth_key: '{{ auth_key }}' + org_name: '{{test_org_name}}' + net_name: TestNetAppliance + state: present + rules: + - type: port + register: error_app_cat + ignore_errors: yes + + - assert: + that: + - 'error_app_cat.msg == "port argument is required when type is port."' + + ################# + ## Cleanup ## + ################# + + # always: + # - name: Delete network + # meraki_network: + # auth_key: '{{ auth_key }}' + # org_name: '{{test_org_name}}' + # net_name: TestNetAppliance + # state: absent + # delegate_to: localhost diff --git a/test/integration/targets/meraki_snmp/tasks/main.yml b/test/integration/targets/meraki_snmp/tasks/main.yml index 8bdf72e36f752a..721a93007b9a64 100644 --- a/test/integration/targets/meraki_snmp/tasks/main.yml +++ b/test/integration/targets/meraki_snmp/tasks/main.yml @@ -3,176 +3,5 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) --- -- name: Test an API key is provided - fail: - msg: Please define an API key - when: auth_key is not defined - -- name: Query all SNMP settings - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: query - delegate_to: localhost - register: snmp_query - -- debug: - msg: '{{snmp_query}}' - -- name: Enable SNMPv2c - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - v2c_enabled: true - delegate_to: localhost - register: snmp_v2_enable - -- debug: - msg: '{{snmp_v2_enable}}' - -- assert: - that: - - snmp_v2_enable.data.v2CommunityString is defined - - snmp_v2_enable.data.v2cEnabled == true - -- name: Disable SNMPv2c - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - v2c_enabled: False - delegate_to: localhost - register: snmp_v2_disable - -- assert: - that: - - snmp_v2_disable.data.v2CommunityString is not defined - - snmp_v2_disable.data.v2cEnabled == False - -- name: Enable SNMPv2c with org_id - meraki_snmp: - auth_key: '{{auth_key}}' - org_id: '{{test_org_id}}' - state: present - v2c_enabled: true - delegate_to: localhost - register: snmp_v2_enable_id - -- debug: - msg: '{{snmp_v2_enable_id}}' - -- assert: - that: - - snmp_v2_enable_id.data.v2CommunityString is defined - - snmp_v2_enable_id.data.v2cEnabled == true - -- name: Disable SNMPv2c with org_id - meraki_snmp: - auth_key: '{{auth_key}}' - org_id: '{{test_org_id}}' - state: present - v2c_enabled: False - delegate_to: localhost - register: snmp_v2_disable_id - -- assert: - that: - - snmp_v2_disable_id.data.v2CommunityString is not defined - - snmp_v2_disable_id.data.v2cEnabled == False - -- name: Enable SNMPv3 - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - v3_enabled: true - v3_auth_mode: SHA - v3_auth_pass: ansiblepass - v3_priv_mode: AES128 - v3_priv_pass: ansiblepass - delegate_to: localhost - register: snmp_v3_enable - -- assert: - that: - - snmp_v3_enable.data.v3Enabled == True - - snmp_v3_enable.changed == True - -- name: Check for idempotency - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - v3_enabled: true - v3_auth_mode: SHA - v3_auth_pass: ansiblepass - v3_priv_mode: AES128 - v3_priv_pass: ansiblepass - delegate_to: localhost - register: snmp_idempotent - -- debug: - msg: '{{snmp_idempotent}}' - -- assert: - that: - - snmp_idempotent.changed == False - - snmp_idempotent.data is defined - -- name: Add peer IPs - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - v3_enabled: true - v3_auth_mode: SHA - v3_auth_pass: ansiblepass - v3_priv_mode: AES128 - v3_priv_pass: ansiblepass - peer_ips: 1.1.1.1;2.2.2.2 - delegate_to: localhost - register: peers - -- debug: - msg: '{{peers}}' - -- assert: - that: - - peers.data.peerIps is defined - -- name: Add invalid peer IPs - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - peer_ips: 1.1.1.1 2.2.2.2 - delegate_to: localhost - register: invalid_peers - ignore_errors: yes - -- assert: - that: - '"Peer IP addresses are semi-colon delimited." in invalid_peers.msg' - -- name: Set short password - meraki_snmp: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: present - v3_enabled: true - v3_auth_mode: SHA - v3_auth_pass: ansible - v3_priv_mode: AES128 - v3_priv_pass: ansible - peer_ips: 1.1.1.1;2.2.2.2 - delegate_to: localhost - register: short_password - ignore_errors: yes - -- debug: - msg: '{{short_password}}' - -- assert: - that: - - '"at least 8" in short_password.msg' +- name: Run test cases + include: tests.yml ansible_connection=local From cd4e2bafe1b23f68fa0f829caf0cdf546e0d0354 Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Wed, 19 Jun 2019 16:49:10 -0500 Subject: [PATCH 10/10] Fix which files I'm editing --- .../network/meraki/meraki_mx_l7_firewall.py | 14 +- .../meraki_mx_l7_firewall/tasks/main.yml | 520 +----------------- .../targets/meraki_snmp/tasks/main.yml | 175 +++++- 3 files changed, 179 insertions(+), 530 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py index 50210cb9313f09..e30aa4c8ec008d 100644 --- a/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py +++ b/lib/ansible/modules/network/meraki/meraki_mx_l7_firewall.py @@ -26,26 +26,22 @@ options: state: description: - - Create or modify an organization. + - Query or modify a firewall rule. choices: ['present', 'query'] default: present - org_name: - description: - - Name of organization. - - If C(clone) is specified, C(org_name) is the name of the new organization. - org_id: - description: - - ID of organization. type: str net_name: description: - Name of network which MX firewall is in. + type: str net_id: description: - ID of network which MX firewall is in. + type: str rules: description: - List of layer 7 firewall rules. + type: list suboptions: policy: description: @@ -266,8 +262,6 @@ import copy import os from ansible.module_utils.basic import AnsibleModule, json, env_fallback -from ansible.module_utils.urls import fetch_url -from ansible.module_utils._text import to_native from ansible.module_utils.common.dict_transformations import recursive_diff from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec diff --git a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml index 0750d13f1976a1..bb4c6fc5941e7a 100644 --- a/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml +++ b/test/integration/targets/meraki_mx_l7_firewall/tasks/main.yml @@ -3,521 +3,5 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) --- -- block: - - name: Test an API key is provided - fail: - msg: Please define an API key - when: auth_key is not defined - - - name: Create network - meraki_network: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - type: appliance - delegate_to: localhost - - - name: Query firewall rules - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: query - delegate_to: localhost - register: query - - - debug: - var: query - - - assert: - that: - - query.data is defined - - - name: Query firewall application categories - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: query - categories: yes - delegate_to: localhost - register: query_categories - - - assert: - that: - - query_categories.data is defined - - - name: Create firewall rule for IP range in check mode - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range_check - check_mode: yes - - - debug: - var: create_ip_range_check - - - assert: - that: - - create_ip_range_check is changed - - - name: Create firewall rule for IP range - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range - - - debug: - var: create_ip_range - - - assert: - that: - - create_ip_range is changed - - create_ip_range.data.rules | length == 1 - - - name: Create firewall rule for IP range with idempotency with check mode - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range_idempotent_check - check_mode: yes - - - assert: - that: - - create_ip_range_idempotent_check is not changed - - - name: Create firewall rule for IP range with idempotency - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range_idempotent - - - assert: - that: - - create_ip_range_idempotent is not changed - - - name: Create firewall rule for IP and port - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.1:23 - delegate_to: localhost - register: create_ip_range_port - - - debug: - var: create_ip_range_port - - - assert: - that: - - create_ip_range_port is changed - - - name: Create firewall rule for IP range - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range - - - debug: - var: create_ip_range - - - assert: - that: - - create_ip_range is changed - - create_ip_range.data.rules | length == 1 - - - name: Create firewall rule for IP range with idempotency with check mode - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range_idempotent_check - check_mode: yes - - - assert: - that: - - create_ip_range_idempotent_check is not changed - - - name: Create firewall rule for IP range with idempotency - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: ip_range - ip_range: 10.11.12.0/24 - delegate_to: localhost - register: create_ip_range_idempotent - - - assert: - that: - - create_ip_range_idempotent is not changed - - - name: Create firewall rule for application - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application - application: - name: facebook - delegate_to: localhost - register: application_rule - - - assert: - that: - - application_rule is changed - - application_rule.data.rules is defined - - - name: Create firewall rule for application via ID - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application - application: - id: meraki:layer7/application/205 - delegate_to: localhost - register: application_rule_id - - - assert: - that: - - application_rule_id is changed - - - name: Create firewall rule for invalid application - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application - application: - name: ansible - delegate_to: localhost - register: application_rule_invalid - ignore_errors: yes - - - name: Create firewall rule for application category - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application_category - application: - name: Advertising - delegate_to: localhost - register: application_category_rule - - - debug: - var: application_category_rule - - - assert: - that: - - application_category_rule is changed - - - name: Create firewall rule for application category with ID and conflict - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application_category - application: - id: meraki:layer7/category/27 - delegate_to: localhost - register: application_category_rule_id_conflict - - - assert: - that: - - application_category_rule_id_conflict is not changed - - - name: Create firewall rule for application category with ID - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application_category - application: - id: meraki:layer7/category/24 - delegate_to: localhost - register: application_category_rule_id - - - assert: - that: - - application_category_rule_id is changed - - - name: Create firewall rule for host - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: host - host: asdf.com - delegate_to: localhost - register: host_rule - - - assert: - that: - - host_rule is changed - - - name: Create firewall rule for port - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: port - port: 1234 - delegate_to: localhost - register: port_rule - - - assert: - that: - - port_rule is changed - - - name: Create firewall rule for blacklisted countries - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: blacklisted_countries - countries: - - CA - - AX - delegate_to: localhost - register: blacklist_countries - - - assert: - that: - - blacklist_countries is changed - - - name: Create firewall rule for whitelisted countries - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: whitelisted_countries - countries: - - US - - FR - delegate_to: localhost - register: whitelist_countries - - - assert: - that: - - whitelist_countries is changed - - - name: Create firewall rule for whitelisted countries with idempotency - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: whitelisted_countries - countries: - - US - - FR - delegate_to: localhost - register: whitelist_countries_idempotent - - - assert: - that: - - whitelist_countries_idempotent is not changed - - - name: Create multiple firewall rules - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application_category - application: - id: meraki:layer7/category/27 - - type: blacklisted_countries - countries: - - CN - - policy: deny - type: port - port: 8080 - delegate_to: localhost - register: multiple_rules - - - debug: - var: multiple_rules - - - assert: - that: - - multiple_rules.data.rules | length == 3 - - multiple_rules is changed - - ######################################### - ## Tests for argument completeness ## - ######################################### - - - name: Test whitelisted_countries incomplete arguments - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: whitelisted_countries - delegate_to: localhost - register: error_whitelist - ignore_errors: yes - - - assert: - that: - - 'error_whitelist.msg == "countries argument is required when type is whitelisted_countries."' - - - name: Test blacklisted_countries incomplete arguments - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: blacklisted_countries - delegate_to: localhost - register: error_blacklist - ignore_errors: yes - - - assert: - that: - - 'error_blacklist.msg == "countries argument is required when type is blacklisted_countries."' - - - name: Test application_category incomplete arguments - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application_category - delegate_to: localhost - register: error_app_cat - ignore_errors: yes - - - assert: - that: - - 'error_app_cat.msg == "application argument is required when type is application_category."' - - - name: Test application incomplete arguments - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: application - delegate_to: localhost - register: error_app_cat - ignore_errors: yes - - - assert: - that: - - 'error_app_cat.msg == "application argument is required when type is application."' - - - name: Test host incomplete arguments - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: host - delegate_to: localhost - register: error_app_cat - ignore_errors: yes - - - assert: - that: - - 'error_app_cat.msg == "host argument is required when type is host."' - - - name: Test port incomplete arguments - meraki_mx_l7_firewall: - auth_key: '{{ auth_key }}' - org_name: '{{test_org_name}}' - net_name: TestNetAppliance - state: present - rules: - - type: port - delegate_to: localhost - register: error_app_cat - ignore_errors: yes - - - assert: - that: - - 'error_app_cat.msg == "port argument is required when type is port."' - - ################# - ## Cleanup ## - ################# - - # always: - # - name: Delete network - # meraki_network: - # auth_key: '{{ auth_key }}' - # org_name: '{{test_org_name}}' - # net_name: TestNetAppliance - # state: absent - # delegate_to: localhost +- name: Run test cases + include: tests.yml ansible_connection=local \ No newline at end of file diff --git a/test/integration/targets/meraki_snmp/tasks/main.yml b/test/integration/targets/meraki_snmp/tasks/main.yml index 721a93007b9a64..8bdf72e36f752a 100644 --- a/test/integration/targets/meraki_snmp/tasks/main.yml +++ b/test/integration/targets/meraki_snmp/tasks/main.yml @@ -3,5 +3,176 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) --- -- name: Run test cases - include: tests.yml ansible_connection=local +- name: Test an API key is provided + fail: + msg: Please define an API key + when: auth_key is not defined + +- name: Query all SNMP settings + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: query + delegate_to: localhost + register: snmp_query + +- debug: + msg: '{{snmp_query}}' + +- name: Enable SNMPv2c + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + v2c_enabled: true + delegate_to: localhost + register: snmp_v2_enable + +- debug: + msg: '{{snmp_v2_enable}}' + +- assert: + that: + - snmp_v2_enable.data.v2CommunityString is defined + - snmp_v2_enable.data.v2cEnabled == true + +- name: Disable SNMPv2c + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + v2c_enabled: False + delegate_to: localhost + register: snmp_v2_disable + +- assert: + that: + - snmp_v2_disable.data.v2CommunityString is not defined + - snmp_v2_disable.data.v2cEnabled == False + +- name: Enable SNMPv2c with org_id + meraki_snmp: + auth_key: '{{auth_key}}' + org_id: '{{test_org_id}}' + state: present + v2c_enabled: true + delegate_to: localhost + register: snmp_v2_enable_id + +- debug: + msg: '{{snmp_v2_enable_id}}' + +- assert: + that: + - snmp_v2_enable_id.data.v2CommunityString is defined + - snmp_v2_enable_id.data.v2cEnabled == true + +- name: Disable SNMPv2c with org_id + meraki_snmp: + auth_key: '{{auth_key}}' + org_id: '{{test_org_id}}' + state: present + v2c_enabled: False + delegate_to: localhost + register: snmp_v2_disable_id + +- assert: + that: + - snmp_v2_disable_id.data.v2CommunityString is not defined + - snmp_v2_disable_id.data.v2cEnabled == False + +- name: Enable SNMPv3 + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + v3_enabled: true + v3_auth_mode: SHA + v3_auth_pass: ansiblepass + v3_priv_mode: AES128 + v3_priv_pass: ansiblepass + delegate_to: localhost + register: snmp_v3_enable + +- assert: + that: + - snmp_v3_enable.data.v3Enabled == True + - snmp_v3_enable.changed == True + +- name: Check for idempotency + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + v3_enabled: true + v3_auth_mode: SHA + v3_auth_pass: ansiblepass + v3_priv_mode: AES128 + v3_priv_pass: ansiblepass + delegate_to: localhost + register: snmp_idempotent + +- debug: + msg: '{{snmp_idempotent}}' + +- assert: + that: + - snmp_idempotent.changed == False + - snmp_idempotent.data is defined + +- name: Add peer IPs + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + v3_enabled: true + v3_auth_mode: SHA + v3_auth_pass: ansiblepass + v3_priv_mode: AES128 + v3_priv_pass: ansiblepass + peer_ips: 1.1.1.1;2.2.2.2 + delegate_to: localhost + register: peers + +- debug: + msg: '{{peers}}' + +- assert: + that: + - peers.data.peerIps is defined + +- name: Add invalid peer IPs + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + peer_ips: 1.1.1.1 2.2.2.2 + delegate_to: localhost + register: invalid_peers + ignore_errors: yes + +- assert: + that: + '"Peer IP addresses are semi-colon delimited." in invalid_peers.msg' + +- name: Set short password + meraki_snmp: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: present + v3_enabled: true + v3_auth_mode: SHA + v3_auth_pass: ansible + v3_priv_mode: AES128 + v3_priv_pass: ansible + peer_ips: 1.1.1.1;2.2.2.2 + delegate_to: localhost + register: short_password + ignore_errors: yes + +- debug: + msg: '{{short_password}}' + +- assert: + that: + - '"at least 8" in short_password.msg'