meraki_snmp - Add support for network SNMP settings (#57708)

* meraki_snmp module supports network SNMP settings
- Network SNMP settings were added to the API
- Parameters are different so it's a new data structure
- Full suite of integration tests
- Commit includes some cleanup as well

* Add reset task for SNMPv3
This commit is contained in:
Kevin Breit 2019-07-31 10:29:57 -05:00 committed by Nathaniel Case
parent 516b39b79a
commit d105c205ef
2 changed files with 420 additions and 214 deletions

View file

@ -53,6 +53,43 @@ options:
peer_ips:
description:
- Semi-colon delimited IP addresses which can perform SNMP queries.
net_name:
description:
- Name of network.
type: str
version_added: '2.9'
net_id:
description:
- ID of network.
type: str
version_added: '2.9'
access:
description:
- Type of SNMP access.
choices: [community, none, users]
type: str
version_added: '2.9'
community_string:
description:
- SNMP community string.
- Only relevant if C(access) is set to C(community).
type: str
version_added: '2.9'
users:
description:
- Information about users with access to SNMP.
- Only relevant if C(access) is set to C(users).
type: list
version_added: '2.9'
suboptions:
username:
description: Username of user with access.
type: str
version_added: '2.9'
passphrase:
description: Passphrase for user SNMP access.
type: str
version_added: '2.9'
author:
- Kevin Breit (@kbreit)
extends_documentation_fragment: meraki
@ -94,6 +131,28 @@ EXAMPLES = r'''
v3_priv_pass: ansiblepass
peer_ips: 192.0.1.1;192.0.1.2
delegate_to: localhost
- name: Set network access type to community string
meraki_snmp:
auth_key: abc1235
org_name: YourOrg
net_name: YourNet
state: present
access: community
community_string: abc123
delegate_to: localhost
- name: Set network access type to username
meraki_snmp:
auth_key: abc1235
org_name: YourOrg
net_name: YourNet
state: present
access: users
users:
- username: ansibleuser
passphrase: ansiblepass
delegate_to: localhost
'''
RETURN = r'''
@ -104,56 +163,76 @@ data:
contains:
hostname:
description: Hostname of SNMP server.
returned: success
returned: success and no network specified.
type: str
sample: n1.meraki.com
peerIps:
description: Semi-colon delimited list of IPs which can poll SNMP information.
returned: success
returned: success and no network specified.
type: str
sample: 192.0.1.1
port:
description: Port number of SNMP.
returned: success
returned: success and no network specified.
type: str
sample: 16100
v2c_enabled:
description: Shows enabled state of SNMPv2c
returned: success
returned: success and no network specified.
type: bool
sample: true
v3_enabled:
description: Shows enabled state of SNMPv3
returned: success
returned: success and no network specified.
type: bool
sample: true
v3_auth_mode:
description: The SNMP version 3 authentication mode either MD5 or SHA.
returned: success
returned: success and no network specified.
type: str
sample: SHA
v3_priv_mode:
description: The SNMP version 3 privacy mode DES or AES128.
returned: success
returned: success and no network specified.
type: str
sample: AES128
v2_community_string:
description: Automatically generated community string for SNMPv2c.
returned: When SNMPv2c is enabled.
returned: When SNMPv2c is enabled and no network specified.
type: str
sample: o/8zd-JaSb
v3_user:
description: Automatically generated username for SNMPv3.
returned: When SNMPv3c is enabled.
returned: When SNMPv3c is enabled and no network specified.
type: str
sample: o/8zd-JaSb
access:
description: Type of SNMP access.
type: str
returned: success, when network specified
community_string:
description: SNMP community string. Only relevant if C(access) is set to C(community).
type: str
returned: success, when network specified
users:
description: Information about users with access to SNMP. Only relevant if C(access) is set to C(users).
type: complex
contains:
username:
description: Username of user with access.
type: str
returned: success, when network specified
passphrase:
description: Passphrase for user SNMP access.
type: str
returned: success, when network specified
'''
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.common.dict_transformations import recursive_diff, snake_dict_to_camel_dict
from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec
@ -175,7 +254,7 @@ def set_snmp(meraki, org_id):
if meraki.params['v2c_enabled'] is not None:
payload = {'v2cEnabled': meraki.params['v2c_enabled'],
}
if meraki.params['v3_enabled'] is not None:
if meraki.params['v3_enabled'] is True:
if len(meraki.params['v3_auth_pass']) < 8 or len(meraki.params['v3_priv_pass']) < 8:
meraki.fail_json(msg='v3_auth_pass and v3_priv_pass must both be at least 8 characters long.')
if (meraki.params['v3_auth_mode'] is None or
@ -188,18 +267,12 @@ def set_snmp(meraki, org_id):
'v3AuthPass': meraki.params['v3_auth_pass'],
'v3PrivMode': meraki.params['v3_priv_mode'].upper(),
'v3PrivPass': meraki.params['v3_priv_pass'],
'peerIps': meraki.params['peer_ips'],
}
full_compare = {'v2cEnabled': meraki.params['v2c_enabled'],
'v3Enabled': meraki.params['v3_enabled'],
'v3AuthMode': meraki.params['v3_auth_mode'],
'v3PrivMode': meraki.params['v3_priv_mode'],
'peerIps': meraki.params['peer_ips'],
}
if meraki.params['v3_enabled'] is None:
full_compare['v3Enabled'] = False
if meraki.params['v2c_enabled'] is None:
full_compare['v2cEnabled'] = False
if meraki.params['peer_ips'] is not None:
payload['peerIps'] = meraki.params['peer_ips']
elif meraki.params['v3_enabled'] is False:
payload = {'v3Enabled': False}
full_compare = snake_dict_to_camel_dict(payload)
path = meraki.construct_path('create', org_id=org_id)
snmp = get_snmp(meraki, org_id)
ignored_parameters = ['v3AuthPass', 'v3PrivPass', 'hostname', 'port', 'v2CommunityString', 'v3User']
@ -208,6 +281,7 @@ def set_snmp(meraki, org_id):
diff = recursive_diff(snmp, full_compare)
snmp.update(payload)
meraki.result['data'] = snmp
meraki.result['changed'] = True
meraki.result['diff'] = {'before': diff[0],
'after': diff[1]}
meraki.exit_json(**meraki.result)
@ -228,6 +302,10 @@ def main():
# define the available arguments/parameters that a user can pass to
# the module
user_arg_spec = dict(username=dict(type='str'),
passphrase=dict(type='str', no_log=True),
)
argument_spec = meraki_argument_spec()
argument_spec.update(state=dict(type='str', choices=['present', 'query'], default='present'),
v2c_enabled=dict(type='bool'),
@ -237,6 +315,11 @@ def main():
v3_priv_mode=dict(type='str', choices=['DES', 'AES128']),
v3_priv_pass=dict(type='str', no_log=True),
peer_ips=dict(type='str'),
access=dict(type='str', choices=['none', 'community', 'users']),
community_string=dict(type='str', no_log=True),
users=dict(type='list', default=None, element='str', options=user_arg_spec),
net_name=dict(type='str'),
net_id=dict(type='str'),
)
# seed the result dict in the object
@ -257,36 +340,62 @@ def main():
meraki = MerakiModule(module, function='snmp')
meraki.params['follow_redirects'] = 'all'
query_urls = {'snmp': '/organizations/{org_id}/snmp',
}
query_urls = {'snmp': '/organizations/{org_id}/snmp'}
query_net_urls = {'snmp': '/networks/{net_id}/snmpSettings'}
update_urls = {'snmp': '/organizations/{org_id}/snmp'}
update_net_urls = {'snmp': '/networks/{net_id}/snmpSettings'}
update_urls = {'snmp': '/organizations/{org_id}/snmp',
}
meraki.url_catalog['get_all'] = query_urls
meraki.url_catalog['get_all'].update(query_urls)
meraki.url_catalog['query_net_all'] = query_net_urls
meraki.url_catalog['create'] = update_urls
meraki.url_catalog['create_net'] = update_net_urls
payload = None
# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
# execute checks for argument completeness
# manipulate or modify the state as needed (this is going to be the
# part where your module will do what it needs to do)
if not meraki.params['org_name'] and not meraki.params['org_id']:
meraki.fail_json(msg='org_name or org_id is required')
org_id = meraki.params['org_id']
if org_id is None:
org_id = meraki.get_org_id(meraki.params['org_name'])
net_id = meraki.params['net_id']
if net_id is None and meraki.params['net_name']:
nets = meraki.get_nets(org_id=org_id)
net_id = meraki.get_net_id(org_id, meraki.params['net_name'], data=nets)
if meraki.params['state'] == 'present':
if net_id is not None:
payload = {'access': meraki.params['access']}
if meraki.params['community_string'] is not None:
payload['communityString'] = meraki.params['community_string']
elif meraki.params['users'] is not None:
payload['users'] = meraki.params['users']
if meraki.params['state'] == 'query':
meraki.result['data'] = get_snmp(meraki, org_id)
if net_id is None:
meraki.result['data'] = get_snmp(meraki, org_id)
else:
path = meraki.construct_path('query_net_all', net_id=net_id)
response = meraki.request(path, method='GET')
if meraki.status == 200:
meraki.result['data'] = response
elif meraki.params['state'] == 'present':
meraki.result['data'] = set_snmp(meraki, org_id)
if net_id is None:
meraki.result['data'] = set_snmp(meraki, org_id)
else:
path = meraki.construct_path('query_net_all', net_id=net_id)
original = meraki.request(path, method='GET')
if meraki.is_update_required(original, payload):
path = meraki.construct_path('create_net', net_id=net_id)
response = meraki.request(path, method='PUT', payload=json.dumps(payload))
if meraki.status == 200:
if response['access'] == 'none':
meraki.result['data'] = {}
else:
meraki.result['data'] = response
meraki.result['changed'] = True
else:
meraki.result['data'] = original
# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results

View file

@ -3,207 +3,304 @@
# 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
- block:
- 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
- name: Create SNMP network
meraki_network:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
net_name: '{{test_net_name}}'
state: present
type: appliance
delegate_to: localhost
register: new_net
- debug:
msg: '{{snmp_query}}'
- set_fact:
net_id: new_net.data.id
- name: Enable SNMPv2c with check mode
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
state: present
v2c_enabled: true
delegate_to: localhost
register: snmp_v2_enable_check
check_mode: yes
- name: Query all SNMP settings
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
state: query
delegate_to: localhost
register: snmp_query
- assert:
that:
- snmp_v2_enable_check is not changed
- 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
- 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}}'
- debug:
msg: '{{snmp_v2_enable}}'
- assert:
that:
- snmp_v2_enable.data.v2_community_string is defined
- snmp_v2_enable.data.v2c_enabled == true
- assert:
that:
- snmp_v2_enable.data.v2_community_string is defined
- snmp_v2_enable.data.v2c_enabled == 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
- 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.v2_community_string is not defined
- snmp_v2_disable.data.v2c_enabled == False
- assert:
that:
- snmp_v2_disable.data.v2_community_string is not defined
- snmp_v2_disable.data.v2c_enabled == 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
- 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}}'
- debug:
msg: '{{snmp_v2_enable_id}}'
- assert:
that:
- snmp_v2_enable_id.data.v2_community_string is defined
- snmp_v2_enable_id.data.v2c_enabled == true
- assert:
that:
- snmp_v2_enable_id.data.v2_community_string is defined
- snmp_v2_enable_id.data.v2c_enabled == 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
- 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.v2_community_string is not defined
- snmp_v2_disable_id.data.v2c_enabled == False
- assert:
that:
- snmp_v2_disable_id.data.v2_community_string is not defined
- snmp_v2_disable_id.data.v2c_enabled == 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
- name: Enable SNMPv3 with check mode
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
check_mode: yes
register: snmp_v3_enable_check
- assert:
that:
- snmp_v3_enable.data.v3_enabled == True
- snmp_v3_enable.changed == True
- assert:
that:
- snmp_v3_enable_check.data.v3_enabled == True
- snmp_v3_enable_check.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
- 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
- debug:
msg: '{{snmp_idempotent}}'
- assert:
that:
- snmp_v3_enable.data.v3_enabled == True
- snmp_v3_enable.changed == True
- assert:
that:
- snmp_idempotent.changed == False
- snmp_idempotent.data is defined
- 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
- name: Check for idempotency with check mode
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_check
- debug:
msg: '{{snmp_idempotent}}'
- assert:
that:
- snmp_idempotent_check is not changed
- 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
- 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}}'
- debug:
msg: '{{peers}}'
- assert:
that:
- peers.data.peer_ips is defined
- assert:
that:
- peers.data.peer_ips 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
- 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'
- 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
- 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}}'
- debug:
msg: '{{short_password}}'
- assert:
that:
- '"at least 8" in short_password.msg'
- assert:
that:
- '"at least 8" in short_password.msg'
- name: Set network access type to community string
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
net_name: '{{test_net_name}}'
state: present
access: community
community_string: abc123
delegate_to: localhost
register: set_net_community
- debug:
var: set_net_community
- assert:
that:
- set_net_community is changed
- set_net_community.data is defined
- name: Set network access type to username
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
net_name: '{{test_net_name}}'
state: present
access: users
users:
- username: ansibleuser
passphrase: ansiblepass
delegate_to: localhost
register: set_net_user
- debug:
var: set_net_user
- assert:
that:
- set_net_user is changed
- set_net_user.data is defined
- name: Set network access type to none
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
net_name: '{{test_net_name}}'
state: present
access: none
delegate_to: localhost
register: set_net_none
- debug:
var: set_net_none
- assert:
that:
- set_net_none is changed
- set_net_none.data is defined
- name: Query network SNMP settings
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
net_name: '{{test_net_name}}'
state: query
delegate_to: localhost
register: get_net
- debug:
var: get_net
- assert:
that:
- get_net.data is defined
always:
- name: Disable SNMPv3
meraki_snmp:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
state: present
v3_enabled: no
v3_auth_mode: SHA
v3_auth_pass: ansiblepass
v3_priv_mode: AES128
v3_priv_pass: ansiblepass
delegate_to: localhost
- name: Delete SNMP network
meraki_network:
auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}'
net_name: '{{test_net_name}}'
state: absent
delegate_to: localhost