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
pull/59848/head
Kevin Breit 5 years ago committed by Nathaniel Case
parent 516b39b79a
commit d105c205ef

@ -53,6 +53,43 @@ options:
peer_ips: peer_ips:
description: description:
- Semi-colon delimited IP addresses which can perform SNMP queries. - 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: author:
- Kevin Breit (@kbreit) - Kevin Breit (@kbreit)
extends_documentation_fragment: meraki extends_documentation_fragment: meraki
@ -94,6 +131,28 @@ EXAMPLES = r'''
v3_priv_pass: ansiblepass v3_priv_pass: ansiblepass
peer_ips: 192.0.1.1;192.0.1.2 peer_ips: 192.0.1.1;192.0.1.2
delegate_to: localhost 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''' RETURN = r'''
@ -104,56 +163,76 @@ data:
contains: contains:
hostname: hostname:
description: Hostname of SNMP server. description: Hostname of SNMP server.
returned: success returned: success and no network specified.
type: str type: str
sample: n1.meraki.com sample: n1.meraki.com
peerIps: peerIps:
description: Semi-colon delimited list of IPs which can poll SNMP information. description: Semi-colon delimited list of IPs which can poll SNMP information.
returned: success returned: success and no network specified.
type: str type: str
sample: 192.0.1.1 sample: 192.0.1.1
port: port:
description: Port number of SNMP. description: Port number of SNMP.
returned: success returned: success and no network specified.
type: str type: str
sample: 16100 sample: 16100
v2c_enabled: v2c_enabled:
description: Shows enabled state of SNMPv2c description: Shows enabled state of SNMPv2c
returned: success returned: success and no network specified.
type: bool type: bool
sample: true sample: true
v3_enabled: v3_enabled:
description: Shows enabled state of SNMPv3 description: Shows enabled state of SNMPv3
returned: success returned: success and no network specified.
type: bool type: bool
sample: true sample: true
v3_auth_mode: v3_auth_mode:
description: The SNMP version 3 authentication mode either MD5 or SHA. description: The SNMP version 3 authentication mode either MD5 or SHA.
returned: success returned: success and no network specified.
type: str type: str
sample: SHA sample: SHA
v3_priv_mode: v3_priv_mode:
description: The SNMP version 3 privacy mode DES or AES128. description: The SNMP version 3 privacy mode DES or AES128.
returned: success returned: success and no network specified.
type: str type: str
sample: AES128 sample: AES128
v2_community_string: v2_community_string:
description: Automatically generated community string for SNMPv2c. description: Automatically generated community string for SNMPv2c.
returned: When SNMPv2c is enabled. returned: When SNMPv2c is enabled and no network specified.
type: str type: str
sample: o/8zd-JaSb sample: o/8zd-JaSb
v3_user: v3_user:
description: Automatically generated username for SNMPv3. description: Automatically generated username for SNMPv3.
returned: When SNMPv3c is enabled. returned: When SNMPv3c is enabled and no network specified.
type: str type: str
sample: o/8zd-JaSb 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 import os
from ansible.module_utils.basic import AnsibleModule, json, env_fallback from ansible.module_utils.basic import AnsibleModule, json, env_fallback
from ansible.module_utils.urls import fetch_url from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_native 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 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: if meraki.params['v2c_enabled'] is not None:
payload = {'v2cEnabled': meraki.params['v2c_enabled'], 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: 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.') 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 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'], 'v3AuthPass': meraki.params['v3_auth_pass'],
'v3PrivMode': meraki.params['v3_priv_mode'].upper(), 'v3PrivMode': meraki.params['v3_priv_mode'].upper(),
'v3PrivPass': meraki.params['v3_priv_pass'], '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: if meraki.params['peer_ips'] is not None:
full_compare['v3Enabled'] = False payload['peerIps'] = meraki.params['peer_ips']
if meraki.params['v2c_enabled'] is None: elif meraki.params['v3_enabled'] is False:
full_compare['v2cEnabled'] = False payload = {'v3Enabled': False}
full_compare = snake_dict_to_camel_dict(payload)
path = meraki.construct_path('create', org_id=org_id) path = meraki.construct_path('create', org_id=org_id)
snmp = get_snmp(meraki, org_id) snmp = get_snmp(meraki, org_id)
ignored_parameters = ['v3AuthPass', 'v3PrivPass', 'hostname', 'port', 'v2CommunityString', 'v3User'] ignored_parameters = ['v3AuthPass', 'v3PrivPass', 'hostname', 'port', 'v2CommunityString', 'v3User']
@ -208,6 +281,7 @@ def set_snmp(meraki, org_id):
diff = recursive_diff(snmp, full_compare) diff = recursive_diff(snmp, full_compare)
snmp.update(payload) snmp.update(payload)
meraki.result['data'] = snmp meraki.result['data'] = snmp
meraki.result['changed'] = True
meraki.result['diff'] = {'before': diff[0], meraki.result['diff'] = {'before': diff[0],
'after': diff[1]} 'after': diff[1]}
meraki.exit_json(**meraki.result) meraki.exit_json(**meraki.result)
@ -228,6 +302,10 @@ def main():
# define the available arguments/parameters that a user can pass to # define the available arguments/parameters that a user can pass to
# the module # the module
user_arg_spec = dict(username=dict(type='str'),
passphrase=dict(type='str', no_log=True),
)
argument_spec = meraki_argument_spec() argument_spec = meraki_argument_spec()
argument_spec.update(state=dict(type='str', choices=['present', 'query'], default='present'), argument_spec.update(state=dict(type='str', choices=['present', 'query'], default='present'),
v2c_enabled=dict(type='bool'), v2c_enabled=dict(type='bool'),
@ -237,6 +315,11 @@ def main():
v3_priv_mode=dict(type='str', choices=['DES', 'AES128']), v3_priv_mode=dict(type='str', choices=['DES', 'AES128']),
v3_priv_pass=dict(type='str', no_log=True), v3_priv_pass=dict(type='str', no_log=True),
peer_ips=dict(type='str'), 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 # seed the result dict in the object
@ -257,36 +340,62 @@ def main():
meraki = MerakiModule(module, function='snmp') meraki = MerakiModule(module, function='snmp')
meraki.params['follow_redirects'] = 'all' 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_urls = {'snmp': '/organizations/{org_id}/snmp', update_net_urls = {'snmp': '/networks/{net_id}/snmpSettings'}
}
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'] = update_urls
meraki.url_catalog['create_net'] = update_net_urls
payload = None 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']: if not meraki.params['org_name'] and not meraki.params['org_id']:
meraki.fail_json(msg='org_name or org_id is required') meraki.fail_json(msg='org_name or org_id is required')
org_id = meraki.params['org_id'] org_id = meraki.params['org_id']
if org_id is None: if org_id is None:
org_id = meraki.get_org_id(meraki.params['org_name']) 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': if meraki.params['state'] == 'query':
if net_id is None:
meraki.result['data'] = get_snmp(meraki, org_id) 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': elif meraki.params['state'] == 'present':
if net_id is None:
meraki.result['data'] = set_snmp(meraki, org_id) 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 # in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results # simple AnsibleModule.exit_json(), passing the key/value results

@ -3,37 +3,37 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 - block:
- name: Test an API key is provided
fail: fail:
msg: Please define an API key msg: Please define an API key
when: auth_key is not defined when: auth_key is not defined
- name: Query all SNMP settings - name: Create SNMP network
meraki_snmp: meraki_network:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
state: query net_name: '{{test_net_name}}'
state: present
type: appliance
delegate_to: localhost delegate_to: localhost
register: snmp_query register: new_net
- debug: - set_fact:
msg: '{{snmp_query}}' net_id: new_net.data.id
- name: Enable SNMPv2c with check mode - name: Query all SNMP settings
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
state: present state: query
v2c_enabled: true
delegate_to: localhost delegate_to: localhost
register: snmp_v2_enable_check register: snmp_query
check_mode: yes
- assert: - debug:
that: msg: '{{snmp_query}}'
- snmp_v2_enable_check is not changed
- name: Enable SNMPv2c - name: Enable SNMPv2c
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -42,15 +42,15 @@
delegate_to: localhost delegate_to: localhost
register: snmp_v2_enable register: snmp_v2_enable
- debug: - debug:
msg: '{{snmp_v2_enable}}' msg: '{{snmp_v2_enable}}'
- assert: - assert:
that: that:
- snmp_v2_enable.data.v2_community_string is defined - snmp_v2_enable.data.v2_community_string is defined
- snmp_v2_enable.data.v2c_enabled == true - snmp_v2_enable.data.v2c_enabled == true
- name: Disable SNMPv2c - name: Disable SNMPv2c
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -59,12 +59,12 @@
delegate_to: localhost delegate_to: localhost
register: snmp_v2_disable register: snmp_v2_disable
- assert: - assert:
that: that:
- snmp_v2_disable.data.v2_community_string is not defined - snmp_v2_disable.data.v2_community_string is not defined
- snmp_v2_disable.data.v2c_enabled == False - snmp_v2_disable.data.v2c_enabled == False
- name: Enable SNMPv2c with org_id - name: Enable SNMPv2c with org_id
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_id: '{{test_org_id}}' org_id: '{{test_org_id}}'
@ -73,15 +73,15 @@
delegate_to: localhost delegate_to: localhost
register: snmp_v2_enable_id register: snmp_v2_enable_id
- debug: - debug:
msg: '{{snmp_v2_enable_id}}' msg: '{{snmp_v2_enable_id}}'
- assert: - assert:
that: that:
- snmp_v2_enable_id.data.v2_community_string is defined - snmp_v2_enable_id.data.v2_community_string is defined
- snmp_v2_enable_id.data.v2c_enabled == true - snmp_v2_enable_id.data.v2c_enabled == true
- name: Disable SNMPv2c with org_id - name: Disable SNMPv2c with org_id
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_id: '{{test_org_id}}' org_id: '{{test_org_id}}'
@ -90,12 +90,12 @@
delegate_to: localhost delegate_to: localhost
register: snmp_v2_disable_id register: snmp_v2_disable_id
- assert: - assert:
that: that:
- snmp_v2_disable_id.data.v2_community_string is not defined - snmp_v2_disable_id.data.v2_community_string is not defined
- snmp_v2_disable_id.data.v2c_enabled == False - snmp_v2_disable_id.data.v2c_enabled == False
- name: Enable SNMPv3 - name: Enable SNMPv3 with check mode
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -106,14 +106,15 @@
v3_priv_mode: AES128 v3_priv_mode: AES128
v3_priv_pass: ansiblepass v3_priv_pass: ansiblepass
delegate_to: localhost delegate_to: localhost
register: snmp_v3_enable check_mode: yes
register: snmp_v3_enable_check
- assert: - assert:
that: that:
- snmp_v3_enable.data.v3_enabled == True - snmp_v3_enable_check.data.v3_enabled == True
- snmp_v3_enable.changed == True - snmp_v3_enable_check.changed == True
- name: Check for idempotency - name: Enable SNMPv3
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -124,17 +125,14 @@
v3_priv_mode: AES128 v3_priv_mode: AES128
v3_priv_pass: ansiblepass v3_priv_pass: ansiblepass
delegate_to: localhost delegate_to: localhost
register: snmp_idempotent register: snmp_v3_enable
- debug:
msg: '{{snmp_idempotent}}'
- assert: - assert:
that: that:
- snmp_idempotent.changed == False - snmp_v3_enable.data.v3_enabled == True
- snmp_idempotent.data is defined - snmp_v3_enable.changed == True
- name: Check for idempotency with check mode - name: Check for idempotency
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -145,13 +143,17 @@
v3_priv_mode: AES128 v3_priv_mode: AES128
v3_priv_pass: ansiblepass v3_priv_pass: ansiblepass
delegate_to: localhost delegate_to: localhost
register: snmp_idempotent_check register: snmp_idempotent
- assert: - debug:
msg: '{{snmp_idempotent}}'
- assert:
that: that:
- snmp_idempotent_check is not changed - snmp_idempotent.changed == False
- snmp_idempotent.data is defined
- name: Add peer IPs - name: Add peer IPs
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -165,14 +167,14 @@
delegate_to: localhost delegate_to: localhost
register: peers register: peers
- debug: - debug:
msg: '{{peers}}' msg: '{{peers}}'
- assert: - assert:
that: that:
- peers.data.peer_ips is defined - peers.data.peer_ips is defined
- name: Add invalid peer IPs - name: Add invalid peer IPs
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -182,11 +184,11 @@
register: invalid_peers register: invalid_peers
ignore_errors: yes ignore_errors: yes
- assert: - assert:
that: that:
'"Peer IP addresses are semi-colon delimited." in invalid_peers.msg' '"Peer IP addresses are semi-colon delimited." in invalid_peers.msg'
- name: Set short password - name: Set short password
meraki_snmp: meraki_snmp:
auth_key: '{{auth_key}}' auth_key: '{{auth_key}}'
org_name: '{{test_org_name}}' org_name: '{{test_org_name}}'
@ -201,9 +203,104 @@
register: short_password register: short_password
ignore_errors: yes ignore_errors: yes
- debug: - debug:
msg: '{{short_password}}' msg: '{{short_password}}'
- assert: - assert:
that: that:
- '"at least 8" in short_password.msg' - '"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

Loading…
Cancel
Save