diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/configquery.py | 90 | ||||
-rw-r--r-- | python/vyos/configverify.py | 2 | ||||
-rw-r--r-- | python/vyos/frr.py | 20 | ||||
-rw-r--r-- | python/vyos/ifconfig/bridge.py | 6 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 39 | ||||
-rw-r--r-- | python/vyos/util.py | 30 |
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 |