Last active
June 12, 2022 19:10
-
-
Save m-hume/9e882d83e084ebf08f9f to your computer and use it in GitHub Desktop.
This python script is used to read and modify the configuration of an EdgeMax ubnt router.
Specifically it updates the source address of an inbound nat rule that allows remote administration from a dynamic IP address tied to a dynamic DNS account.
Highly reusable is the function get_config_object(). Pass to this the textural content of config.bo…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python | |
# This python script is used to read and modify the configuration of an EdgeMax | |
# ubnt router. | |
# Specifically it updates the source address of an inbound nat rule that allows | |
# remote administration from a dynamic IP address tied to a dynamic DNS account. | |
# Highly reusable is the function get_config_object(). Pass to this the textural | |
# content of config.boot and it will convert it into an object for querying. | |
# Feel free to use it and modify it! | |
# mh 2015 monkeyr.com | |
import json | |
import socket | |
import os | |
def get_config_object(s_config): | |
s_out = '{' | |
i_current_indent = -1 | |
i_multiplyer = 4 | |
for s_line in s_config.splitlines(): | |
s_striped_line = s_line.strip(' ') | |
i_spaces = len(s_line) - len(s_striped_line) | |
i_loop_indent = i_spaces / i_multiplyer | |
if i_loop_indent == i_current_indent and b_object_has_data: | |
s_out += ',' | |
if s_striped_line[-1:] == '{': #is object start | |
b_object_has_data = False | |
s_out += '"'+s_striped_line[:-2].strip('"')+'":{' | |
elif s_striped_line[-1:] == '}': #is object end | |
b_object_has_data = True | |
s_out += '}' | |
else :# is property# | |
b_object_has_data = True | |
tmp = s_striped_line.split(' ', 1) | |
if len(tmp) == 1: | |
tmp.append('"true"') | |
s_out += '"'+tmp[0]+'":'+json.dumps(tmp[1].strip('"')) | |
i_current_indent = i_loop_indent | |
s_out += '}' | |
#print out | |
return json.loads(s_out) | |
s_cfg_path = '/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper' | |
s_dyna_ip = socket.gethostbyname("my-dynamic-dns-account.no-ip.org") | |
if s_dyna_ip: # were connected to the internet and can resolve the dynamic host name | |
# get the config as a string | |
f_handle = os.popen(s_cfg_path+ ' show') | |
s_config = f_handle.read() | |
# get the config as an object | |
o_config = get_config_object(s_config) | |
s_rule_name = '' | |
s_current_ip = '' | |
# find the right nat rule | |
for rule, value in o_config['service']['nat'].iteritems(): | |
if value['description'] == 'Remote admin from home': | |
#record current ip | |
s_rule_name = rule | |
s_current_ip = value['source']['address'] | |
break | |
if s_rule_name and s_current_ip: | |
if s_dyna_ip == s_current_ip: | |
print '%s source address is current %s' % (s_rule_name, s_current_ip) | |
else: | |
# set the new address | |
os.system(s_cfg_path+' begin') | |
os.system(s_cfg_path+' set service nat '+ s_rule_name +' source address '+ s_dyna_ip) | |
os.system(s_cfg_path+' commit') | |
os.system(s_cfg_path+' end') | |
print '%s source address updated from %s to %s' % (s_rule_name, s_current_ip, s_dyna_ip) |
I tried to bang something together from scratch and came up with the following. This should return a valid JSON string, and any situation such as the double address gets returned as a value string that can be converted into a list. I'm sure this isn't the most effective code, but I digress.
import socket
import os
import re
import shlex
from itertools import tee, islice, izip_longest
def get_next(some_iterable, window=1):
items, nexts = tee(some_iterable, 2)
nexts = islice(nexts, window, None)
return izip_longest(items, nexts)
def get_config_object(s_config):
s_out = '{'
stripped = re.sub('[ ]{2,}','',s_config)
looped_values=[]
looped_key = []
for s_line, s_next_line in get_next(stripped.splitlines()):
if s_line.endswith('{'):
s_out += '"'+s_line.split(' {')[0]+'":'+' {\n'
elif s_line.endswith('}'):
if s_next_line is None:
s_out += '}\n'
elif s_next_line.endswith('{'):
s_out += '},\n'
elif s_next_line.endswith('}'):
s_out += '}\n'
else:
s_out += '},\n'
else:
key_current= shlex.split(s_line)
key_next = shlex.split(s_next_line)
if key_current[0] != key_next[0] and key_current[0] != looped_key:
if len(key_current) == 1:
if s_next_line.endswith('}'):
s_out += '"'+key_current[0]+'": ""\n'
else:
s_out += '"'+key_current[0]+'": "",\n'
else:
if s_next_line.endswith('}'):
s_out += '"'+key_current[0]+'": "'+key_current[1]+'"\n'
else:
s_out += '"'+key_current[0]+'": "'+key_current[1]+'",\n'
else:
looped_key = key_current[0]
looped_values.append(key_current[1])
if key_next[0] != key_current[0]:
s_out += '"'+looped_key+'": "'+str(looped_values)+'"'
if s_next_line.endswith('}'):
s_out += '\n'
else:
s_out += ',\n'
looped_key = []
looped_values = []
s_out += '}'
return json.loads(s_out)
s_cfg_path = '/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper'
f_handle = os.popen(s_cfg_path+ ' show')
s_config = f_handle.read()
# get the config as an object
o_config = get_config_object(s_config)```
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It appears your get_config_object loop is parsing interfaces incorrectly: If an interface has more than one address, this script will only print one of the interfaces.
Example interface:
show interfaces ethernet eth0 { address 10.1.1.1/24 address 10.4.2.1/24 duplex auto speed auto }
Example adapted code:
Output:
{u'ethernet eth4': {u'duplex': u'auto', u'poe': {u'output': u'off'}, u'speed': u'auto', u'address': u'192.168.1.9/24'}, u'ethernet eth1': {u'duplex': u'auto', u'firewall': {u'out': {u'name': u'Wan_Access'}}, u'speed': u'auto', u'address': u'123.123.123.1/24'}, u'loopback lo': {}, u'ethernet eth3': {u'duplex': u'auto', u'speed': u'auto', u'address': u'10.0.1.1/24'}, u'ethernet eth2': {u'duplex': u'auto', u'firewall': {u'out': {u'name': u'Wan_Access'}}, u'speed': u'auto', u'address': u'10.0.0.1/24'}, u'switch switch0': {u'switch-port': {u'vlan-aware': u'disable'}, u'mtu': u'1500'}, u'ethernet eth0': {u'duplex': u'auto', u'speed': u'auto', u'address': u'10.4.2.1/24'}} duplex auto speed auto address 10.4.2.1/24
As you can see, from the poorly formatted code and outputs above, there's only one address being shown, despite having 2 addresses in the config.
I'm not fluent in python so I'll need time to propose a solution, but I just wanted to make sure people where aware of this information.