Skip to content

Commit

Permalink
meraki_mx_static_route - Add gateway_vlan_id parameter (#323)
Browse files Browse the repository at this point in the history
* Add gateway_vlan_id
- v1 changed how VLANs are enabled on networks
- Other updates are required for the VLAN settings

* Properly enable VLANs

* Add documentation for gateway vlan ID
  • Loading branch information
kbreit committed Jun 15, 2022
1 parent 95ae548 commit 6f565e3
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 99 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/mx_static_route.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
bugfixes:
- meraki_mx_static_route - Add support for gateway_vlan_id otherwise requests could error
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ build_ignore:
- '*tar.gz'
- '*.DS_Store'
- '*.json'
- 'venv'
- 'venv*'
- '.vscode'
- '.gitignore'
- '.env'
Expand Down
242 changes: 144 additions & 98 deletions plugins/modules/meraki_mx_static_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
# 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'
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "community",
}

DOCUMENTATION = r'''
DOCUMENTATION = r"""
---
module: meraki_mx_static_route
short_description: Manage static routes in the Meraki cloud
Expand Down Expand Up @@ -51,6 +52,10 @@
description:
- Unique ID of static route.
type: str
gateway_vlan_id:
description:
- The gateway IP (next hop) VLAN ID of the static route.
type: int
fixed_ip_assignments:
description:
- List of fixed MAC to IP bindings for DHCP.
Expand Down Expand Up @@ -96,9 +101,9 @@
author:
- Kevin Breit (@kbreit)
extends_documentation_fragment: cisco.meraki.meraki
'''
"""

EXAMPLES = r'''
EXAMPLES = r"""
- name: Create static_route
meraki_static_route:
auth_key: abc123
Expand Down Expand Up @@ -139,9 +144,9 @@
net_name: YourNet
route_id: '{{item}}'
delegate_to: localhost
'''
"""

RETURN = r'''
RETURN = r"""
data:
description: Information about the created or manipulated object.
returned: info
Expand Down Expand Up @@ -217,34 +222,39 @@
returned: query or update
type: str
sample: JimLaptop
'''
"""

from ansible.module_utils.basic import AnsibleModule, json
from ansible_collections.cisco.meraki.plugins.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec
from ansible_collections.cisco.meraki.plugins.module_utils.network.meraki.meraki import (
MerakiModule,
meraki_argument_spec,
)


def fixed_ip_factory(meraki, data):
fixed_ips = dict()
for item in data:
fixed_ips[item['mac']] = {'ip': item['ip'], 'name': item['name']}
fixed_ips[item["mac"]] = {"ip": item["ip"], "name": item["name"]}
return fixed_ips


def get_static_routes(meraki, net_id):
path = meraki.construct_path('get_all', net_id=net_id)
r = meraki.request(path, method='GET')
path = meraki.construct_path("get_all", net_id=net_id)
r = meraki.request(path, method="GET")
return r


def get_static_route(meraki, net_id, route_id):
path = meraki.construct_path('get_one', net_id=net_id, custom={'route_id': route_id})
r = meraki.request(path, method='GET')
path = meraki.construct_path(
"get_one", net_id=net_id, custom={"route_id": route_id}
)
r = meraki.request(path, method="GET")
return r


def does_route_exist(name, routes):
for route in routes:
if name == route['name']:
if name == route["name"]:
return route
return None

Expand All @@ -261,132 +271,168 @@ def main():
# define the available arguments/parameters that a user can pass to
# the module

fixed_ip_arg_spec = dict(mac=dict(type='str'),
ip=dict(type='str'),
name=dict(type='str'),
)
fixed_ip_arg_spec = dict(
mac=dict(type="str"),
ip=dict(type="str"),
name=dict(type="str"),
)

reserved_ip_arg_spec = dict(start=dict(type='str'),
end=dict(type='str'),
comment=dict(type='str'),
)
reserved_ip_arg_spec = dict(
start=dict(type="str"),
end=dict(type="str"),
comment=dict(type="str"),
)

