summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/vyos/configdep.py2
-rw-r--r--python/vyos/configdict.py14
-rw-r--r--python/vyos/ifconfig/bond.py13
-rw-r--r--python/vyos/ifconfig/ethernet.py34
-rw-r--r--python/vyos/nat.py2
-rw-r--r--python/vyos/progressbar.py70
-rw-r--r--python/vyos/qos/trafficshaper.py9
-rw-r--r--python/vyos/remote.py44
-rw-r--r--python/vyos/template.py3
-rw-r--r--python/vyos/utils/dict.py59
-rw-r--r--python/vyos/utils/io.py39
11 files changed, 217 insertions, 72 deletions
diff --git a/python/vyos/configdep.py b/python/vyos/configdep.py
index 05d9a3fa3..8a28811eb 100644
--- a/python/vyos/configdep.py
+++ b/python/vyos/configdep.py
@@ -43,7 +43,7 @@ def canon_name_of_path(path: str) -> str:
return canon_name(script)
def caller_name() -> str:
- return stack()[-1].filename
+ return stack()[2].filename
def read_dependency_dict(dependency_dir: str = dependency_dir) -> dict:
res = {}
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 71a06b625..075ffe466 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -258,10 +258,10 @@ def has_address_configured(conf, intf):
old_level = conf.get_level()
conf.set_level([])
- intfpath = 'interfaces ' + Section.get_config_path(intf)
- if ( conf.exists(f'{intfpath} address') or
- conf.exists(f'{intfpath} ipv6 address autoconf') or
- conf.exists(f'{intfpath} ipv6 address eui64') ):
+ intfpath = ['interfaces', Section.get_config_path(intf)]
+ if (conf.exists([intfpath, 'address']) or
+ conf.exists([intfpath, 'ipv6', 'address', 'autoconf']) or
+ conf.exists([intfpath, 'ipv6', 'address', 'eui64'])):
ret = True
conf.set_level(old_level)
@@ -279,8 +279,7 @@ def has_vrf_configured(conf, intf):
old_level = conf.get_level()
conf.set_level([])
- tmp = ['interfaces', Section.get_config_path(intf), 'vrf']
- if conf.exists(tmp):
+ if conf.exists(['interfaces', Section.get_config_path(intf), 'vrf']):
ret = True
conf.set_level(old_level)
@@ -298,8 +297,7 @@ def has_vlan_subinterface_configured(conf, intf):
ret = False
intfpath = ['interfaces', Section.section(intf), intf]
- if ( conf.exists(intfpath + ['vif']) or
- conf.exists(intfpath + ['vif-s'])):
+ if (conf.exists(intfpath + ['vif']) or conf.exists(intfpath + ['vif-s'])):
ret = True
return ret
diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py
index d1d7d48c4..45e6e4c16 100644
--- a/python/vyos/ifconfig/bond.py
+++ b/python/vyos/ifconfig/bond.py
@@ -92,6 +92,19 @@ class BondIf(Interface):
}
}}
+ @staticmethod
+ def get_inherit_bond_options() -> list:
+ """
+ Returns list of option
+ which are inherited from bond interface to member interfaces
+ :return: List of interface options
+ :rtype: list
+ """
+ options = [
+ 'mtu'
+ ]
+ return options
+
def remove(self):
"""
Remove interface from operating system. Removing the interface
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 285542057..aa1e87744 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -75,6 +75,40 @@ class EthernetIf(Interface):
},
}}
+ @staticmethod
+ def get_bond_member_allowed_options() -> list:
+ """
+ Return list of options which are allowed for changing,
+ when interface is a bond member
+ :return: List of interface options
+ :rtype: list
+ """
+ bond_allowed_sections = [
+ 'description',
+ 'disable',
+ 'disable_flow_control',
+ 'disable_link_detect',
+ 'duplex',
+ 'eapol.ca_certificate',
+ 'eapol.certificate',
+ 'eapol.passphrase',
+ 'mirror.egress',
+ 'mirror.ingress',
+ 'offload.gro',
+ 'offload.gso',
+ 'offload.lro',
+ 'offload.rfs',
+ 'offload.rps',
+ 'offload.sg',
+ 'offload.tso',
+ 'redirect',
+ 'ring_buffer.rx',
+ 'ring_buffer.tx',
+ 'speed',
+ 'hw_id'
+ ]
+ return bond_allowed_sections
+
def __init__(self, ifname, **kargs):
super().__init__(ifname, **kargs)
self.ethtool = Ethtool(ifname)
diff --git a/python/vyos/nat.py b/python/vyos/nat.py
index 0887bfdf1..e32b5ae74 100644
--- a/python/vyos/nat.py
+++ b/python/vyos/nat.py
@@ -170,7 +170,7 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
operator = ''
if addr_prefix[:1] == '!':
operator = '!='
- addr_prefix = addr[1:]
+ addr_prefix = addr_prefix[1:]
output.append(f'ip6 {prefix}addr {operator} {addr_prefix}')
port = dict_search_args(side_conf, 'port')
diff --git a/python/vyos/progressbar.py b/python/vyos/progressbar.py
new file mode 100644
index 000000000..1793c445b
--- /dev/null
+++ b/python/vyos/progressbar.py
@@ -0,0 +1,70 @@
+# Copyright 2023 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/>.
+
+import math
+import os
+import signal
+import subprocess
+import sys
+
+from vyos.utils.io import print_error
+
+class Progressbar:
+ def __init__(self, step=None):
+ self.total = 0.0
+ self.step = step
+ def __enter__(self):
+ # Recalculate terminal width with every window resize.
+ signal.signal(signal.SIGWINCH, lambda signum, frame: self._update_cols())
+ # Disable line wrapping to prevent the staircase effect.
+ subprocess.run(['tput', 'rmam'], check=False)
+ self._update_cols()
+ # Print an empty progressbar with entry.
+ self.progress(0, 1)
+ return self
+ def __exit__(self, exc_type, kexc_val, exc_tb):
+ # Revert to the default SIGWINCH handler (ie nothing).
+ signal.signal(signal.SIGWINCH, signal.SIG_DFL)
+ # Reenable line wrapping.
+ subprocess.run(['tput', 'smam'], check=False)
+ def _update_cols(self):
+ # `os.get_terminal_size()' is fast enough for our purposes.
+ self.col = max(os.get_terminal_size().columns - 15, 20)
+ def increment(self):
+ """
+ Stateful progressbar taking the step fraction at init and no input at
+ callback (for FTP)
+ """
+ if self.step:
+ if self.total < 1.0:
+ self.total += self.step
+ if self.total >= 1.0:
+ self.total = 1.0
+ # Ignore superfluous calls caused by fuzzy FTP size calculations.
+ self.step = None
+ self.progress(self.total, 1.0)
+ def progress(self, done, total):
+ """
+ Stateless progressbar taking no input at init and current progress with
+ final size at callback (for SSH)
+ """
+ if done <= total:
+ length = math.ceil(self.col * done / total)
+ percentage = str(math.ceil(100 * done / total)).rjust(3)
+ # Carriage return at the end will make sure the line will get overwritten.
+ print_error(f'[{length * "#"}{(self.col - length) * "_"}] {percentage}%', end='\r')
+ # Print a newline to make sure the full progressbar doesn't get overwritten by the next line.
+ if done == total:
+ print_error()
diff --git a/python/vyos/qos/trafficshaper.py b/python/vyos/qos/trafficshaper.py
index c63c7cf39..0d5f9a8a1 100644
--- a/python/vyos/qos/trafficshaper.py
+++ b/python/vyos/qos/trafficshaper.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2022-2023 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
@@ -89,6 +89,10 @@ class TrafficShaper(QoSBase):
if 'priority' in cls_config:
priority = cls_config['priority']
tmp += f' prio {priority}'
+
+ if 'ceiling' in cls_config:
+ f_ceil = self._rate_convert(cls_config['ceiling'])
+ tmp += f' ceil {f_ceil}'
self._cmd(tmp)
tmp = f'tc qdisc replace dev {self._interface} parent {self._parent:x}:{cls:x} sfq'
@@ -102,6 +106,9 @@ class TrafficShaper(QoSBase):
if 'priority' in config['default']:
priority = config['default']['priority']
tmp += f' prio {priority}'
+ if 'ceiling' in config['default']:
+ f_ceil = self._rate_convert(config['default']['ceiling'])
+ tmp += f' ceil {f_ceil}'
self._cmd(tmp)
tmp = f'tc qdisc replace dev {self._interface} parent {self._parent:x}:{default_minor_id:x} sfq'
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index cf731c881..1ca8a9530 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -32,9 +32,8 @@ from requests import Session
from requests.adapters import HTTPAdapter
from requests.packages.urllib3 import PoolManager
+from vyos.progressbar import Progressbar
from vyos.utils.io import ask_yes_no
-from vyos.utils.io import make_incremental_progressbar
-from vyos.utils.io import make_progressbar
from vyos.utils.io import print_error
from vyos.utils.misc import begin
from vyos.utils.process import cmd
@@ -131,16 +130,16 @@ class FtpC:
if self.secure:
conn.prot_p()
# Almost all FTP servers support the `SIZE' command.
+ size = conn.size(self.path)
if self.check_space:
- check_storage(path, conn.size(self.path))
+ check_storage(path, size)
# No progressbar if we can't determine the size or if the file is too small.
if self.progressbar and size and size > CHUNK_SIZE:
- progress = make_incremental_progressbar(CHUNK_SIZE / size)
- next(progress)
- callback = lambda block: begin(f.write(block), next(progress))
+ with Progressbar(CHUNK_SIZE / size) as p:
+ callback = lambda block: begin(f.write(block), p.increment())
+ conn.retrbinary('RETR ' + self.path, callback, CHUNK_SIZE)
else:
- callback = f.write
- conn.retrbinary('RETR ' + self.path, callback, CHUNK_SIZE)
+ conn.retrbinary('RETR ' + self.path, f.write, CHUNK_SIZE)
def upload(self, location: str):
size = os.path.getsize(location)
@@ -150,12 +149,10 @@ class FtpC:
if self.secure:
conn.prot_p()
if self.progressbar and size and size > CHUNK_SIZE:
- progress = make_incremental_progressbar(CHUNK_SIZE / size)
- next(progress)
- callback = lambda block: next(progress)
+ with Progressbar(CHUNK_SIZE / size) as p:
+ conn.storbinary('STOR ' + self.path, f, CHUNK_SIZE, lambda block: p.increment())
else:
- callback = None
- conn.storbinary('STOR ' + self.path, f, CHUNK_SIZE, callback)
+ conn.storbinary('STOR ' + self.path, f, CHUNK_SIZE)
class SshC:
known_hosts = os.path.expanduser('~/.ssh/known_hosts')
@@ -190,14 +187,16 @@ class SshC:
return ssh
def download(self, location: str):
- callback = make_progressbar() if self.progressbar else None
with self._establish() as ssh, ssh.open_sftp() as sftp:
if self.check_space:
check_storage(location, sftp.stat(self.path).st_size)
- sftp.get(self.path, location, callback=callback)
+ if self.progressbar:
+ with Progressbar() as p:
+ sftp.get(self.path, location, callback=p.progress)
+ else:
+ sftp.get(self.path, location)
def upload(self, location: str):
- callback = make_progressbar() if self.progressbar else None
with self._establish() as ssh, ssh.open_sftp() as sftp:
try:
# If the remote path is a directory, use the original filename.
@@ -210,7 +209,11 @@ class SshC:
except IOError:
path = self.path
finally:
- sftp.put(location, path, callback=callback)
+ if self.progressbar:
+ with Progressbar() as p:
+ sftp.put(location, path, callback=p.progress)
+ else:
+ sftp.put(location, path)
class HttpC:
@@ -264,10 +267,9 @@ class HttpC:
with s.get(final_urlstring, stream=True,
timeout=self.timeout) as r, open(location, 'wb') as f:
if self.progressbar and size:
- progress = make_incremental_progressbar(CHUNK_SIZE / size)
- next(progress)
- for chunk in iter(lambda: begin(next(progress), r.raw.read(CHUNK_SIZE)), b''):
- f.write(chunk)
+ with Progressbar(CHUNK_SIZE / size) as p:
+ for chunk in iter(lambda: begin(p.increment(), r.raw.read(CHUNK_SIZE)), b''):
+ f.write(chunk)
else:
# We'll try to stream the download directly with `copyfileobj()` so that large
# files (like entire VyOS images) don't occupy much memory.
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 3be486cc4..c778d0de8 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -582,10 +582,11 @@ def nft_rule(rule_conf, fw_hook, fw_name, rule_id, ip_name='ip'):
def nft_default_rule(fw_conf, fw_name, ipv6=False):
output = ['counter']
default_action = fw_conf['default_action']
+ family = 'ipv6' if ipv6 else 'ipv4'
if 'enable_default_log' in fw_conf:
action_suffix = default_action[:1].upper()
- output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}]"')
+ output.append(f'log prefix "[{family}-{fw_name[:19]}-default-{action_suffix}]"')
#output.append(nft_action(default_action))
output.append(f'{default_action}')
diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py
index 9484eacdd..d36b6fcfb 100644
--- a/python/vyos/utils/dict.py
+++ b/python/vyos/utils/dict.py
@@ -199,6 +199,31 @@ def dict_search_recursive(dict_object, key, path=[]):
for x in dict_search_recursive(j, key, new_path):
yield x
+
+def dict_set(key_path, value, dict_object):
+ """ Set value to Python dictionary (dict_object) using path to key delimited by dot (.).
+ The key will be added if it does not exist.
+ """
+ path_list = key_path.split(".")
+ dynamic_dict = dict_object
+ if len(path_list) > 0:
+ for i in range(0, len(path_list)-1):
+ dynamic_dict = dynamic_dict[path_list[i]]
+ dynamic_dict[path_list[len(path_list)-1]] = value
+
+def dict_delete(key_path, dict_object):
+ """ Delete key in Python dictionary (dict_object) using path to key delimited by dot (.).
+ """
+ path_dict = dict_object
+ path_list = key_path.split('.')
+ inside = path_list[:-1]
+ if not inside:
+ del dict_object[path_list]
+ else:
+ for key in path_list[:-1]:
+ path_dict = path_dict[key]
+ del path_dict[path_list[len(path_list)-1]]
+
def dict_to_list(d, save_key_to=None):
""" Convert a dict to a list of dicts.
@@ -228,6 +253,39 @@ def dict_to_list(d, save_key_to=None):
return collect
+def dict_to_paths_values(conf: dict) -> dict:
+ """
+ Convert nested dictionary to simple dictionary, where key is a path is delimited by dot (.).
+ """
+ list_of_paths = []
+ dict_of_options ={}
+ for path in dict_to_key_paths(conf):
+ str_path = '.'.join(path)
+ list_of_paths.append(str_path)
+
+ for path in list_of_paths:
+ dict_of_options[path] = dict_search(path,conf)
+
+ return dict_of_options
+def dict_to_key_paths(d: dict) -> list:
+ """ Generator to return list of key paths from dict of list[str]|str
+ """
+ def func(d, path):
+ if isinstance(d, dict):
+ if not d:
+ yield path
+ for k, v in d.items():
+ for r in func(v, path + [k]):
+ yield r
+ elif isinstance(d, list):
+ yield path
+ elif isinstance(d, str):
+ yield path
+ else:
+ raise ValueError('object is not a dict of strings/list of strings')
+ for r in func(d, []):
+ yield r
+
def dict_to_paths(d: dict) -> list:
""" Generator to return list of paths from dict of list[str]|str
"""
@@ -305,3 +363,4 @@ class FixedDict(dict):
if k not in self._allowed:
raise ConfigError(f'Option "{k}" has no defined default')
super().__setitem__(k, v)
+
diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py
index 843494855..5fffa62f8 100644
--- a/python/vyos/utils/io.py
+++ b/python/vyos/utils/io.py
@@ -24,45 +24,6 @@ def print_error(str='', end='\n'):
sys.stderr.write(end)
sys.stderr.flush()
-def make_progressbar():
- """
- Make a procedure that takes two arguments `done` and `total` and prints a
- progressbar based on the ratio thereof, whose length is determined by the
- width of the terminal.
- """
- import shutil, math
- col, _ = shutil.get_terminal_size()
- col = max(col - 15, 20)
- def print_progressbar(done, total):
- if done <= total:
- increment = total / col
- length = math.ceil(done / increment)
- percentage = str(math.ceil(100 * done / total)).rjust(3)
- print_error(f'[{length * "#"}{(col - length) * "_"}] {percentage}%', '\r')
- # Print a newline so that the subsequent prints don't overwrite the full bar.
- if done == total:
- print_error()
- return print_progressbar
-
-def make_incremental_progressbar(increment: float):
- """
- Make a generator that displays a progressbar that grows monotonically with
- every iteration.
- First call displays it at 0% and every subsequent iteration displays it
- at `increment` increments where 0.0 < `increment` < 1.0.
- Intended for FTP and HTTP transfers with stateless callbacks.
- """
- print_progressbar = make_progressbar()
- total = 0.0
- while total < 1.0:
- print_progressbar(total, 1.0)
- yield
- total += increment
- print_progressbar(1, 1)
- # Ignore further calls.
- while True:
- yield
-
def ask_input(question, default='', numeric_only=False, valid_responses=[]):
question_out = question
if default: