summaryrefslogtreecommitdiff
path: root/python/vyos
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos')
-rw-r--r--python/vyos/configquery.py90
-rw-r--r--python/vyos/configverify.py2
-rw-r--r--python/vyos/frr.py20
-rw-r--r--python/vyos/ifconfig/bridge.py6
-rw-r--r--python/vyos/ifconfig/interface.py39
-rw-r--r--python/vyos/util.py30
6 files changed, 158 insertions, 29 deletions
diff --git a/python/vyos/configquery.py b/python/vyos/configquery.py
new file mode 100644
index 000000000..ed7346f1f
--- /dev/null
+++ b/python/vyos/configquery.py
@@ -0,0 +1,90 @@
+# Copyright 2021 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 small library that allows querying existence or value(s) of config
+settings from op mode, and execution of arbitrary op mode commands.
+'''
+
+from subprocess import STDOUT
+from vyos.util import popen
+
+
+class ConfigQueryError(Exception):
+ pass
+
+class GenericConfigQuery:
+ def __init__(self):
+ pass
+
+ def exists(self, path: list):
+ raise NotImplementedError
+
+ def value(self, path: list):
+ raise NotImplementedError
+
+ def values(self, path: list):
+ raise NotImplementedError
+
+class GenericOpRun:
+ def __init__(self):
+ pass
+
+ def run(self, path: list, **kwargs):
+ raise NotImplementedError
+
+class CliShellApiConfigQuery(GenericConfigQuery):
+ def __init__(self):
+ super().__init__()
+
+ def exists(self, path: list):
+ cmd = ' '.join(path)
+ (_, err) = popen(f'cli-shell-api existsActive {cmd}')
+ if err:
+ return False
+ return True
+
+ def value(self, path: list):
+ cmd = ' '.join(path)
+ (out, err) = popen(f'cli-shell-api returnActiveValue {cmd}')
+ if err:
+ raise ConfigQueryError('No value for given path')
+ return out
+
+ def values(self, path: list):
+ cmd = ' '.join(path)
+ (out, err) = popen(f'cli-shell-api returnActiveValues {cmd}')
+ if err:
+ raise ConfigQueryError('No values for given path')
+ return out
+
+class VbashOpRun(GenericOpRun):
+ def __init__(self):
+ super().__init__()
+
+ def run(self, path: list, **kwargs):
+ cmd = ' '.join(path)
+ (out, err) = popen(f'. /opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run; _vyatta_op_run {cmd}', stderr=STDOUT, **kwargs)
+ if err:
+ raise ConfigQueryError(out)
+ return out
+
+def query_context(config_query_class=CliShellApiConfigQuery,
+ op_run_class=VbashOpRun):
+ query = config_query_class()
+ run = op_run_class()
+ return query, run
+
+
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 7cf2cb8f9..718b7445d 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -80,7 +80,7 @@ def verify_vrf(config):
recurring validation of VRF configuration.
"""
from netifaces import interfaces
- if 'vrf' in config:
+ if 'vrf' in config and config['vrf'] != 'default':
if config['vrf'] not in interfaces():
raise ConfigError('VRF "{vrf}" does not exist'.format(**config))
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index 69c7a14ce..ec0bef37a 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -86,6 +86,7 @@ _frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd',
path_vtysh = '/usr/bin/vtysh'
path_frr_reload = '/usr/lib/frr/frr-reload.py'
+path_config = '/run/frr'
class FrrError(Exception):
@@ -207,6 +208,25 @@ def reload_configuration(config, daemon=None):
return output
+def save_configuration(daemon=None):
+ """Save FRR configuration to /run/frr/{daemon}.conf
+ It save configuration on each commit.
+ """
+ if daemon and daemon not in _frr_daemons:
+ raise ValueError(f'The specified daemon type is not supported {repr(daemon)}')
+
+ cmd = f"{path_vtysh} -d {daemon} -c 'show run no-header'"
+ output, code = util.popen(cmd, stderr=util.STDOUT)
+ if code:
+ raise OSError(code, output)
+
+ with open(f"{path_config}/{daemon}.conf", "w") as f:
+ f.write(output)
+ config = output
+
+ return config
+
+
def execute(command):
""" Run commands inside vtysh
command: str containing commands to execute inside a vtysh session
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 600bd3db8..14f64a8de 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -312,9 +312,15 @@ class BridgeIf(Interface):
# not have any addresses configured by CLI so just flush any
# remaining ones
lower.flush_addrs()
+
# enslave interface port to bridge
self.add_port(interface)
+ # always set private-vlan/port isolation
+ tmp = dict_search('isolated', interface_config)
+ value = 'on' if (tmp != None) else 'off'
+ lower.set_port_isolation(value)
+
# set bridge port path cost
if 'cost' in interface_config:
value = interface_config.get('cost')
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index fe6a3c95e..a61985da5 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -113,6 +113,10 @@ class Interface(Control):
'convert': lambda name: name if name else '',
'shellcmd': 'ip link set dev {ifname} alias "{value}"',
},
+ 'bridge_port_isolation': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'shellcmd': 'bridge link set dev {ifname} isolated {value}',
+ },
'mac': {
'validate': assert_mac,
'shellcmd': 'ip link set dev {ifname} address {value}',
@@ -689,6 +693,20 @@ class Interface(Control):
"""
self.set_interface('path_priority', priority)
+ def set_port_isolation(self, on_or_off):
+ """
+ Controls whether a given port will be isolated, which means it will be
+ able to communicate with non-isolated ports only. By default this flag
+ is off.
+
+ Use enable=1 to enable or enable=0 to disable
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth1').set_port_isolation('on')
+ """
+ self.set_interface('bridge_port_isolation', on_or_off)
+
def set_proxy_arp(self, enable):
"""
Set per interface proxy ARP configuration
@@ -1307,27 +1325,6 @@ class VLANIf(Interface):
""" Specific class which abstracts 802.1q and 802.1ad (Q-in-Q) VLAN interfaces """
iftype = 'vlan'
- def remove(self):
- """
- Remove interface from operating system. Removing the interface
- deconfigures all assigned IP addresses and clear possible DHCP(v6)
- client processes.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> VLANIf('eth0.10').remove
- """
- # Do we have sub interfaces (VLANs)? As interfaces need to be deleted
- # "in order" starting from Q-in-Q we delete them first.
- for upper in glob(f'/sys/class/net/{self.ifname}/upper*'):
- # an upper interface could be named: upper_bond0.1000.1100, thus
- # we need top drop the upper_ prefix
- vif_c = os.path.basename(upper)
- vif_c = vif_c.replace('upper_', '')
- VLANIf(vif_c).remove()
-
- super().remove()
-
def _create(self):
# bail out early if interface already exists
if self.exists(f'{self.ifname}'):
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 17a7dda91..e2f4b8fc4 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -630,23 +630,26 @@ def find_device_file(device):
return None
-def dict_search(path, dict):
- """ Traverse Python dictionary (dict) delimited by dot (.).
+def dict_search(path, my_dict):
+ """ Traverse Python dictionary (my_dict) delimited by dot (.).
Return value of key if found, None otherwise.
- This is faster implementation then jmespath.search('foo.bar', dict)"""
+ This is faster implementation then jmespath.search('foo.bar', my_dict)"""
+ if not isinstance(my_dict, dict) or not path:
+ return None
+
parts = path.split('.')
inside = parts[:-1]
if not inside:
- if path not in dict:
+ if path not in my_dict:
return None
- return dict[path]
- c = dict
+ return my_dict[path]
+ c = my_dict
for p in parts[:-1]:
c = c.get(p, {})
return c.get(parts[-1], None)
-def get_json_iface_options(interface):
+def get_interface_config(interface):
""" Returns the used encapsulation protocol for given interface.
If interface does not exist, None is returned.
"""
@@ -655,3 +658,16 @@ def get_json_iface_options(interface):
from json import loads
tmp = loads(cmd(f'ip -d -j link show {interface}'))[0]
return tmp
+
+def get_all_vrfs():
+ """ Return a dictionary of all system wide known VRF instances """
+ from json import loads
+ tmp = loads(cmd('ip -j vrf list'))
+ # Result is of type [{"name":"red","table":1000},{"name":"blue","table":2000}]
+ # so we will re-arrange it to a more nicer representation:
+ # {'red': {'table': 1000}, 'blue': {'table': 2000}}
+ data = {}
+ for entry in tmp:
+ name = entry.pop('name')
+ data[name] = entry
+ return data