argument_spec = meraki_argument_spec()
argument_spec.update(
net_id=dict(type='str'),
net_name=dict(type='str'),
name=dict(type='str'),
subnet=dict(type='str'),
gateway_ip=dict(type='str'),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
fixed_ip_assignments=dict(type='list', elements='dict', options=fixed_ip_arg_spec),
reserved_ip_ranges=dict(type='list', elements='dict', options=reserved_ip_arg_spec),
route_id=dict(type='str'),
enabled=dict(type='bool'),
net_id=dict(type="str"),
net_name=dict(type="str"),
name=dict(type="str"),
subnet=dict(type="str"),
gateway_ip=dict(type="str"),
state=dict(
type="str", default="present", choices=["absent", "present", "query"]
),
fixed_ip_assignments=dict(
type="list", elements="dict", options=fixed_ip_arg_spec
),
reserved_ip_ranges=dict(
type="list", elements="dict", options=reserved_ip_arg_spec
),
route_id=dict(type="str"),
enabled=dict(type="bool"),
gateway_vlan_id=dict(type="int"),
)

# 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,
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)

meraki = MerakiModule(module, function='static_route')
module.params['follow_redirects'] = 'all'
meraki = MerakiModule(module, function="static_route")
module.params["follow_redirects"] = "all"
payload = None

query_urls = {'static_route': '/networks/{net_id}/appliance/staticRoutes'}
query_one_urls = {'static_route': '/networks/{net_id}/appliance/staticRoutes/{route_id}'}
create_urls = {'static_route': '/networks/{net_id}/appliance/staticRoutes/'}
update_urls = {'static_route': '/networks/{net_id}/appliance/staticRoutes/{route_id}'}
delete_urls = {'static_route': '/networks/{net_id}/appliance/staticRoutes/{route_id}'}
meraki.url_catalog['get_all'].update(query_urls)
meraki.url_catalog['get_one'].update(query_one_urls)
meraki.url_catalog['create'] = create_urls
meraki.url_catalog['update'] = update_urls
meraki.url_catalog['delete'] = delete_urls

if not meraki.params['org_name'] and not meraki.params['org_id']:
meraki.fail_json(msg="Parameters 'org_name' or 'org_id' parameters are required")
if not meraki.params['net_name'] and not meraki.params['net_id']:
meraki.fail_json(msg="Parameters 'net_name' or 'net_id' parameters are required")
if meraki.params['net_name'] and meraki.params['net_id']:
query_urls = {"static_route": "/networks/{net_id}/appliance/staticRoutes"}
query_one_urls = {
"static_route": "/networks/{net_id}/appliance/staticRoutes/{route_id}"
}
create_urls = {"static_route": "/networks/{net_id}/appliance/staticRoutes/"}
update_urls = {
"static_route": "/networks/{net_id}/appliance/staticRoutes/{route_id}"
}
delete_urls = {
"static_route": "/networks/{net_id}/appliance/staticRoutes/{route_id}"
}
meraki.url_catalog["get_all"].update(query_urls)
meraki.url_catalog["get_one"].update(query_one_urls)
meraki.url_catalog["create"] = create_urls
meraki.url_catalog["update"] = update_urls
meraki.url_catalog["delete"] = delete_urls

if not meraki.params["org_name"] and not meraki.params["org_id"]:
meraki.fail_json(
msg="Parameters 'org_name' or 'org_id' parameters are required"
)
if not meraki.params["net_name"] and not meraki.params["net_id"]:
meraki.fail_json(
msg="Parameters 'net_name' or 'net_id' parameters are required"
)
if meraki.params["net_name"] and meraki.params["net_id"]:
meraki.fail_json(msg="'net_name' and 'net_id' are mutually exclusive")

# Construct payload
if meraki.params['state'] == 'present':
if meraki.params["state"] == "present":
payload = dict()
if meraki.params['net_name']:
payload['name'] = meraki.params['net_name']
if meraki.params["net_name"]:
payload["name"] = meraki.params["net_name"]

# 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']
org_id = meraki.params["org_id"]
if not org_id:
org_id = meraki.get_org_id(meraki.params['org_name'])
net_id = meraki.params['net_id']
org_id = meraki.get_org_id(meraki.params["org_name"])
net_id = meraki.params["net_id"]
if net_id is None:
nets = meraki.get_nets(org_id=org_id)
net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets)
net_id = meraki.get_net_id(net_name=meraki.params["net_name"], data=nets)

