diff options
-rw-r--r-- | data/templates/https/nginx.default.tmpl | 7 | ||||
-rw-r--r-- | data/templates/l2tp/chap-secrets.tmpl | 7 | ||||
-rw-r--r-- | data/templates/pppoe-server/chap-secrets.tmpl | 5 | ||||
-rw-r--r-- | data/templates/pptp/chap-secrets.tmpl | 2 | ||||
-rw-r--r-- | interface-definitions/https.xml.in | 9 | ||||
-rw-r--r-- | interface-definitions/interfaces-tunnel.xml.in | 2 | ||||
-rw-r--r-- | python/vyos/util.py | 50 | ||||
-rwxr-xr-x | src/conf_mode/https.py | 10 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-bonding.py | 19 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-bridge.py | 3 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-dummy.py | 11 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-geneve.py | 13 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-l2tpv3.py | 23 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-openvpn.py | 10 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-pseudo-ethernet.py | 8 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-vxlan.py | 11 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-wireguard.py | 12 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-wireless.py | 10 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-wirelessmodem.py | 9 | ||||
-rwxr-xr-x | src/migration-scripts/https/1-to-2 | 54 |
20 files changed, 219 insertions, 56 deletions
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl index b6d3aaf83..33f7b2820 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.tmpl @@ -51,10 +51,17 @@ server { error_page 501 502 503 =200 @50*_json; +{% if api_somewhere %} + location @50*_json { + default_type application/json; + return 200 '{"error": "service https api unavailable at this proxy address: set service https api-restrict virtual-host"}'; + } +{% else %} location @50*_json { default_type application/json; return 200 '{"error": "Start service in configuration mode: set service https api"}'; } +{% endif %} } diff --git a/data/templates/l2tp/chap-secrets.tmpl b/data/templates/l2tp/chap-secrets.tmpl index 323528b3c..0db295fdc 100644 --- a/data/templates/l2tp/chap-secrets.tmpl +++ b/data/templates/l2tp/chap-secrets.tmpl @@ -1,11 +1,10 @@ # username server password acceptable local IP addresses shaper {% for user in authentication['local-users'] %} {% if authentication['local-users'][user]['state'] == 'enabled' %} -{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\ -{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}} +{% if authentication['local-users'][user]['upload'] and authentication['local-users'][user]['download'] %} +{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {{ authentication['local-users'][user]['download'] }} / {{ authentication['local-users'][user]['upload'] }} {% else %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}} +{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {% endif %} {% endif %} {% endfor %} diff --git a/data/templates/pppoe-server/chap-secrets.tmpl b/data/templates/pppoe-server/chap-secrets.tmpl index 808debccb..907ac6ed7 100644 --- a/data/templates/pppoe-server/chap-secrets.tmpl +++ b/data/templates/pppoe-server/chap-secrets.tmpl @@ -2,10 +2,9 @@ {% for user in authentication['local-users'] %} {% if authentication['local-users'][user]['state'] == 'enabled' %} {% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\ -{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}} +{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {{ authentication['local-users'][user]['download'] }} / {{ authentication['local-users'][user]['upload'] }} {% else %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}} +{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {% endif %} {% endif %} {% endfor %} diff --git a/data/templates/pptp/chap-secrets.tmpl b/data/templates/pptp/chap-secrets.tmpl index 6bfa2d64e..f93f4607b 100644 --- a/data/templates/pptp/chap-secrets.tmpl +++ b/data/templates/pptp/chap-secrets.tmpl @@ -1,6 +1,6 @@ # username server password acceptable local IP addresses {% for user in authentication['local-users'] %} {% if authentication['local-users'][user]['state'] == 'enabled' %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}} +{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {% endif %} {% endfor %} diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index 49bd25b82..9bb96f1f0 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- HTTPS configuration --> <interfaceDefinition> - <syntaxVersion component='https' version='1'></syntaxVersion> + <syntaxVersion component='https' version='2'></syntaxVersion> <node name="service"> <children> <node name="https" owner="${vyos_conf_scripts_dir}/https.py"> @@ -111,6 +111,13 @@ <hidden/> </properties> </leafNode> + </children> + </node> + <node name="api-restrict"> + <properties> + <help>Restrict api proxy to subset of virtual hosts</help> + </properties> + <children> <leafNode name="virtual-host"> <properties> <help>Restrict proxy to virtual host(s)</help> diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index 3ba82067f..e1ac60319 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -107,7 +107,7 @@ </leafNode> <leafNode name="encapsulation"> <properties> - <help>Ignore link state changes</help> + <help>Encapsulation of this tunnel interface</help> <completionHelp> <list>gre gre-bridge ipip sit ipip6 ip6ip6 ip6gre</list> </completionHelp> diff --git a/python/vyos/util.py b/python/vyos/util.py index fa2b4dd99..16cfae92d 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -18,10 +18,6 @@ import re import sys from subprocess import Popen, PIPE, STDOUT, DEVNULL - -# debugging - - def debug(flag): return flag if os.path.isfile(f'/tmp/vyos.{flag}.debug') else '' @@ -31,11 +27,9 @@ def debug_msg(message, section=''): print(f'DEBUG/{section:<6} {message}') -# commands - -# popen does not raise -# it returns the output of the command and the error code -def popen(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None): +def popen(command, section='', shell=None, input=None, timeout=None, env=None, + universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None): + """ popen does not raise, returns the output and error code of command """ use_shell = shell if shell is None: use_shell = True if ' ' in command else False @@ -52,9 +46,10 @@ def popen(command, section='', shell=None, input=None, timeout=None, env=None, u debug_msg(f"returned:\n{decoded}", section) return decoded, p.returncode -# run does not raise -# it returns the error code -def run(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None): + +def run(command, section='', shell=None, input=None, timeout=None, env=None, + universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None): + """ does not raise exception on error, returns error code """ _, code = popen( command, section, stdout=stdout, stderr=stderr, @@ -65,9 +60,11 @@ def run(command, section='', shell=None, input=None, timeout=None, env=None, uni ) return code -# cmd does raise -# it returns the output -def cmd(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None, raising=None, message=''): + +def cmd(command, section='', shell=None, input=None, timeout=None, env=None, + universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None, + raising=None, message=''): + """ does raise exception, returns output of command """ decoded, code = popen( command, section, stdout=stdout, stderr=stderr, @@ -89,9 +86,6 @@ def cmd(command, section='', shell=None, input=None, timeout=None, env=None, uni return decoded -# file manipulation - - def read_file(path): """ Read a file to string """ with open(path, 'r') as f: @@ -109,7 +103,6 @@ def chown_file(path, user, group): gid = getgrnam(group).gr_gid os.chown(path, uid, gid) - def chmod_x(path): """ make file executable """ from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH @@ -328,3 +321,22 @@ def mac2eui64(mac, prefix=None): return str(net[euil]) except: # pylint: disable=bare-except return + +def is_bridge_member(interface): + """ + Checks if passed interfaces is part of a bridge device or not. + + Returns a tuple: + False, None -> Not part of a bridge + True, bridge-name -> If it is assigned to a bridge + """ + from vyos.config import Config + c = Config() + base = ['interfaces', 'bridge'] + for bridge in c.list_nodes(base): + members = c.list_nodes(base + [bridge, 'member', 'interface']) + if interface in members: + return (True, bridge) + + return False, None + diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index e46f1a4e7..777792229 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -88,14 +88,16 @@ def get_config(): # certbot organizes certificates by first domain sb['certbot_dir'] = certbot_domains[0] + api_somewhere = False api_data = {} if conf.exists('api'): + api_somewhere = True api_data = vyos.defaults.api_data if conf.exists('api port'): port = conf.return_value('api port') api_data['port'] = port - if conf.exists('api virtual-host'): - vhosts = conf.return_values('api virtual-host') + if conf.exists('api-restrict virtual-host'): + vhosts = conf.return_values('api-restrict virtual-host') api_data['vhost'] = vhosts[:] if api_data: @@ -110,7 +112,9 @@ def get_config(): if block['id'] in vhost_list: block['api'] = api_data - https = {'server_block_list' : server_block_list, 'certbot': certbot} + https = {'server_block_list' : server_block_list, + 'api_somewhere': api_somewhere, + 'certbot': certbot} return https def verify(https): diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 19f43f725..6a002bc06 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -24,9 +24,8 @@ from vyos.ifconfig import BondIf from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, vlan_to_dict from vyos.config import Config +from vyos.util import run, is_bridge_member from vyos import ConfigError -from vyos.util import run - default_config_data = { 'address': [], @@ -278,17 +277,23 @@ def get_config(): def verify(bond): + if bond['deleted']: + interface = bond['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) + return None + if len (bond['arp_mon_tgt']) > 16: raise ConfigError('The maximum number of targets that can be specified is 16') if bond['primary']: if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']: raise ConfigError('Mode dependency failed, primary not supported ' \ - 'in this mode.'.format()) - - if bond['primary'] not in bond['member']: - raise ConfigError('Interface "{}" is not part of the bond' \ - .format(bond['primary'])) + 'in mode "{}"!'.format(bond['mode'])) vrf_name = bond['vrf'] if vrf_name and vrf_name not in interfaces(): diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 28e5957e4..79247ee51 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -243,6 +243,9 @@ def verify(bridge): if intf['name'] not in interfaces(): raise ConfigError('Can not add non existing interface "{}" to bridge "{}"'.format(intf['name'], bridge['intf'])) + if intf['name'] == 'lo': + raise ConfigError('Loopback interface "lo" can not be added to a bridge') + # bridge members are not allowed to be bond members, too for intf in bridge['member']: for bond in conf.list_nodes('interfaces bonding'): diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py index b7b75517d..a256103af 100755 --- a/src/conf_mode/interfaces-dummy.py +++ b/src/conf_mode/interfaces-dummy.py @@ -23,6 +23,7 @@ from netifaces import interfaces from vyos.ifconfig import DummyIf from vyos.configdict import list_diff from vyos.config import Config +from vyos.util import is_bridge_member from vyos import ConfigError default_config_data = { @@ -78,6 +79,16 @@ def get_config(): return dummy def verify(dummy): + if dummy['deleted']: + interface = dummy['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) + return None + vrf_name = dummy['vrf'] if vrf_name and vrf_name not in interfaces(): raise ConfigError(f'VRF "{vrf_name}" does not exist') diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index eaa678d3e..e47473d76 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -18,11 +18,12 @@ import os from sys import exit from copy import deepcopy +from netifaces import interfaces from vyos.config import Config -from vyos.ifconfig import GeneveIf, Interface +from vyos.ifconfig import GeneveIf +from vyos.util import is_bridge_member from vyos import ConfigError -from netifaces import interfaces default_config_data = { 'address': [], @@ -92,7 +93,13 @@ def get_config(): def verify(geneve): if geneve['deleted']: - # bail out early + interface = geneve['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None if not geneve['remote']: diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 4b5fc8306..0400cb849 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -22,7 +22,7 @@ from copy import deepcopy from vyos.config import Config from vyos.ifconfig import L2TPv3If, Interface from vyos import ConfigError -from vyos.util import run +from vyos.util import run, is_bridge_member from netifaces import interfaces default_config_data = { @@ -154,27 +154,34 @@ def get_config(): def verify(l2tpv3): + interface = l2tpv3['intf'] + if l2tpv3['deleted']: - # bail out early + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None if not l2tpv3['local_address']: - raise ConfigError('Must configure the l2tpv3 local-ip for {}'.format(l2tpv3['intf'])) + raise ConfigError(f'Must configure the l2tpv3 local-ip for {interface}') if not l2tpv3['remote_address']: - raise ConfigError('Must configure the l2tpv3 remote-ip for {}'.format(l2tpv3['intf'])) + raise ConfigError(f'Must configure the l2tpv3 remote-ip for {interface}') if not l2tpv3['tunnel_id']: - raise ConfigError('Must configure the l2tpv3 tunnel-id for {}'.format(l2tpv3['intf'])) + raise ConfigError(f'Must configure the l2tpv3 tunnel-id for {interface}') if not l2tpv3['peer_tunnel_id']: - raise ConfigError('Must configure the l2tpv3 peer-tunnel-id for {}'.format(l2tpv3['intf'])) + raise ConfigError(f'Must configure the l2tpv3 peer-tunnel-id for {interface}') if not l2tpv3['session_id']: - raise ConfigError('Must configure the l2tpv3 session-id for {}'.format(l2tpv3['intf'])) + raise ConfigError(f'Must configure the l2tpv3 session-id for {interface}') if not l2tpv3['peer_session_id']: - raise ConfigError('Must configure the l2tpv3 peer-session-id for {}'.format(l2tpv3['intf'])) + raise ConfigError(f'Must configure the l2tpv3 peer-session-id for {interface}') return None diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 1fe1143cd..e9b40bb38 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -31,7 +31,7 @@ from shutil import rmtree from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import VTunIf -from vyos.util import process_running, cmd +from vyos.util import process_running, cmd, is_bridge_member from vyos.validate import is_addr_assigned from vyos import ConfigError @@ -444,8 +444,16 @@ def get_config(): def verify(openvpn): if openvpn['deleted']: + interface = openvpn['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None + if not openvpn['mode']: raise ConfigError('Must specify OpenVPN operation mode') diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 56d4fdfc3..50b5a12a0 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -24,6 +24,7 @@ from vyos.ifconfig import MACVLANIf from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, vlan_to_dict from vyos.config import Config +from vyos.util import is_bridge_member from vyos import ConfigError default_config_data = { @@ -217,6 +218,13 @@ def get_config(): def verify(peth): if peth['deleted']: + interface = peth['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None if not peth['link']: diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 1f9636729..b9bfb242a 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -18,11 +18,12 @@ import os from sys import exit from copy import deepcopy +from netifaces import interfaces from vyos.config import Config from vyos.ifconfig import VXLANIf, Interface +from vyos.util import is_bridge_member from vyos import ConfigError -from netifaces import interfaces default_config_data = { 'address': [], @@ -148,7 +149,13 @@ def get_config(): def verify(vxlan): if vxlan['deleted']: - # bail out early + interface = vxlan['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None if vxlan['mtu'] < 1500: diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 4fa0dd8c0..54121a6c1 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -24,7 +24,7 @@ from netifaces import interfaces from vyos import ConfigError from vyos.config import Config from vyos.configdict import list_diff -from vyos.util import run +from vyos.util import run, is_bridge_member from vyos.ifconfig import WireGuardIf kdir = r'/config/auth/wireguard' @@ -179,6 +179,16 @@ def verify(c): if not c: return None + if c['delete']: + interface = c['intfc'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) + return None + if not os.path.exists(c['pk']): raise ConfigError( "No keys found, generate them by executing: \'run generate wireguard [keypair|named-keypairs]\'") diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 188d0ee22..709085b0f 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -29,7 +29,7 @@ from vyos.configdict import list_diff, vlan_to_dict from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import WiFiIf from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config -from vyos.util import process_running, chmod_x, chown_file, run +from vyos.util import process_running, chmod_x, chown_file, run, is_bridge_member from vyos import ConfigError user = 'root' @@ -554,8 +554,16 @@ def get_config(): def verify(wifi): if wifi['deleted']: + interface = wifi['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None + if wifi['op_mode'] != 'monitor' and not wifi['ssid']: raise ConfigError('SSID must be set for {}'.format(wifi['intf'])) diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py index 5e10cfce7..49445aaa4 100755 --- a/src/conf_mode/interfaces-wirelessmodem.py +++ b/src/conf_mode/interfaces-wirelessmodem.py @@ -23,7 +23,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.defaults import directories as vyos_data_dir -from vyos.util import chown_file, chmod_x, cmd, run +from vyos.util import chown_file, chmod_x, cmd, run, is_bridge_member from vyos import ConfigError default_config_data = { @@ -115,6 +115,13 @@ def get_config(): def verify(wwan): if wwan['deleted']: + interface = wwan['intf'] + is_member, bridge = is_bridge_member(interface) + if is_member: + # can not use a f'' formatted-string here as bridge would not get + # expanded in the print statement + raise ConfigError('Can not delete interface "{0}" as it ' \ + 'is a member of bridge "{1}"!'.format(interface, bridge)) return None if not wwan['apn']: diff --git a/src/migration-scripts/https/1-to-2 b/src/migration-scripts/https/1-to-2 new file mode 100755 index 000000000..b1cf37ea6 --- /dev/null +++ b/src/migration-scripts/https/1-to-2 @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# * Move 'api virtual-host' list to 'api-restrict virtual-host' so it +# is owned by https.py instead of http-api.py + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 2): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +old_base = ['service', 'https', 'api', 'virtual-host'] +if not config.exists(old_base): + # Nothing to do + sys.exit(0) +else: + new_base = ['service', 'https', 'api-restrict', 'virtual-host'] + config.set(new_base) + + names = config.return_values(old_base) + for name in names: + config.set(new_base, value=name, replace=False) + + config.delete(old_base) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) |