From ba99fb9d041f63f5d300956daabefeb0a86edb06 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Thu, 8 Aug 2019 10:51:33 -0700 Subject: reroot --- plugins/modules/vyos_static_route.py | 265 +++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 plugins/modules/vyos_static_route.py (limited to 'plugins/modules/vyos_static_route.py') diff --git a/plugins/modules/vyos_static_route.py b/plugins/modules/vyos_static_route.py new file mode 100644 index 00000000..ec1c6c95 --- /dev/null +++ b/plugins/modules/vyos_static_route.py @@ -0,0 +1,265 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Ansible by Red Hat, inc +# +# This file is part of Ansible by Red Hat +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'network'} + + +DOCUMENTATION = """ +--- +module: vyos_static_route +version_added: "2.4" +author: "Trishna Guha (@trishnaguha)" +short_description: Manage static IP routes on Vyatta VyOS network devices +description: + - This module provides declarative management of static + IP routes on Vyatta VyOS network devices. +notes: + - Tested against VYOS 1.1.7 +options: + prefix: + description: + - Network prefix of the static route. + C(mask) param should be ignored if C(prefix) is provided + with C(mask) value C(prefix/mask). + mask: + description: + - Network prefix mask of the static route. + next_hop: + description: + - Next hop IP of the static route. + admin_distance: + description: + - Admin distance of the static route. + aggregate: + description: List of static route definitions + state: + description: + - State of the static route configuration. + default: present + choices: ['present', 'absent'] +extends_documentation_fragment: vyos +""" + +EXAMPLES = """ +- name: configure static route + vyos_static_route: + prefix: 192.168.2.0 + mask: 24 + next_hop: 10.0.0.1 + +- name: configure static route prefix/mask + vyos_static_route: + prefix: 192.168.2.0/16 + next_hop: 10.0.0.1 + +- name: remove configuration + vyos_static_route: + prefix: 192.168.2.0 + mask: 16 + next_hop: 10.0.0.1 + state: absent + +- name: configure aggregates of static routes + vyos_static_route: + aggregate: + - { prefix: 192.168.2.0, mask: 24, next_hop: 10.0.0.1 } + - { prefix: 192.168.3.0, mask: 16, next_hop: 10.0.2.1 } + - { prefix: 192.168.3.0/16, next_hop: 10.0.2.1 } + +- name: Remove static route collections + vyos_static_route: + aggregate: + - { prefix: 172.24.1.0/24, next_hop: 192.168.42.64 } + - { prefix: 172.24.3.0/24, next_hop: 192.168.42.64 } + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - set protocols static route 192.168.2.0/16 next-hop 10.0.0.1 +""" +import re + +from copy import deepcopy + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.common.utils import remove_default_spec +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_config, load_config +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import vyos_argument_spec + + +def spec_to_commands(updates, module): + commands = list() + want, have = updates + for w in want: + prefix = w['prefix'] + mask = w['mask'] + next_hop = w['next_hop'] + admin_distance = w['admin_distance'] + state = w['state'] + del w['state'] + + if state == 'absent' and w in have: + commands.append('delete protocols static route %s/%s' % (prefix, mask)) + elif state == 'present' and w not in have: + cmd = 'set protocols static route %s/%s next-hop %s' % (prefix, mask, next_hop) + if admin_distance != 'None': + cmd += ' distance %s' % (admin_distance) + commands.append(cmd) + + return commands + + +def config_to_dict(module): + data = get_config(module) + obj = [] + + for line in data.split('\n'): + if line.startswith('set protocols static route'): + match = re.search(r'static route (\S+)', line, re.M) + prefix = match.group(1).split('/')[0] + mask = match.group(1).split('/')[1] + if 'next-hop' in line: + match_hop = re.search(r'next-hop (\S+)', line, re.M) + next_hop = match_hop.group(1).strip("'") + + match_distance = re.search(r'distance (\S+)', line, re.M) + if match_distance is not None: + admin_distance = match_distance.group(1)[1:-1] + else: + admin_distance = None + + if admin_distance is not None: + obj.append({'prefix': prefix, + 'mask': mask, + 'next_hop': next_hop, + 'admin_distance': admin_distance}) + else: + obj.append({'prefix': prefix, + 'mask': mask, + 'next_hop': next_hop, + 'admin_distance': 'None'}) + + return obj + + +def map_params_to_obj(module, required_together=None): + obj = [] + aggregate = module.params.get('aggregate') + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + + module._check_required_together(required_together, item) + d = item.copy() + if '/' in d['prefix']: + d['mask'] = d['prefix'].split('/')[1] + d['prefix'] = d['prefix'].split('/')[0] + + if 'admin_distance' in d: + d['admin_distance'] = str(d['admin_distance']) + + obj.append(d) + else: + prefix = module.params['prefix'].strip() + if '/' in prefix: + mask = prefix.split('/')[1] + prefix = prefix.split('/')[0] + else: + mask = module.params['mask'].strip() + next_hop = module.params['next_hop'].strip() + admin_distance = str(module.params['admin_distance']) + state = module.params['state'] + + obj.append({ + 'prefix': prefix, + 'mask': mask, + 'next_hop': next_hop, + 'admin_distance': admin_distance, + 'state': state + }) + + return obj + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + prefix=dict(type='str'), + mask=dict(type='str'), + next_hop=dict(type='str'), + admin_distance=dict(type='int'), + state=dict(default='present', choices=['present', 'absent']) + ) + + aggregate_spec = deepcopy(element_spec) + aggregate_spec['prefix'] = dict(required=True) + + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + + argument_spec = dict( + aggregate=dict(type='list', elements='dict', options=aggregate_spec), + ) + + argument_spec.update(element_spec) + argument_spec.update(vyos_argument_spec) + + required_one_of = [['aggregate', 'prefix']] + required_together = [['prefix', 'next_hop']] + mutually_exclusive = [['aggregate', 'prefix']] + + module = AnsibleModule(argument_spec=argument_spec, + required_one_of=required_one_of, + required_together=required_together, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True) + + warnings = list() + + result = {'changed': False} + if warnings: + result['warnings'] = warnings + want = map_params_to_obj(module, required_together=required_together) + have = config_to_dict(module) + + commands = spec_to_commands((want, have), module) + result['commands'] = commands + + if commands: + commit = not module.check_mode + load_config(module, commands, commit=commit) + result['changed'] = True + + module.exit_json(**result) + + +if __name__ == '__main__': + main() -- cgit v1.2.3