summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog13
-rw-r--r--debian/control1
-rw-r--r--python/vyos/configdict.py80
-rwxr-xr-xsrc/conf_mode/dhcp_server.py13
-rwxr-xr-xsrc/conf_mode/host_name.py6
-rwxr-xr-xsrc/conf_mode/snmp.py4
-rwxr-xr-xsrc/conf_mode/wireguard.py14
-rwxr-xr-xsrc/migration-scripts/ipsec/4-to-533
-rwxr-xr-xsrc/migration-scripts/pppoe-server/0-to-14
-rwxr-xr-xsrc/migration-scripts/quagga/2-to-317
-rwxr-xr-xsrc/op_mode/show_ipsec_sa.py21
11 files changed, 189 insertions, 17 deletions
diff --git a/debian/changelog b/debian/changelog
index 7666cfd68..783080a92 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,16 @@
+vyos-1x (1.2.0-9) unstable; urgency=low
+
+ * T1168: Upgrade: 1,1,7 -> 1.2.0-epa2
+ - keyword change in config
+
+ -- hagbard <vyosdev@derith.de> Mon, 07 Jan 2019 11:42:49 -0800
+
+vyos-1x (1.2.0-8) unstable; urgency=low
+
+ * T1162: WireGuard: Unable to modify tunnels - KeyError: 'state'
+
+ -- hagbard <vyosdev@derith.de> Sun, 06 Jan 2019 15:58:40 -0800
+
vyos-1x (1.2.0-7) unstable; urgency=low
* T1061: Wireguard: Missing option to administrativly shutdown interface
diff --git a/debian/control b/debian/control
index 7061d50ef..93608d888 100644
--- a/debian/control
+++ b/debian/control
@@ -35,6 +35,7 @@ Depends: python3,
lsscsi,
pciutils,
usbutils,
+ procps,
snmp, snmpd,
openssh-server,
ntp,
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
new file mode 100644
index 000000000..157011839
--- /dev/null
+++ b/python/vyos/configdict.py
@@ -0,0 +1,80 @@
+# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+A library for retrieving value dicts from VyOS configs in a declarative fashion.
+
+"""
+
+
+def retrieve_config(path_hash, base_path, config):
+ """
+ Retrieves a VyOS config as a dict according to a declarative description
+
+ The description dict, passed in the first argument, must follow this format:
+ ``field_name : <path, type, [inner_options_dict]>``.
+
+ Supported types are: ``str`` (for normal nodes),
+ ``list`` (returns a list of strings, for multi nodes),
+ ``bool`` (returns True if valueless node exists),
+ ``dict`` (for tag nodes, returns a dict indexed by node names,
+ according to description in the third item of the tuple).
+
+ Args:
+ path_hash (dict): Declarative description of the config to retrieve
+ base_path (list): A base path to prepend to all option paths
+ config (vyos.config.Config): A VyOS config object
+
+ Returns:
+ dict: config dict
+ """
+ config_hash = {}
+
+ for k in path_hash:
+
+ if type(path_hash[k]) != tuple:
+ raise ValueError("In field {0}: expected a tuple, got a value {1}".format(k, str(path_hash[k])))
+ if len(path_hash[k]) < 2:
+ raise ValueError("In field {0}: field description must be a tuple of at least two items, path (list) and type".format(k))
+
+ path = path_hash[k][0]
+ if type(path) != list:
+ raise ValueError("In field {0}: path must be a list, not a {1}".format(k, type(path)))
+
+ typ = path_hash[k][1]
+ if type(typ) != type:
+ raise ValueError("In field {0}: type must be a type, not a {1}".format(k, type(typ)))
+
+ path = base_path + path
+
+ path_str = " ".join(path)
+
+ if typ == str:
+ config_hash[k] = config.return_value(path_str)
+ elif typ == list:
+ config_hash[k] = config.return_values(path_str)
+ elif typ == bool:
+ config_hash[k] = config.exists(path_str)
+ elif typ == dict:
+ try:
+ inner_hash = path_hash[k][2]
+ except IndexError:
+ raise ValueError("The type of the \'{0}\' field is dict, but inner options hash is missing from the tuple".format(k))
+ config_hash[k] = {}
+ nodes = config.list_nodes(path_str)
+ for node in nodes:
+ config_hash[k][node] = retrieve_config(inner_hash, path + [node], config)
+
+ return config_hash
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index 560c80e7f..22ada72a8 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -150,6 +150,12 @@ shared-network {{ network.name }} {
{%- if subnet.domain_name %}
option domain-name "{{ subnet.domain_name }}";
{%- endif -%}
+ {%- if subnet.subnet_parameters %}
+ # The following {{ subnet.subnet_parameters | length }} line(s) were added as subnet-parameters in the CLI and have not been validated
+ {%- for param in subnet.subnet_parameters %}
+ {{ param }}
+ {%- endfor -%}
+ {%- endif %}
{%- if subnet.tftp_server %}
option tftp-server-name "{{ subnet.tftp_server }}";
{%- endif -%}
@@ -570,7 +576,7 @@ def get_config():
#
# deprecate this and issue a warning like we do for DNS forwarding?
if conf.exists('subnet-parameters'):
- config['subnet_parameters'] = conf.return_values('subnet-parameters')
+ subnet['subnet_parameters'] = conf.return_values('subnet-parameters')
# This option is used to identify a TFTP server and, if supported by the client, should have
# the same effect as the server-name declaration. BOOTP clients are unlikely to support this
@@ -767,6 +773,11 @@ def generate(dhcp):
tmpl = jinja2.Template(config_tmpl)
config_text = tmpl.render(dhcp)
+
+ # Please see: https://phabricator.vyos.net/T1129 for quoting of the raw parameters
+ # we can pass to ISC DHCPd
+ config_text = config_text.replace("&quot;",'"')
+
with open(config_file, 'w') as f:
f.write(config_text)
diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py
index 3b3958f7f..030735215 100755
--- a/src/conf_mode/host_name.py
+++ b/src/conf_mode/host_name.py
@@ -100,9 +100,13 @@ def apply(config):
"""Apply configuration"""
os.system("hostnamectl set-hostname --static {0}".format(config["fqdn"]))
- # restart services that use the hostname
+ # Restart services that use the hostname
os.system("systemctl restart rsyslog.service")
+ # If SNMP is running, restart it too
+ if os.system("pgrep snmpd > /dev/null") == 0:
+ os.system("systemctl restart snmpd.service")
+
return None
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 026f6d2f7..d21a2b603 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -21,6 +21,7 @@ import os
import shutil
import stat
import pwd
+import time
import jinja2
import random
@@ -793,6 +794,9 @@ def apply(snmp):
# snmpd, which we see when a magic line appears in this file.
snmpReady = False
while not snmpReady:
+ while not os.path.exists(config_file_user):
+ time.sleep(1)
+
with open(config_file_user, 'r') as f:
for line in f:
# Search for our magic string inside the file
diff --git a/src/conf_mode/wireguard.py b/src/conf_mode/wireguard.py
index f5452579e..c88e9085a 100755
--- a/src/conf_mode/wireguard.py
+++ b/src/conf_mode/wireguard.py
@@ -124,7 +124,6 @@ def get_config():
if c.exists(cnf + ' peer ' + p + ' preshared-key'):
config_data['interfaces'][intfc]['peer'][p]['psk'] = c.return_value(cnf + ' peer ' + p + ' preshared-key')
-
return config_data
def verify(c):
@@ -166,12 +165,13 @@ def apply(c):
### link status up/down aka interface disable
for intf in c['interfaces']:
- if c['interfaces'][intf]['state'] == 'disable':
- sl.syslog(sl.LOG_NOTICE, "disable interface " + intf)
- subprocess.call(['ip l s dev ' + intf + ' down ' + ' &>/dev/null'], shell=True)
- else:
- sl.syslog(sl.LOG_NOTICE, "enable interface " + intf)
- subprocess.call(['ip l s dev ' + intf + ' up ' + ' &>/dev/null'], shell=True)
+ if not c['interfaces'][intf]['status'] == 'delete':
+ if c['interfaces'][intf]['state'] == 'disable':
+ sl.syslog(sl.LOG_NOTICE, "disable interface " + intf)
+ subprocess.call(['ip l s dev ' + intf + ' down ' + ' &>/dev/null'], shell=True)
+ else:
+ sl.syslog(sl.LOG_NOTICE, "enable interface " + intf)
+ subprocess.call(['ip l s dev ' + intf + ' up ' + ' &>/dev/null'], shell=True)
### deletion of a specific interface
for intf in c['interfaces']:
diff --git a/src/migration-scripts/ipsec/4-to-5 b/src/migration-scripts/ipsec/4-to-5
new file mode 100755
index 000000000..b64aa8462
--- /dev/null
+++ b/src/migration-scripts/ipsec/4-to-5
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+
+# log-modes have changed, keyword all to any
+
+import sys
+
+from vyos.configtree import ConfigTree
+
+if (len(sys.argv) < 1):
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ctree = ConfigTree(config_file)
+
+if not ctree.exists(['vpn', 'ipsec', 'logging','log-modes']):
+ # Nothing to do
+ sys.exit(0)
+else:
+ lmodes = ctree.return_values(['vpn', 'ipsec', 'logging','log-modes'])
+ for mode in lmodes:
+ if mode == 'all':
+ ctree.set(['vpn', 'ipsec', 'logging','log-modes'], value='any', replace=True)
+
+ try:
+ open(file_name,'w').write(ctree.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ sys.exit(1)
diff --git a/src/migration-scripts/pppoe-server/0-to-1 b/src/migration-scripts/pppoe-server/0-to-1
index df816a321..bb24211b6 100755
--- a/src/migration-scripts/pppoe-server/0-to-1
+++ b/src/migration-scripts/pppoe-server/0-to-1
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
-# Delete "service ssh allow-root" option
+# Convert "service pppoe-server authentication radius-server node key"
+# to:
+# "service pppoe-server authentication radius-server node secret"
import sys
diff --git a/src/migration-scripts/quagga/2-to-3 b/src/migration-scripts/quagga/2-to-3
index 99d96a0aa..4c1cd86a3 100755
--- a/src/migration-scripts/quagga/2-to-3
+++ b/src/migration-scripts/quagga/2-to-3
@@ -178,6 +178,23 @@ else:
for peer_group in peer_groups:
migrate_neighbor(config, peer_group_path, peer_group)
+ ## Migrate redistribute statements
+ redistribute_path = ['protocols', 'bgp', asn, 'redistribute']
+ if config.exists(redistribute_path):
+ config.set(bgp_path + af_path + ['redistribute'])
+
+ redistributes = config.list_nodes(redistribute_path)
+ for redistribute in redistributes:
+ config.set(bgp_path + af_path + ['redistribute', redistribute])
+ if config.exists(redistribute_path + [redistribute, 'metric']):
+ redist_metric = config.return_value(redistribute_path + [redistribute, 'metric'])
+ config.set(bgp_path + af_path + ['redistribute', redistribute, 'metric'], value=redist_metric)
+ if config.exists(redistribute_path + [redistribute, 'route-map']):
+ redist_route_map = config.return_value(redistribute_path + [redistribute, 'route-map'])
+ config.set(bgp_path + af_path + ['redistribute', redistribute, 'route-map'], value=redist_route_map)
+
+ config.delete(redistribute_path)
+
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py
index 8240c4fd3..792c27dad 100755
--- a/src/op_mode/show_ipsec_sa.py
+++ b/src/op_mode/show_ipsec_sa.py
@@ -1,23 +1,30 @@
#!/usr/bin/env python3
import re
+import sys
import subprocess
import tabulate
import hurry.filesize
def parse_conn_spec(s):
- # Example: ESTABLISHED 14 seconds ago, 10.0.0.2[foo]...10.0.0.1[10.0.0.1]
- return re.search(r'.*ESTABLISHED\s+(.*)ago,\s(.*)\[(.*)\]\.\.\.(.*)\[(.*)\].*', s).groups()
+ try:
+ # Example: ESTABLISHED 14 seconds ago, 10.0.0.2[foo]...10.0.0.1[10.0.0.1]
+ return re.search(r'.*ESTABLISHED\s+(.*)ago,\s(.*)\[(.*)\]\.\.\.(.*)\[(.*)\].*', s).groups()
+ except AttributeError:
+ # No active SAs found, so we have nothing to display
+ print("No established security associations found.")
+ print("Use \"show vpn ipsec sa\" to view inactive and connecting tunnels.")
+ sys.exit(0)
def parse_ike_line(s):
try:
# Example with traffic: AES_CBC_256/HMAC_SHA2_256_128/ECP_521, 2382660 bytes_i (1789 pkts, 2s ago), 2382660 bytes_o ...
- return re.search(r'.*:\s+(.*)\/(.*)\/(.*),\s+(\d+)\s+bytes_i\s\(.*pkts,.*\),\s+(\d+)\s+bytes_o', s).groups()
+ return re.search(r'.*:\s+(.*\/.*(?:\/.*)?),\s+(\d+)\s+bytes_i\s\(.*pkts,.*\),\s+(\d+)\s+bytes_o', s).groups()
except AttributeError:
try:
# Example without traffic: 3DES_CBC/HMAC_MD5_96/MODP_1024, 0 bytes_i, 0 bytes_o, rekeying in 45 minutes
- return re.search(r'.*:\s+(.*)\/(.*)\/(.*),\s+(\d+)\s+bytes_i,\s+(\d+)\s+bytes_o,\s+rekeying', s).groups()
+ return re.search(r'.*:\s+(.*\/.*(?:\/.*)?),\s+(\d+)\s+bytes_i,\s+(\d+)\s+bytes_o,\s+rekeying', s).groups()
except AttributeError:
return (None, None, None, None, None)
@@ -25,7 +32,7 @@ def parse_ike_line(s):
# Get a list of all configured connections
with open('/etc/ipsec.conf', 'r') as f:
config = f.read()
- connections = re.findall(r'conn\s([^\s]+)\s*\n', config)
+ connections = set(re.findall(r'conn\s([^\s]+)\s*\n', config))
connections = list(filter(lambda s: s != '%default', connections))
status_data = []
@@ -39,13 +46,13 @@ for conn in connections:
time, _, _, ip, id = parse_conn_spec(status)
if ip == id:
id = None
- enc, hash, dh, bytes_in, bytes_out = parse_ike_line(status)
+ enc, bytes_in, bytes_out = parse_ike_line(status)
# Convert bytes to human-readable units
bytes_in = hurry.filesize.size(int(bytes_in))
bytes_out = hurry.filesize.size(int(bytes_out))
- status_line = [conn, "up", time, "{0}/{1}".format(bytes_in, bytes_out), ip, id, "{0}/{1}/{2}".format(enc, hash, dh)]
+ status_line = [conn, "up", time, "{0}/{1}".format(bytes_in, bytes_out), ip, id, enc]
except Exception as e:
print(status)
raise e