diff options
-rw-r--r-- | data/templates/frr/bgpd.frr.j2 | 32 | ||||
-rw-r--r-- | data/templates/load-balancing/haproxy.cfg.j2 | 13 | ||||
-rw-r--r-- | interface-definitions/include/bgp/bmp-monitor-afi-policy.xml.i | 14 | ||||
-rw-r--r-- | interface-definitions/include/bgp/protocol-common-config.xml.i | 86 | ||||
-rw-r--r-- | interface-definitions/include/firewall/action.xml.i | 8 | ||||
-rw-r--r-- | interface-definitions/include/firewall/ipv4-custom-name.xml.i | 1 | ||||
-rw-r--r-- | interface-definitions/include/firewall/ipv6-custom-name.xml.i | 1 | ||||
-rw-r--r-- | interface-definitions/include/pki/certificate-multi.xml.i | 15 | ||||
-rw-r--r-- | interface-definitions/load-balancing-haproxy.xml.in | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 2 | ||||
-rw-r--r-- | python/vyos/system/grub.py | 2 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 36 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_bgp.py | 56 | ||||
-rwxr-xr-x | src/conf_mode/load-balancing-haproxy.py | 20 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bgp.py | 23 | ||||
-rwxr-xr-x | src/op_mode/image_installer.py | 30 |
16 files changed, 314 insertions, 27 deletions
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2 index 641dac44a..679ba8b04 100644 --- a/data/templates/frr/bgpd.frr.j2 +++ b/data/templates/frr/bgpd.frr.j2 @@ -470,6 +470,38 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }} {% endfor %} {% endif %} ! +{% if bmp is vyos_defined %} +{% if bmp.mirror_buffer_limit is vyos_defined %} + bmp mirror buffer-limit {{ bmp.mirror_buffer_limit }} + ! +{% endif %} +{% if bmp.target is vyos_defined %} +{% for bmp, bmp_config in bmp.target.items() %} + bmp targets {{ bmp }} +{% if bmp_config.mirror is vyos_defined %} + bmp mirror +{% endif %} +{% if bmp_config.monitor is vyos_defined %} +{% if bmp_config.monitor.ipv4_unicast.pre_policy is vyos_defined %} + bmp monitor ipv4 unicast pre-policy +{% endif %} +{% if bmp_config.monitor.ipv4_unicast.post_policy is vyos_defined %} + bmp monitor ipv4 unicast post-policy +{% endif %} +{% if bmp_config.monitor.ipv6_unicast.pre_policy is vyos_defined %} + bmp monitor ipv6 unicast pre-policy +{% endif %} +{% if bmp_config.monitor.ipv6_unicast.post_policy is vyos_defined %} + bmp monitor ipv6 unicast post-policy +{% endif %} +{% endif %} +{% if bmp_config.address is vyos_defined %} + bmp connect {{ bmp_config.address }} port {{ bmp_config.port }} min-retry {{ bmp_config.min_retry }} max-retry {{ bmp_config.max_retry }} +{% endif %} +{% endfor %} + exit +{% endif %} +{% endif %} {% if peer_group is vyos_defined %} {% for peer, config in peer_group.items() %} {{ bgp_neighbor(peer, config, true) }} diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2 index a75ee9904..defb76fba 100644 --- a/data/templates/load-balancing/haproxy.cfg.j2 +++ b/data/templates/load-balancing/haproxy.cfg.j2 @@ -50,13 +50,19 @@ defaults {% if service is vyos_defined %} {% for front, front_config in service.items() %} frontend {{ front }} -{% set ssl_front = 'ssl crt /run/haproxy/' ~ front_config.ssl.certificate ~ '.pem' if front_config.ssl.certificate is vyos_defined else '' %} +{% set ssl_front = [] %} +{% if front_config.ssl.certificate is vyos_defined and front_config.ssl.certificate is iterable %} +{% for cert in front_config.ssl.certificate %} +{% set _ = ssl_front.append('crt /run/haproxy/' ~ cert ~ '.pem') %} +{% endfor %} +{% endif %} +{% set ssl_directive = 'ssl' if ssl_front else '' %} {% if front_config.listen_address is vyos_defined %} {% for address in front_config.listen_address %} - bind {{ address | bracketize_ipv6 }}:{{ front_config.port }} {{ ssl_front }} + bind {{ address | bracketize_ipv6 }}:{{ front_config.port }} {{ ssl_directive }} {{ ssl_front | join(' ') }} {% endfor %} {% else %} - bind :::{{ front_config.port }} v4v6 {{ ssl_front }} + bind :::{{ front_config.port }} v4v6 {{ ssl_directive }} {{ ssl_front | join(' ') }} {% endif %} {% if front_config.redirect_http_to_https is vyos_defined %} http-request redirect scheme https unless { ssl_fc } @@ -161,4 +167,3 @@ backend {{ back }} {% endfor %} {% endif %} - diff --git a/interface-definitions/include/bgp/bmp-monitor-afi-policy.xml.i b/interface-definitions/include/bgp/bmp-monitor-afi-policy.xml.i new file mode 100644 index 000000000..261d60232 --- /dev/null +++ b/interface-definitions/include/bgp/bmp-monitor-afi-policy.xml.i @@ -0,0 +1,14 @@ +<!-- include start from bgp/bmp-monitor-afi-policy.xml.i --> +<leafNode name="pre-policy"> + <properties> + <help>Send state before policy and filter processing</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="post-policy"> + <properties> + <help>Send state with policy and filters applied</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index c379ba95c..dce61ee77 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -909,6 +909,92 @@ </node> </children> </node> +<node name="bmp"> + <properties> + <help>BGP Monitoring Protocol (BMP)</help> + </properties> + <children> + <leafNode name="mirror-buffer-limit"> + <properties> + <help>Maximum memory used for buffered mirroring messages (in bytes)</help> + <valueHelp> + <format>u32:0-4294967294</format> + <description>Limit in bytes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967294"/> + </constraint> + </properties> + </leafNode> + <tagNode name="target"> + <properties> + <help>BMP target</help> + </properties> + <children> + #include <include/address-ipv4-ipv6-single.xml.i> + #include <include/port-number.xml.i> + <leafNode name="port"> + <defaultValue>5000</defaultValue> + </leafNode> + <leafNode name="min-retry"> + <properties> + <help>Minimum connection retry interval (in milliseconds)</help> + <valueHelp> + <format>u32:100-86400000</format> + <description>Minimum connection retry interval</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 100-86400000"/> + </constraint> + </properties> + <defaultValue>1000</defaultValue> + </leafNode> + <leafNode name="max-retry"> + <properties> + <help>Maximum connection retry interval</help> + <valueHelp> + <format>u32:100-4294967295</format> + <description>Maximum connection retry interval</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 100-86400000"/> + </constraint> + </properties> + <defaultValue>2000</defaultValue> + </leafNode> + <leafNode name="mirror"> + <properties> + <help>Send BMP route mirroring messages</help> + <valueless/> + </properties> + </leafNode> + <node name="monitor"> + <properties> + <help>Send BMP route monitoring messages</help> + </properties> + <children> + <node name="ipv4-unicast"> + <properties> + <help>Address family IPv4 unicast</help> + </properties> + <children> + #include <include/bgp/bmp-monitor-afi-policy.xml.i> + </children> + </node> + <node name="ipv6-unicast"> + <properties> + <help>Address family IPv6 unicast</help> + </properties> + <children> + #include <include/bgp/bmp-monitor-afi-policy.xml.i> + </children> + </node> + </children> + </node> + </children> + </tagNode> + </children> +</node> <tagNode name="interface"> <properties> <help>Configure interface related parameters, e.g. MPLS</help> diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i index 954e4f23e..e1f0c6cb6 100644 --- a/interface-definitions/include/firewall/action.xml.i +++ b/interface-definitions/include/firewall/action.xml.i @@ -3,7 +3,7 @@ <properties> <help>Rule action</help> <completionHelp> - <list>accept continue jump reject return drop queue synproxy</list> + <list>accept continue jump reject return drop queue offload synproxy</list> </completionHelp> <valueHelp> <format>accept</format> @@ -34,11 +34,15 @@ <description>Enqueue packet to userspace</description> </valueHelp> <valueHelp> + <format>offload</format> + <description>Offload packet via flowtable</description> + </valueHelp> + <valueHelp> <format>synproxy</format> <description>Synproxy connections</description> </valueHelp> <constraint> - <regex>(accept|continue|jump|reject|return|drop|queue|synproxy)</regex> + <regex>(accept|continue|jump|reject|return|drop|queue|offload|synproxy)</regex> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/firewall/ipv4-custom-name.xml.i b/interface-definitions/include/firewall/ipv4-custom-name.xml.i index 9d6ecfaf2..c6420fe1f 100644 --- a/interface-definitions/include/firewall/ipv4-custom-name.xml.i +++ b/interface-definitions/include/firewall/ipv4-custom-name.xml.i @@ -33,6 +33,7 @@ <children> #include <include/firewall/common-rule-ipv4.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> </tagNode> diff --git a/interface-definitions/include/firewall/ipv6-custom-name.xml.i b/interface-definitions/include/firewall/ipv6-custom-name.xml.i index 81610babf..2cc45a60c 100644 --- a/interface-definitions/include/firewall/ipv6-custom-name.xml.i +++ b/interface-definitions/include/firewall/ipv6-custom-name.xml.i @@ -33,6 +33,7 @@ <children> #include <include/firewall/common-rule-ipv6.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> </tagNode> diff --git a/interface-definitions/include/pki/certificate-multi.xml.i b/interface-definitions/include/pki/certificate-multi.xml.i new file mode 100644 index 000000000..c49c5d9b2 --- /dev/null +++ b/interface-definitions/include/pki/certificate-multi.xml.i @@ -0,0 +1,15 @@ +<!-- include start from pki/certificate-multi.xml.i --> +<leafNode name="certificate"> + <properties> + <help>Certificate in PKI configuration</help> + <completionHelp> + <path>pki certificate</path> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Name of certificate in PKI configuration</description> + </valueHelp> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/load-balancing-haproxy.xml.in b/interface-definitions/load-balancing-haproxy.xml.in index 564c335ec..8f6bd3a99 100644 --- a/interface-definitions/load-balancing-haproxy.xml.in +++ b/interface-definitions/load-balancing-haproxy.xml.in @@ -49,7 +49,7 @@ <help>SSL Certificate, SSL Key and CA</help> </properties> <children> - #include <include/pki/certificate.xml.i> + #include <include/pki/certificate-multi.xml.i> </children> </node> </children> diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 1586710db..56dcde214 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -115,7 +115,7 @@ class Interface(Control): }, 'vrf': { 'shellcmd': 'ip -json -detail link list dev {ifname}', - 'format': lambda j: jmespath.search('[*].master | [0]', json.loads(j)), + 'format': lambda j: jmespath.search('[?linkinfo.info_slave_kind == `vrf`].master | [0]', json.loads(j)), }, } diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py index 0ac16af9a..2692aaea1 100644 --- a/python/vyos/system/grub.py +++ b/python/vyos/system/grub.py @@ -138,6 +138,8 @@ def version_list(root_dir: str = '') -> list[str]: versions_list: list[str] = [] for file in versions_files: versions_list.append(file.stem) + versions_list.sort(reverse=True) + return versions_list diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 066ed707b..5cfddb269 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -753,5 +753,41 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK') self.verify_nftables_chain([['accept']], 'ip6 vyos_conntrack', 'FW_CONNTRACK') + def test_zone_flow_offload(self): + self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0']) + self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'hardware']) + + # QEMU virtual NIC does not support hw-tc-offload + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'software']) + + self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'action', 'offload']) + self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'offload-target', 'smoketest']) + + self.cli_set(['firewall', 'ipv6', 'name', 'smoketest', 'rule', '1', 'action', 'offload']) + self.cli_set(['firewall', 'ipv6', 'name', 'smoketest', 'rule', '1', 'offload-target', 'smoketest']) + + self.cli_commit() + + nftables_search = [ + ['chain NAME_smoketest'], + ['flow add @VYOS_FLOWTABLE_smoketest'] + ] + + self.verify_nftables(nftables_search, 'ip vyos_filter') + + nftables_search = [ + ['chain NAME6_smoketest'], + ['flow add @VYOS_FLOWTABLE_smoketest'] + ] + + self.verify_nftables(nftables_search, 'ip6 vyos_filter') + + # Check conntrack + self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK') + self.verify_nftables_chain([['accept']], 'ip6 vyos_conntrack', 'FW_CONNTRACK') + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 97dab255e..8102a3153 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -15,6 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import unittest +from subprocess import run from base_vyostest_shim import VyOSUnitTestSHIM @@ -1148,5 +1149,60 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' locator {locator_name}', frrconfig) self.assertIn(f' sid vpn per-vrf export {sid}', frrconfig) + def test_bgp_25_bmp(self): + target_name = 'instance-bmp' + target_address = '127.0.0.1' + target_port = '5000' + min_retry = '1024' + max_retry = '2048' + monitor_ipv4 = 'pre-policy' + monitor_ipv6 = 'pre-policy' + mirror_buffer = '32000000' + bmp_path = base_path + ['bmp'] + target_path = bmp_path + ['target', target_name] + bgpd_bmp_pid = process_named_running('bgpd', 'bmp') + command = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'restart', 'bgp'] + + self.cli_set(bmp_path) + # by default the 'bmp' module not loaded for the bgpd + # expect Error + if not bgpd_bmp_pid: + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + # add required 'bmp' module to bgpd and restart bgpd + self.cli_delete(bmp_path) + self.cli_set(['system', 'frr', 'bmp']) + self.cli_commit() + # restart bgpd to apply "-M bmp" and update PID + run(command, input='Y', text=True) + self.daemon_pid = process_named_running(PROCESS_NAME) + + # set bmp config but not set address + self.cli_set(target_path + ['port', target_port]) + # address is not set, expect Error + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + # config other bmp options + self.cli_set(target_path + ['address', target_address]) + self.cli_set(bmp_path + ['mirror-buffer-limit', mirror_buffer]) + self.cli_set(target_path + ['port', target_port]) + self.cli_set(target_path + ['min-retry', min_retry]) + self.cli_set(target_path + ['max-retry', max_retry]) + self.cli_set(target_path + ['mirror']) + self.cli_set(target_path + ['monitor', 'ipv4-unicast', monitor_ipv4]) + self.cli_set(target_path + ['monitor', 'ipv6-unicast', monitor_ipv6]) + self.cli_commit() + + # Verify bgpd bmp configuration + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + self.assertIn(f'bmp mirror buffer-limit {mirror_buffer}', frrconfig) + self.assertIn(f'bmp targets {target_name}', frrconfig) + self.assertIn(f'bmp mirror', frrconfig) + self.assertIn(f'bmp monitor ipv4 unicast {monitor_ipv4}', frrconfig) + self.assertIn(f'bmp monitor ipv6 unicast {monitor_ipv6}', frrconfig) + self.assertIn(f'bmp connect {target_address} port {target_port} min-retry {min_retry} max-retry {max_retry}', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/load-balancing-haproxy.py b/src/conf_mode/load-balancing-haproxy.py index ec4311bb5..333ebc66c 100755 --- a/src/conf_mode/load-balancing-haproxy.py +++ b/src/conf_mode/load-balancing-haproxy.py @@ -108,17 +108,19 @@ def generate(lb): if 'ssl' in front_config: if 'certificate' in front_config['ssl']: - cert_name = front_config['ssl']['certificate'] - pki_cert = lb['pki']['certificate'][cert_name] - cert_file_path = os.path.join(load_balancing_dir, f'{cert_name}.pem') - cert_key_path = os.path.join(load_balancing_dir, f'{cert_name}.pem.key') + cert_names = front_config['ssl']['certificate'] - with open(cert_file_path, 'w') as f: - f.write(wrap_certificate(pki_cert['certificate'])) + for cert_name in cert_names: + pki_cert = lb['pki']['certificate'][cert_name] + cert_file_path = os.path.join(load_balancing_dir, f'{cert_name}.pem') + cert_key_path = os.path.join(load_balancing_dir, f'{cert_name}.pem.key') - if 'private' in pki_cert and 'key' in pki_cert['private']: - with open(cert_key_path, 'w') as f: - f.write(wrap_private_key(pki_cert['private']['key'])) + with open(cert_file_path, 'w') as f: + f.write(wrap_certificate(pki_cert['certificate'])) + + if 'private' in pki_cert and 'key' in pki_cert['private']: + with open(cert_key_path, 'w') as f: + f.write(wrap_private_key(pki_cert['private']['key'])) if 'ca_certificate' in front_config['ssl']: ca_name = front_config['ssl']['ca_certificate'] diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 557f0a9e9..bf807fa5f 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -30,6 +30,7 @@ from vyos.template import render_to_string from vyos.utils.dict import dict_search from vyos.utils.network import get_interface_vrf from vyos.utils.network import is_addr_assigned +from vyos.utils.process import process_named_running from vyos import ConfigError from vyos import frr from vyos import airbag @@ -49,8 +50,13 @@ def get_config(config=None): # eqivalent of the C foo ? 'a' : 'b' statement base = vrf and ['vrf', 'name', vrf, 'protocols', 'bgp'] or base_path - bgp = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + bgp = conf.get_config_dict( + base, + key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True, + with_recursive_defaults=True, + ) bgp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'], key_mangling=('-', '_'), @@ -247,6 +253,19 @@ def verify(bgp): if 'system_as' not in bgp: raise ConfigError('BGP system-as number must be defined!') + # Verify BMP + if 'bmp' in bgp: + # check bmp flag "bgpd -d -F traditional --daemon -A 127.0.0.1 -M rpki -M bmp" + if not process_named_running('bgpd', 'bmp'): + raise ConfigError( + f'"bmp" flag is not found in bgpd. Configure "set system frr bmp" and restart bgp process' + ) + # check bmp target + if 'target' in bgp['bmp']: + for target, target_config in bgp['bmp']['target'].items(): + if 'address' not in target_config: + raise ConfigError(f'BMP target "{target}" address must be defined!') + # Verify vrf on interface and bgp section if 'interface' in bgp: for interface in bgp['interface']: diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index 09501ef46..6a8797aec 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -22,6 +22,7 @@ from pathlib import Path from shutil import copy, chown, rmtree, copytree from glob import glob from sys import exit +from os import environ from time import sleep from typing import Union from urllib.parse import urlparse @@ -463,16 +464,22 @@ def validate_signature(file_path: str, sign_type: str) -> None: print('Signature is valid') def download_file(local_file: str, remote_path: str, vrf: str, + username: str, password: str, progressbar: bool = False, check_space: bool = False): + environ['REMOTE_USERNAME'] = username + environ['REMOTE_PASSWORD'] = password if vrf is None: download(local_file, remote_path, progressbar=progressbar, check_space=check_space, raise_error=True) else: - vrf_cmd = f'ip vrf exec {vrf} {external_download_script} \ + vrf_cmd = f'REMOTE_USERNAME={username} REMOTE_PASSWORD={password} \ + ip vrf exec {vrf} {external_download_script} \ --local-file {local_file} --remote-path {remote_path}' cmd(vrf_cmd) -def image_fetch(image_path: str, vrf: str = None, no_prompt: bool = False) -> Path: +def image_fetch(image_path: str, vrf: str = None, + username: str = '', password: str = '', + no_prompt: bool = False) -> Path: """Fetch an ISO image Args: @@ -486,6 +493,7 @@ def image_fetch(image_path: str, vrf: str = None, no_prompt: bool = False) -> Pa if urlparse(image_path).scheme: # download an image download_file(ISO_DOWNLOAD_PATH, image_path, vrf, + username, password, progressbar=True, check_space=True) # download a signature @@ -493,7 +501,8 @@ def image_fetch(image_path: str, vrf: str = None, no_prompt: bool = False) -> Pa for sign_type in ['minisig', 'asc']: try: download_file(f'{ISO_DOWNLOAD_PATH}.{sign_type}', - f'{image_path}.{sign_type}', vrf) + f'{image_path}.{sign_type}', vrf, + username, password) sign_file = (True, sign_type) break except Exception: @@ -744,7 +753,8 @@ def install_image() -> None: @compat.grub_cfg_update -def add_image(image_path: str, vrf: str = None, no_prompt: bool = False) -> None: +def add_image(image_path: str, vrf: str = None, username: str = '', + password: str = '', no_prompt: bool = False) -> None: """Add a new image Args: @@ -754,7 +764,7 @@ def add_image(image_path: str, vrf: str = None, no_prompt: bool = False) -> None exit(MSG_ERR_LIVE) # fetch an image - iso_path: Path = image_fetch(image_path, vrf, no_prompt) + iso_path: Path = image_fetch(image_path, vrf, username, password, no_prompt) try: # mount an ISO Path(DIR_ISO_MOUNT).mkdir(mode=0o755, parents=True) @@ -858,8 +868,11 @@ def parse_arguments() -> Namespace: help='vrf name for image download') parser.add_argument('--no-prompt', action='store_true', help='perform action non-interactively') - parser.add_argument( - '--image-path', + parser.add_argument('--username', default='', + help='username for image download') + parser.add_argument('--password', default='', + help='password for image download') + parser.add_argument('--image-path', help='a path (HTTP or local file) to an image that needs to be installed' ) # parser.add_argument('--image_new_name', help='a new name for image') @@ -877,7 +890,8 @@ if __name__ == '__main__': if args.action == 'install': install_image() if args.action == 'add': - add_image(args.image_path, args.vrf, args.no_prompt) + add_image(args.image_path, args.vrf, + args.username, args.password, args.no_prompt) exit() |