ansible-later/env_27/lib/python2.7/site-packages/ansible/modules/network/meraki/meraki_ssid.py
2019-04-11 13:00:36 +02:00

510 lines
21 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Kevin Breit (@kbreit) <kevin.breit@kevinbreit.net>
# 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_ssid
short_description: Manage wireless SSIDs in the Meraki cloud
version_added: "2.7"
description:
- Allows for management of SSIDs in a Meraki wireless environment.
notes:
- Deleting an SSID does not delete RADIUS servers.
options:
state:
description:
- Specifies whether SNMP information should be queried or modified.
choices: ['absent', 'query', 'present']
default: present
number:
description:
- SSID number within network.
aliases: [ssid_number]
name:
description:
- Name of SSID.
org_name:
description:
- Name of organization.
org_id:
description:
- ID of organization.
net_name:
description:
- Name of network.
net_id:
description:
- ID of network.
enabled:
description:
- Enable or disable SSID network.
type: bool
auth_mode:
description:
- Set authentication mode of network.
choices: [open, psk, open-with-radius, 8021x-meraki, 8021x-radius]
encryption_mode:
description:
- Set encryption mode of network.
choices: [wpa, eap, wpa-eap]
psk:
description:
- Password for wireless network.
- Requires auth_mode to be set to psk.
wpa_encryption_mode:
description:
- Encryption mode within WPA2 specification.
choices: [WPA1 and WPA2, WPA2 only]
splash_page:
description:
- Set to enable splash page and specify type of splash.
choices: ['None',
'Click-through splash page',
'Billing',
'Password-protected with Meraki RADIUS',
'Password-protected with custom RADIUS',
'Password-protected with Active Directory',
'Password-protected with LDAP',
'SMS authentication',
'Systems Manager Sentry',
'Facebook Wi-Fi',
'Google OAuth',
'Sponsored guest']
radius_servers:
description:
- List of RADIUS servers.
suboptions:
host:
description:
- IP address or hostname of RADIUS server.
port:
description:
- Port number RADIUS server is listening to.
secret:
description:
- RADIUS password.
radius_coa_enabled:
description:
- Enable or disable RADIUS CoA (Change of Authorization) on SSID.
type: bool
radius_failover_policy:
description:
- Set client access policy in case RADIUS servers aren't available.
choices: [Deny access, Allow access]
radius_load_balancing_policy:
description:
- Set load balancing policy when multiple RADIUS servers are specified.
choices: [Strict priority order, Round robin]
radius_accounting_enabled:
description:
- Enable or disable RADIUS accounting.
type: bool
radius_accounting_servers:
description:
- List of RADIUS servers for RADIUS accounting.
suboptions:
host:
description:
- IP address or hostname of RADIUS server.
port:
description:
- Port number RADIUS server is listening to.
secret:
description:
- RADIUS password.
ip_assignment_mode:
description:
- Method of which SSID uses to assign IP addresses.
choices: ['NAT mode',
'Bridge mode',
'Layer 3 roaming',
'Layer 3 roaming with a concentrator',
'VPN']
use_vlan_tagging:
description:
- Set whether to use VLAN tagging.
type: bool
default_vlan_id:
description:
- Default VLAN ID.
vlan_id:
description:
- ID number of VLAN on SSID.
ap_tags_vlan_ids:
description:
- List of VLAN tags.
suboptions:
tags:
description:
- List of AP tags.
vlan_id:
description:
- Numerical identifier that is assigned to the VLAN.
walled_garden_enabled:
description:
- Enable or disable walled garden functionality.
type: bool
walled_garden_ranges:
description:
- List of walled garden ranges.
min_bitrate:
description:
- Minimum bitrate (Mbps) allowed on SSID.
choices: [1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54]
band_selection:
description:
- Set band selection mode.
choices: ['Dual band operation', '5 GHz band only', 'Dual band operation with Band Steering']
per_client_bandwidth_limit_up:
description:
- Maximum bandwidth in Mbps devices on SSID can upload.
per_client_bandwidth_limit_down:
description:
- Maximum bandwidth in Mbps devices on SSID can download.
concentrator_network_id:
description:
- The concentrator to use for 'Layer 3 roaming with a concentrator' or 'VPN'.
author:
- Kevin Breit (@kbreit)
extends_documentation_fragment: meraki
'''
EXAMPLES = r'''
'''
RETURN = r'''
data:
description: List of wireless SSIDs.
returned: success
type: complex
contains:
number:
description: Zero-based index number for SSIDs.
returned: success
type: int
sample: 0
name:
description:
- Name of wireless SSID.
- This value is what is broadcasted.
returned: success
type: str
sample: CorpWireless
enabled:
description: Enabled state of wireless network.
returned: success
type: bool
sample: true
splashPage:
description: Splash page to show when user authenticates.
returned: success
type: str
sample: Click-through splash page
ssidAdminAccessible:
description: Whether SSID is administratively accessible.
returned: success
type: bool
sample: true
authMode:
description: Authentication method.
returned: success
type: str
sample: psk
psk:
description: Secret wireless password.
returned: success
type: str
sample: SecretWiFiPass
encryptionMode:
description: Wireless traffic encryption method.
returned: success
type: str
sample: wpa
wpaEncryptionMode:
description: Enabled WPA versions.
returned: success
type: str
sample: WPA2 only
ipAssignmentMode:
description: Wireless client IP assignment method.
returned: success
type: str
sample: NAT mode
minBitrate:
description: Minimum bitrate a wireless client can connect at.
returned: success
type: int
sample: 11
bandSelection:
description: Wireless RF frequency wireless network will be broadcast on.
returned: success
type: str
sample: 5 GHz band only
perClientBandwidthLimitUp:
description: Maximum upload bandwidth a client can use.
returned: success
type: int
sample: 1000
perClientBandwidthLimitDown:
description: Maximum download bandwidth a client can use.
returned: success
type: int
sample: 0
'''
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_available_number(data):
for item in data:
if 'Unconfigured SSID' in item['name']:
return item['number']
return False
def get_ssid_number(name, data):
for ssid in data:
if name == ssid['name']:
return ssid['number']
return False
def get_ssids(meraki, net_id):
path = meraki.construct_path('get_all', net_id=net_id)
return meraki.request(path, method='GET')
def main():
param_map = {'name': 'name',
'enabled': 'enabled',
'authMode': 'auth_mode',
'encryptionMode': 'encryption_mode',
'psk': 'psk',
'wpaEncryptionMode': 'wpa_encryption_mode',
'splashPage': 'splash_page',
'radiusServers': 'radius_servers',
'radiusCoaEnabled': 'radius_coa_enabled',
'radiusFailoverPolicy': 'radius_failover_policy',
'radiusLoadBalancingPolicy': 'radius_load_balancing_policy',
'radiusAccountingEnabled': 'radius_accounting_enabled',
'radiusAccountingServers': 'radius_accounting_servers',
'ipAssignmentMode': 'ip_assignment_mode',
'useVlanTagging': 'use_vlan_tagging',
'concentratorNetworkId': 'concentrator_network_id',
'vlanId': 'vlan_id',
'defaultVlanId': 'default_vlan_id',
'apTagsAndVlanIds': 'ap_tags_vlan_ids',
'walledGardenEnabled': 'walled_garden_enabled',
'walledGardenRanges': 'walled_garden_ranges',
'minBitrate': 'min_bitrate',
'bandSelection': 'band_selection',
'perClientBandwidthLimitUp': 'per_client_bandwidth_limit_up',
'perClientBandwidthLimitDown': 'per_client_bandwidth_limit_down',
}
default_payload = {'name': 'Unconfigured SSID',
'auth_mode': 'open',
'splashPage': 'None',
'perClientBandwidthLimitUp': 0,
'perClientBandwidthLimitDown': 0,
'ipAssignmentMode': 'NAT mode',
'enabled': False,
'bandSelection': 'Dual band operation',
'minBitrate': 11,
}
# define the available arguments/parameters that a user can pass to
# the module
radius_arg_spec = dict(host=dict(type='str', required=True),
port=dict(type='int'),
secret=dict(type='str', no_log=True),
)
vlan_arg_spec = dict(tags=dict(type='list'),
vlan_id=dict(type='int'),
)
argument_spec = meraki_argument_spec()
argument_spec.update(state=dict(type='str', choices=['absent', 'present', 'query'], default='present'),
number=dict(type='int', aliases=['ssid_number']),
name=dict(type='str'),
org_name=dict(type='str', aliases=['organization']),
org_id=dict(type='int'),
net_name=dict(type='str'),
net_id=dict(type='str'),
enabled=dict(type='bool'),
auth_mode=dict(type='str', choices=['open', 'psk', 'open-with-radius', '8021x-meraki', '8021x-radius']),
encryption_mode=dict(type='str', choices=['wpa', 'eap', 'wpa-eap']),
psk=dict(type='str', no_log=True),
wpa_encryption_mode=dict(type='str', choices=['WPA1 and WPA2', 'WPA2 only']),
splash_page=dict(type='str', choices=['None',
'Click-through splash page',
'Billing',
'Password-protected with Meraki RADIUS',
'Password-protected with custom RADIUS',
'Password-protected with Active Directory',
'Password-protected with LDAP',
'SMS authentication',
'Systems Manager Sentry',
'Facebook Wi-Fi',
'Google OAuth',
'Sponsored guest']),
radius_servers=dict(type='list', default=None, element='dict', options=radius_arg_spec),
radius_coa_enabled=dict(type='bool'),
radius_failover_policy=dict(type='str', choices=['Deny access', 'Allow access']),
radius_load_balancing_policy=dict(type='str', choices=['Strict priority order', 'Round robin']),
radius_accounting_enabled=dict(type='bool'),
radius_accounting_servers=dict(type='list', element='dict', options=radius_arg_spec),
ip_assignment_mode=dict(type='str', choices=['NAT mode',
'Bridge mode',
'Layer 3 roaming',
'Layer 3 roaming with a concentrator',
'VPN']),
use_vlan_tagging=dict(type='bool'),
concentrator_network_id=dict(type='str'),
vlan_id=dict(type='int'),
default_vlan_id=dict(type='int'),
ap_tags_vlan_ids=dict(type='list', default=None, element='dict', options=vlan_arg_spec),
walled_garden_enabled=dict(type='bool'),
walled_garden_ranges=dict(type='list'),
min_bitrate=dict(type='float', choices=[1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54]),
band_selection=dict(type='str', choices=['Dual band operation',
'5 GHz band only',
'Dual band operation with Band Steering']),
per_client_bandwidth_limit_up=dict(type='int'),
per_client_bandwidth_limit_down=dict(type='int'),
)
# 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='ssid')
meraki.params['follow_redirects'] = 'all'
query_urls = {'ssid': '/networks/{net_id}/ssids'}
query_url = {'ssid': 'networks/{net_id}/ssids/'}
update_url = {'ssid': 'networks/{net_id}/ssids/'}
meraki.url_catalog['get_all'].update(query_urls)
meraki.url_catalog['get_one'].update(query_url)
meraki.url_catalog['update'] = update_url
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
# FIXME: Work with Meraki so they can implement a check mode
if module.check_mode:
meraki.exit_json(**meraki.result)
# execute checks for argument completeness
if meraki.params['psk']:
if meraki.params['auth_mode'] != 'psk':
meraki.fail_json(msg='PSK is only allowed when auth_mode is set to psk')
if meraki.params['encryption_mode'] != 'wpa':
meraki.fail_json(msg='PSK requires encryption_mode be set to wpa')
if meraki.params['radius_servers']:
if meraki.params['auth_mode'] not in ('open-with-radius', '8021x-radius'):
meraki.fail_json(msg='radius_servers requires auth_mode to be open-with-radius or 8021x-radius')
if meraki.params['radius_accounting_enabled'] is True:
if meraki.params['auth_mode'] not in ('open-with-radius', '8021x-radius'):
meraki.fails_json(msg='radius_accounting_enabled is only allowed when auth_mode is open-with-radius or 8021x-radius')
if meraki.params['radius_accounting_servers'] is True:
if meraki.params['auth_mode'] not in ('open-with-radius', '8021x-radius') or meraki.params['radius_accounting_enabled'] is False:
meraki.fail_json(msg='radius_accounting_servers is only allowed when auth_mode is open_with_radius or 8021x-radius and \
radius_accounting_enabled is true')
# 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']
net_id = meraki.params['net_id']
if org_id is None:
org_id = meraki.get_org_id(meraki.params['org_name'])
if net_id is None:
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'] == 'query':
if meraki.params['name']:
ssid_id = get_ssid_number(meraki.params['name'], get_ssids(meraki, net_id))
path = meraki.construct_path('get_one', net_id=net_id) + str(ssid_id)
meraki.result['data'] = meraki.request(path, method='GET')
elif meraki.params['number']:
path = meraki.construct_path('get_one', net_id=net_id) + meraki.params['number']
meraki.result['data'] = meraki.request(path, method='GET')
else:
meraki.result['data'] = get_ssids(meraki, net_id)
elif meraki.params['state'] == 'present':
payload = dict()
for k, v in param_map.items():
if meraki.params[v] is not None:
payload[k] = meraki.params[v]
ssids = get_ssids(meraki, net_id)
original = ssids[get_ssid_number(meraki.params['name'], ssids)]
# meraki.fail_json(msg=meraki.is_update_required(original, payload), original=original, payload=payload)
if meraki.is_update_required(original, payload):
ssid_id = meraki.params['number']
if ssid_id is None: # Name should be used to lookup number
ssid_id = get_ssid_number(meraki.params['name'], ssids)
if ssid_id is False:
ssid_id = get_available_number(ssids)
if ssid_id is False:
meraki.fail_json(msg='No unconfigured SSIDs are available. Specify a number.')
path = meraki.construct_path('update', net_id=net_id) + str(ssid_id)
result = meraki.request(path, 'PUT', payload=json.dumps(payload))
meraki.result['data'] = result
meraki.result['changed'] = True
elif meraki.params['state'] == 'absent':
ssids = get_ssids(meraki, net_id)
ssid_id = meraki.params['number']
if ssid_id is None: # Name should be used to lookup number
ssid_id = get_ssid_number(meraki.params['name'], ssids)
if ssid_id is False:
ssid_id = get_available_number(ssids)
if ssid_id is False:
meraki.fail_json(msg='No SSID found by specified name and no number was referenced.')
path = meraki.construct_path('update', net_id=net_id) + str(ssid_id)
payload = default_payload
payload['name'] = payload['name'] + ' ' + str(ssid_id + 1)
# meraki.fail_json(msg='Payload', payload=payload)
result = meraki.request(path, 'PUT', payload=json.dumps(payload))
meraki.result['data'] = result
meraki.result['changed'] = True
# 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()