if meraki.params['state'] == 'query':
if meraki.params['route_id'] is not None:
meraki.result['data'] = get_static_route(meraki, net_id, meraki.params['route_id'])
if meraki.params["state"] == "query":
if meraki.params["route_id"] is not None:
meraki.result["data"] = get_static_route(
meraki, net_id, meraki.params["route_id"]
)
else:
meraki.result['data'] = get_static_routes(meraki, net_id)
elif meraki.params['state'] == 'present':
payload = {'name': meraki.params['name'],
'subnet': meraki.params['subnet'],
'gatewayIp': meraki.params['gateway_ip'],
}
if meraki.params['fixed_ip_assignments'] is not None:
payload['fixedIpAssignments'] = fixed_ip_factory(meraki,
meraki.params['fixed_ip_assignments'])
if meraki.params['reserved_ip_ranges'] is not None:
payload['reservedIpRanges'] = meraki.params['reserved_ip_ranges']
if meraki.params['enabled'] is not None:
payload['enabled'] = meraki.params['enabled']

route_id = meraki.params['route_id']
if meraki.params['name'] is not None and route_id is None:
route_status = does_route_exist(meraki.params['name'], get_static_routes(meraki, net_id))
meraki.result["data"] = get_static_routes(meraki, net_id)
elif meraki.params["state"] == "present":
payload = {
"name": meraki.params["name"],
"subnet": meraki.params["subnet"],
"gatewayIp": meraki.params["gateway_ip"],
}
if meraki.params["fixed_ip_assignments"] is not None:
payload["fixedIpAssignments"] = fixed_ip_factory(
meraki, meraki.params["fixed_ip_assignments"]
)
if meraki.params["reserved_ip_ranges"] is not None:
payload["reservedIpRanges"] = meraki.params["reserved_ip_ranges"]
if meraki.params["enabled"] is not None:
payload["enabled"] = meraki.params["enabled"]
if meraki.params["gateway_vlan_id"] is not None:
payload["gatewayVlanId"] = meraki.params["gateway_vlan_id"]

route_id = meraki.params["route_id"]
if meraki.params["name"] is not None and route_id is None:
route_status = does_route_exist(
meraki.params["name"], get_static_routes(meraki, net_id)
)
if route_status is not None: # Route exists, assign route_id
route_id = route_status['id']
route_id = route_status["id"]

if route_id is not None:
existing_route = get_static_route(meraki, net_id, route_id)
original = existing_route.copy()
payload = update_dict(existing_route, payload)
if module.check_mode:
meraki.result['data'] = payload
meraki.result["data"] = payload
meraki.exit_json(**meraki.result)
if meraki.is_update_required(original, payload, optional_ignore=['id']):
path = meraki.construct_path('update', net_id=net_id, custom={'route_id': route_id})
meraki.result['data'] = meraki.request(path, method="PUT", payload=json.dumps(payload))
meraki.result['changed'] = True
if meraki.is_update_required(original, payload, optional_ignore=["id"]):
path = meraki.construct_path(
"update", net_id=net_id, custom={"route_id": route_id}
)
meraki.result["data"] = meraki.request(
path, method="PUT", payload=json.dumps(payload)
)
meraki.result["changed"] = True
else:
meraki.result['data'] = original
meraki.result["data"] = original
else:
if module.check_mode:
meraki.result['data'] = payload
meraki.result["data"] = payload
meraki.exit_json(**meraki.result)
path = meraki.construct_path('create', net_id=net_id)
meraki.result['data'] = meraki.request(path, method="POST", payload=json.dumps(payload))
meraki.result['changed'] = True
elif meraki.params['state'] == 'absent':
path = meraki.construct_path("create", net_id=net_id)
meraki.result["data"] = meraki.request(
path, method="POST", payload=json.dumps(payload)
)
meraki.result["changed"] = True
elif meraki.params["state"] == "absent":
if module.check_mode:
meraki.exit_json(**meraki.result)
path = meraki.construct_path('delete', net_id=net_id, custom={'route_id': meraki.params['route_id']})
meraki.result['data'] = meraki.request(path, method='DELETE')
meraki.result['changed'] = True
path = meraki.construct_path(
"delete", net_id=net_id, custom={"route_id": meraki.params["route_id"]}
)
meraki.result["data"] = meraki.request(path, method="DELETE")
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__':
if __name__ == "__main__":
main()
Loading

0 comments on commit 6f565e3

Please sign in to comment.