summaryrefslogtreecommitdiff
path: root/src/op_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/op_mode')
-rwxr-xr-xsrc/op_mode/connect_disconnect.py68
-rwxr-xr-xsrc/op_mode/force_root-partition-auto-resize.sh60
-rwxr-xr-xsrc/op_mode/format_disk.py76
-rwxr-xr-xsrc/op_mode/generate_ipsec_debug_archive.sh36
-rwxr-xr-xsrc/op_mode/lldp_op.py3
-rwxr-xr-xsrc/op_mode/pki.py73
-rwxr-xr-xsrc/op_mode/ppp-server-ctrl.py2
-rwxr-xr-xsrc/op_mode/show_configuration_json.py36
-rwxr-xr-xsrc/op_mode/show_interfaces.py4
-rwxr-xr-xsrc/op_mode/show_ipsec_sa.py3
-rwxr-xr-xsrc/op_mode/show_openvpn_mfa.py64
-rwxr-xr-xsrc/op_mode/show_ram.py64
-rwxr-xr-xsrc/op_mode/show_ram.sh33
-rwxr-xr-xsrc/op_mode/show_uptime.py50
-rwxr-xr-xsrc/op_mode/vpn_ipsec.py2
15 files changed, 420 insertions, 154 deletions
diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py
index a773aa28e..ffc574362 100755
--- a/src/op_mode/connect_disconnect.py
+++ b/src/op_mode/connect_disconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 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
@@ -17,21 +17,19 @@
import os
import argparse
-from sys import exit
from psutil import process_iter
-from time import strftime, localtime, time
from vyos.util import call
+from vyos.util import DEVNULL
+from vyos.util import is_wwan_connected
-def check_interface(interface):
+def check_ppp_interface(interface):
if not os.path.isfile(f'/etc/ppp/peers/{interface}'):
- print(f'Interface {interface}: invalid!')
+ print(f'Interface {interface} does not exist!')
exit(1)
def check_ppp_running(interface):
- """
- Check if ppp process is running in the interface in question
- """
+ """ Check if PPP process is running in the interface in question """
for p in process_iter():
if "pppd" in p.name():
if interface in p.cmdline():
@@ -40,32 +38,46 @@ def check_ppp_running(interface):
return False
def connect(interface):
- """
- Connect PPP interface
- """
- check_interface(interface)
+ """ Connect dialer interface """
- # Check if interface is already dialed
- if os.path.isdir(f'/sys/class/net/{interface}'):
- print(f'Interface {interface}: already connected!')
- elif check_ppp_running(interface):
- print(f'Interface {interface}: connection is beeing established!')
+ if interface.startswith('ppp'):
+ check_ppp_interface(interface)
+ # Check if interface is already dialed
+ if os.path.isdir(f'/sys/class/net/{interface}'):
+ print(f'Interface {interface}: already connected!')
+ elif check_ppp_running(interface):
+ print(f'Interface {interface}: connection is beeing established!')
+ else:
+ print(f'Interface {interface}: connecting...')
+ call(f'systemctl restart ppp@{interface}.service')
+ elif interface.startswith('wwan'):
+ if is_wwan_connected(interface):
+ print(f'Interface {interface}: already connected!')
+ else:
+ call(f'VYOS_TAGNODE_VALUE={interface} /usr/libexec/vyos/conf_mode/interfaces-wwan.py')
else:
- print(f'Interface {interface}: connecting...')
- call(f'systemctl restart ppp@{interface}.service')
+ print(f'Unknown interface {interface}, can not connect. Aborting!')
def disconnect(interface):
- """
- Disconnect PPP interface
- """
- check_interface(interface)
+ """ Disconnect dialer interface """
- # Check if interface is already down
- if not check_ppp_running(interface):
- print(f'Interface {interface}: connection is already down')
+ if interface.startswith('ppp'):
+ check_ppp_interface(interface)
+
+ # Check if interface is already down
+ if not check_ppp_running(interface):
+ print(f'Interface {interface}: connection is already down')
+ else:
+ print(f'Interface {interface}: disconnecting...')
+ call(f'systemctl stop ppp@{interface}.service')
+ elif interface.startswith('wwan'):
+ if not is_wwan_connected(interface):
+ print(f'Interface {interface}: connection is already down')
+ else:
+ modem = interface.lstrip('wwan')
+ call(f'mmcli --modem {modem} --simple-disconnect', stdout=DEVNULL)
else:
- print(f'Interface {interface}: disconnecting...')
- call(f'systemctl stop ppp@{interface}.service')
+ print(f'Unknown interface {interface}, can not disconnect. Aborting!')
def main():
parser = argparse.ArgumentParser()
diff --git a/src/op_mode/force_root-partition-auto-resize.sh b/src/op_mode/force_root-partition-auto-resize.sh
new file mode 100755
index 000000000..b39e87560
--- /dev/null
+++ b/src/op_mode/force_root-partition-auto-resize.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2021 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 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/>.
+
+# ROOT_PART_DEV – root partition device path
+# ROOT_PART_NAME – root partition device name
+# ROOT_DEV_NAME – disk device name
+# ROOT_DEV – disk device path
+# ROOT_PART_NUM – number of root partition on disk
+# ROOT_DEV_SIZE – disk total size in 512 bytes sectors
+# ROOT_PART_SIZE – root partition total size in 512 bytes sectors
+# ROOT_PART_START – number of 512 bytes sector where root partition starts
+# AVAILABLE_EXTENSION_SIZE – calculation available disk space after root partition in 512 bytes sectors
+ROOT_PART_DEV=$(findmnt /usr/lib/live/mount/persistence -o source -n)
+ROOT_PART_NAME=$(echo "$ROOT_PART_DEV" | cut -d "/" -f 3)
+ROOT_DEV_NAME=$(echo /sys/block/*/"${ROOT_PART_NAME}" | cut -d "/" -f 4)
+ROOT_DEV="/dev/${ROOT_DEV_NAME}"
+ROOT_PART_NUM=$(cat "/sys/block/${ROOT_DEV_NAME}/${ROOT_PART_NAME}/partition")
+ROOT_DEV_SIZE=$(cat "/sys/block/${ROOT_DEV_NAME}/size")
+ROOT_PART_SIZE=$(cat "/sys/block/${ROOT_DEV_NAME}/${ROOT_PART_NAME}/size")
+ROOT_PART_START=$(cat "/sys/block/${ROOT_DEV_NAME}/${ROOT_PART_NAME}/start")
+AVAILABLE_EXTENSION_SIZE=$((ROOT_DEV_SIZE - ROOT_PART_START - ROOT_PART_SIZE - 8))
+
+#
+# Check if device have space for root partition growing up.
+#
+if [ $AVAILABLE_EXTENSION_SIZE -lt 1 ]; then
+ echo "There is no available space for root partition extension"
+ exit 0;
+fi
+
+#
+# Resize the partition and grow the filesystem.
+#
+# "print" and "Fix" directives were added to fix GPT table if it corrupted after virtual drive extension.
+# If GPT table is corrupted we'll get Fix/Ignore dialogue after "print" command.
+# "Fix" will be the answer for this dialogue.
+# If GPT table is fine and no auto-fix dialogue appeared the directive "Fix" simply will print parted utility help info.
+parted -m ${ROOT_DEV} ---pretend-input-tty > /dev/null 2>&1 <<EOF
+print
+Fix
+resizepart
+${ROOT_PART_NUM}
+Yes
+100%
+EOF
+partprobe > /dev/null 2>&1
+resize2fs ${ROOT_PART_DEV} > /dev/null 2>&1
diff --git a/src/op_mode/format_disk.py b/src/op_mode/format_disk.py
index df4486bce..b3ba44e87 100755
--- a/src/op_mode/format_disk.py
+++ b/src/op_mode/format_disk.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 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
@@ -17,11 +17,10 @@
import argparse
import os
import re
-import sys
+
from datetime import datetime
-from time import sleep
-from vyos.util import is_admin, ask_yes_no
+from vyos.util import ask_yes_no
from vyos.util import call
from vyos.util import cmd
from vyos.util import DEVNULL
@@ -38,16 +37,17 @@ def list_disks():
def is_busy(disk: str):
"""Check if given disk device is busy by re-reading it's partition table"""
- return call(f'sudo blockdev --rereadpt /dev/{disk}', stderr=DEVNULL) != 0
+ return call(f'blockdev --rereadpt /dev/{disk}', stderr=DEVNULL) != 0
def backup_partitions(disk: str):
"""Save sfdisk partitions output to a backup file"""
- device_path = '/dev/' + disk
- backup_ts = datetime.now().strftime('%Y-%m-%d-%H:%M')
- backup_file = '/var/tmp/backup_{}.{}'.format(disk, backup_ts)
- cmd(f'sudo /sbin/sfdisk -d {device_path} > {backup_file}')
+ device_path = f'/dev/{disk}'
+ backup_ts = datetime.now().strftime('%Y%m%d-%H%M')
+ backup_file = f'/var/tmp/backup_{disk}.{backup_ts}'
+ call(f'sfdisk -d {device_path} > {backup_file}')
+ print(f'Partition table backup saved to {backup_file}')
def list_partitions(disk: str):
@@ -65,11 +65,11 @@ def list_partitions(disk: str):
def delete_partition(disk: str, partition_idx: int):
- cmd(f'sudo /sbin/parted /dev/{disk} rm {partition_idx}')
+ cmd(f'parted /dev/{disk} rm {partition_idx}')
def format_disk_like(target: str, proto: str):
- cmd(f'sudo /sbin/sfdisk -d /dev/{proto} | sudo /sbin/sfdisk --force /dev/{target}')
+ cmd(f'sfdisk -d /dev/{proto} | sfdisk --force /dev/{target}')
if __name__ == '__main__':
@@ -79,10 +79,6 @@ if __name__ == '__main__':
group.add_argument('-p', '--proto', type=str, required=True, help='Prototype device to use as reference')
args = parser.parse_args()
- if not is_admin():
- print('Must be admin or root to format disk')
- sys.exit(1)
-
target_disk = args.target
eligible_target_disks = list_disks()
@@ -90,54 +86,48 @@ if __name__ == '__main__':
eligible_proto_disks = eligible_target_disks.copy()
eligible_proto_disks.remove(target_disk)
- fmt = {
- 'target_disk': target_disk,
- 'proto_disk': proto_disk,
- }
-
if proto_disk == target_disk:
print('The two disk drives must be different.')
- sys.exit(1)
+ exit(1)
- if not os.path.exists('/dev/' + proto_disk):
- print('Device /dev/{proto_disk} does not exist'.format_map(fmt))
- sys.exit(1)
+ if not os.path.exists(f'/dev/{proto_disk}'):
+ print(f'Device /dev/{proto_disk} does not exist')
+ exit(1)
if not os.path.exists('/dev/' + target_disk):
- print('Device /dev/{target_disk} does not exist'.format_map(fmt))
- sys.exit(1)
+ print(f'Device /dev/{target_disk} does not exist')
+ exit(1)
if target_disk not in eligible_target_disks:
- print('Device {target_disk} can not be formatted'.format_map(fmt))
- sys.exit(1)
+ print(f'Device {target_disk} can not be formatted')
+ exit(1)
if proto_disk not in eligible_proto_disks:
- print('Device {proto_disk} can not be used as a prototype for {target_disk}'.format_map(fmt))
- sys.exit(1)
+ print(f'Device {proto_disk} can not be used as a prototype for {target_disk}')
+ exit(1)
if is_busy(target_disk):
- print("Disk device {target_disk} is busy. Can't format it now".format_map(fmt))
- sys.exit(1)
+ print(f'Disk device {target_disk} is busy, unable to format')
+ exit(1)
- print('This will re-format disk {target_disk} so that it has the same disk\n'
- 'partion sizes and offsets as {proto_disk}. This will not copy\n'
- 'data from {proto_disk} to {target_disk}. But this will erase all\n'
- 'data on {target_disk}.\n'.format_map(fmt))
+ print(f'\nThis will re-format disk {target_disk} so that it has the same disk'
+ f'\npartion sizes and offsets as {proto_disk}. This will not copy'
+ f'\ndata from {proto_disk} to {target_disk}. But this will erase all'
+ f'\ndata on {target_disk}.\n')
- if not ask_yes_no("Do you wish to proceed?"):
- print('OK. Disk drive {target_disk} will not be re-formated'.format_map(fmt))
- sys.exit(0)
+ if not ask_yes_no('Do you wish to proceed?'):
+ print(f'Disk drive {target_disk} will not be re-formated')
+ exit(0)
- print('OK. Re-formating disk drive {target_disk}...'.format_map(fmt))
+ print(f'Re-formating disk drive {target_disk}...')
print('Making backup copy of partitions...')
backup_partitions(target_disk)
- sleep(1)
print('Deleting old partitions...')
for p in list_partitions(target_disk):
delete_partition(disk=target_disk, partition_idx=p)
- print('Creating new partitions on {target_disk} based on {proto_disk}...'.format_map(fmt))
+ print(f'Creating new partitions on {target_disk} based on {proto_disk}...')
format_disk_like(target=target_disk, proto=proto_disk)
- print('Done.')
+ print('Done!')
diff --git a/src/op_mode/generate_ipsec_debug_archive.sh b/src/op_mode/generate_ipsec_debug_archive.sh
new file mode 100755
index 000000000..53d0a6eaa
--- /dev/null
+++ b/src/op_mode/generate_ipsec_debug_archive.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+# Collecting IPSec Debug Information
+
+DATE=`date +%d-%m-%Y`
+
+a_CMD=(
+ "sudo ipsec status"
+ "sudo swanctl -L"
+ "sudo swanctl -l"
+ "sudo swanctl -P"
+ "sudo ip x sa show"
+ "sudo ip x policy show"
+ "sudo ip tunnel show"
+ "sudo ip address"
+ "sudo ip rule show"
+ "sudo ip route"
+ "sudo ip route show table 220"
+ )
+
+
+echo "DEBUG: ${DATE} on host \"$(hostname)\"" > /tmp/ipsec-status-${DATE}.txt
+date >> /tmp/ipsec-status-${DATE}.txt
+
+# Execute all DEBUG commands and save it to file
+for cmd in "${a_CMD[@]}"; do
+ echo -e "\n### ${cmd} ###" >> /tmp/ipsec-status-${DATE}.txt
+ ${cmd} >> /tmp/ipsec-status-${DATE}.txt 2>/dev/null
+done
+
+# Collect charon logs, build .tgz archive
+sudo journalctl /usr/lib/ipsec/charon > /tmp/journalctl-charon-${DATE}.txt && \
+sudo tar -zcvf /tmp/ipsec-debug-${DATE}.tgz /tmp/journalctl-charon-${DATE}.txt /tmp/ipsec-status-${DATE}.txt >& /dev/null
+sudo rm -f /tmp/journalctl-charon-${DATE}.txt /tmp/ipsec-status-${DATE}.txt
+
+echo "Debug file is generated and located in /tmp/ipsec-debug-${DATE}.tgz"
diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py
index 731e71891..b9ebc991a 100755
--- a/src/op_mode/lldp_op.py
+++ b/src/op_mode/lldp_op.py
@@ -55,6 +55,9 @@ def parse_data(data, interface):
if interface is not None and local_if != interface:
continue
for chassis, c_value in values.get('chassis', {}).items():
+ # bail out early if no capabilities found
+ if 'capability' not in c_value:
+ continue
capabilities = c_value['capability']
if isinstance(capabilities, dict):
capabilities = [capabilities]
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index 2283cd820..bc7813052 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -35,6 +35,7 @@ from vyos.pki import verify_certificate
from vyos.xml import defaults
from vyos.util import ask_input, ask_yes_no
from vyos.util import cmd
+from vyos.util import install_into_config
CERT_REQ_END = '-----END CERTIFICATE REQUEST-----'
auth_dir = '/config/auth'
@@ -142,48 +143,50 @@ def get_revoked_by_serial_numbers(serial_numbers=[]):
return certs_out
def install_certificate(name, cert='', private_key=None, key_type=None, key_passphrase=None, is_ca=False):
- # Show conf commands for installing certificate
+ # Show/install conf commands for certificate
prefix = 'ca' if is_ca else 'certificate'
- print('Configure mode commands to install:')
- base = f"set pki {prefix} {name}"
+ base = f"pki {prefix} {name}"
+ config_paths = []
if cert:
cert_pem = "".join(encode_certificate(cert).strip().split("\n")[1:-1])
- print(f"{base} certificate '{cert_pem}'")
+ config_paths.append(f"{base} certificate '{cert_pem}'")
if private_key:
key_pem = "".join(encode_private_key(private_key, passphrase=key_passphrase).strip().split("\n")[1:-1])
- print(f"{base} private key '{key_pem}'")
+ config_paths.append(f"{base} private key '{key_pem}'")
if key_passphrase:
- print(f"{base} private password-protected")
+ config_paths.append(f"{base} private password-protected")
+
+ install_into_config(conf, config_paths)
def install_crl(ca_name, crl):
- # Show conf commands for installing crl
- print("Configure mode commands to install CRL:")
+ # Show/install conf commands for crl
crl_pem = "".join(encode_certificate(crl).strip().split("\n")[1:-1])
- print(f"set pki ca {ca_name} crl '{crl_pem}'")
+ install_into_config(conf, [f"pki ca {ca_name} crl '{crl_pem}'"])
def install_dh_parameters(name, params):
- # Show conf commands for installing dh params
- print("Configure mode commands to install DH parameters:")
+ # Show/install conf commands for dh params
dh_pem = "".join(encode_dh_parameters(params).strip().split("\n")[1:-1])
- print(f"set pki dh {name} parameters '{dh_pem}'")
+ install_into_config(conf, [f"pki dh {name} parameters '{dh_pem}'"])
def install_ssh_key(name, public_key, private_key, passphrase=None):
- # Show conf commands for installing ssh key
+ # Show/install conf commands for ssh key
key_openssh = encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH')
username = os.getlogin()
type_key_split = key_openssh.split(" ")
- base = f"set system login user {username} authentication public-keys {name}"
- print("Configure mode commands to install SSH key:")
- print(f"{base} key '{type_key_split[1]}'")
- print(f"{base} type '{type_key_split[0]}'", end="\n\n")
+ base = f"system login user {username} authentication public-keys {name}"
+ install_into_config(conf, [
+ f"{base} key '{type_key_split[1]}'",
+ f"{base} type '{type_key_split[0]}'"
+ ])
print(encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase))
def install_keypair(name, key_type, private_key=None, public_key=None, passphrase=None):
- # Show conf commands for installing key-pair
- print("Configure mode commands to install key pair:")
+ # Show/install conf commands for key-pair
+
+ config_paths = []
if public_key:
install_public_key = ask_yes_no('Do you want to install the public key?', default=True)
@@ -191,7 +194,7 @@ def install_keypair(name, key_type, private_key=None, public_key=None, passphras
if install_public_key:
install_public_pem = "".join(public_key_pem.strip().split("\n")[1:-1])
- print(f"set pki key-pair {name} public key '{install_public_pem}'")
+ config_paths.append(f"pki key-pair {name} public key '{install_public_pem}'")
else:
print("Public key:")
print(public_key_pem)
@@ -202,13 +205,15 @@ def install_keypair(name, key_type, private_key=None, public_key=None, passphras
if install_private_key:
install_private_pem = "".join(private_key_pem.strip().split("\n")[1:-1])
- print(f"set pki key-pair {name} private key '{install_private_pem}'")
+ config_paths.append(f"pki key-pair {name} private key '{install_private_pem}'")
if passphrase:
- print(f"set pki key-pair {name} private password-protected")
+ config_paths.append(f"pki key-pair {name} private password-protected")
else:
print("Private key:")
print(private_key_pem)
+ install_into_config(conf, config_paths)
+
def install_wireguard_key(interface, private_key, public_key):
# Show conf commands for installing wireguard key pairs
from vyos.ifconfig import Section
@@ -217,20 +222,10 @@ def install_wireguard_key(interface, private_key, public_key):
exit(1)
# Check if we are running in a config session - if yes, we can directly write to the CLI
- cli_string = f"interfaces wireguard {interface} private-key '{private_key}'"
- if Config().in_session():
- cmd(f"/opt/vyatta/sbin/my_set {cli_string}")
-
- print('"generate" CLI command executed from config session.\nGenerated private-key was imported to CLI!',end='\n\n')
- print(f'Use the following command to verify: show interfaces wireguard {interface}')
- else:
- print('"generate" CLI command executed from operational level.\n'
- 'Generated private-key is not stored to CLI, use configure mode commands to install key:', end='\n\n')
- print(f"set {cli_string}", end="\n\n")
+ install_into_config(conf, [f"interfaces wireguard {interface} private-key '{private_key}'"])
print(f"Corresponding public-key to use on peer system is: '{public_key}'")
-
def install_wireguard_psk(interface, peer, psk):
from vyos.ifconfig import Section
if Section.section(interface) != 'wireguard':
@@ -238,17 +233,7 @@ def install_wireguard_psk(interface, peer, psk):
exit(1)
# Check if we are running in a config session - if yes, we can directly write to the CLI
- cli_string = f"interfaces wireguard {interface} peer {peer} preshared-key '{psk}'"
- if Config().in_session():
- cmd(f"/opt/vyatta/sbin/my_set {cli_string}")
-
- print('"generate" CLI command executed from config session.\nGenerated preshared-key was imported to CLI!',end='\n\n')
- print(f'Use the following command to verify: show interfaces wireguard {interface}')
- else:
- print('"generate" CLI command executed from operational level.\n'
- 'Generated preshared-key is not stored to CLI, use configure mode commands to install key:', end='\n\n')
- print(f"set {cli_string}", end="\n\n")
-
+ install_into_config(conf, [f"interfaces wireguard {interface} peer {peer} preshared-key '{psk}'"])
def ask_passphrase():
passphrase = None
diff --git a/src/op_mode/ppp-server-ctrl.py b/src/op_mode/ppp-server-ctrl.py
index 670cdf879..e93963fdd 100755
--- a/src/op_mode/ppp-server-ctrl.py
+++ b/src/op_mode/ppp-server-ctrl.py
@@ -60,7 +60,7 @@ def main():
output, err = popen(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][args.proto]) + args.action + ses_pattern, stderr=DEVNULL, decode='utf-8')
if not err:
try:
- print(output)
+ print(f' {output}')
except:
sys.exit(0)
else:
diff --git a/src/op_mode/show_configuration_json.py b/src/op_mode/show_configuration_json.py
new file mode 100755
index 000000000..fdece533b
--- /dev/null
+++ b/src/op_mode/show_configuration_json.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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/>.
+
+import argparse
+import json
+
+from vyos.configquery import ConfigTreeQuery
+
+
+config = ConfigTreeQuery()
+c = config.get_config_dict()
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-p", "--pretty", action="store_true", help="Show pretty configuration in JSON format")
+
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+
+ if args.pretty:
+ print(json.dumps(c, indent=4))
+ else:
+ print(json.dumps(c))
diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py
index 3d50eb938..eac068274 100755
--- a/src/op_mode/show_interfaces.py
+++ b/src/op_mode/show_interfaces.py
@@ -94,10 +94,8 @@ def split_text(text, used=0):
used: number of characted already used in the screen
"""
no_tty = call('tty -s')
- if no_tty:
- return text.split()
- returned = cmd('stty size')
+ returned = cmd('stty size') if not no_tty else ''
if len(returned) == 2:
rows, columns = [int(_) for _ in returned]
else:
diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py
index c964caaeb..e72f0f965 100755
--- a/src/op_mode/show_ipsec_sa.py
+++ b/src/op_mode/show_ipsec_sa.py
@@ -46,7 +46,6 @@ def format_output(conns, sas):
if parent_sa["state"] == b"ESTABLISHED" and installed_sas:
state = "up"
- uptime = vyos.util.seconds_to_human(parent_sa["established"].decode())
remote_host = parent_sa["remote-host"].decode()
remote_id = parent_sa["remote-id"].decode()
@@ -75,6 +74,8 @@ def format_output(conns, sas):
# Remove B from <1K values
pkts_str = re.sub(r'B', r'', pkts_str)
+ uptime = vyos.util.seconds_to_human(isa['install-time'].decode())
+
enc = isa["encr-alg"].decode()
if "encr-keysize" in isa:
key_size = isa["encr-keysize"].decode()
diff --git a/src/op_mode/show_openvpn_mfa.py b/src/op_mode/show_openvpn_mfa.py
new file mode 100755
index 000000000..1ab54600c
--- /dev/null
+++ b/src/op_mode/show_openvpn_mfa.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+
+# Copyright 2017, 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/>.
+
+import re
+import socket
+import urllib.parse
+import argparse
+
+from vyos.util import popen
+
+otp_file = '/config/auth/openvpn/{interface}-otp-secrets'
+
+def get_mfa_secret(interface, client):
+ try:
+ with open(otp_file.format(interface=interface), "r") as f:
+ users = f.readlines()
+ for user in users:
+ if re.search('^' + client + ' ', user):
+ return user.split(':')[3]
+ except:
+ pass
+
+def get_mfa_uri(client, secret):
+ hostname = socket.gethostname()
+ fqdn = socket.getfqdn()
+ uri = 'otpauth://totp/{hostname}:{client}@{fqdn}?secret={secret}'
+
+ return urllib.parse.quote(uri.format(hostname=hostname, client=client, fqdn=fqdn, secret=secret), safe='/:@?=')
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(add_help=False, description='Show two-factor authentication information')
+ parser.add_argument('--intf', action="store", type=str, default='', help='only show the specified interface')
+ parser.add_argument('--user', action="store", type=str, default='', help='only show the specified users')
+ parser.add_argument('--action', action="store", type=str, default='show', help='action to perform')
+
+ args = parser.parse_args()
+ secret = get_mfa_secret(args.intf, args.user)
+
+ if args.action == "secret" and secret:
+ print(secret)
+
+ if args.action == "uri" and secret:
+ uri = get_mfa_uri(args.user, secret)
+ print(uri)
+
+ if args.action == "qrcode" and secret:
+ uri = get_mfa_uri(args.user, secret)
+ qrcode,err = popen('qrencode -t ansiutf8', input=uri)
+ print(qrcode)
+
diff --git a/src/op_mode/show_ram.py b/src/op_mode/show_ram.py
new file mode 100755
index 000000000..5818ec132
--- /dev/null
+++ b/src/op_mode/show_ram.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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/>.
+#
+
+def get_system_memory():
+ from re import search as re_search
+
+ def find_value(keyword, mem_data):
+ regex = keyword + ':\s+(\d+)'
+ res = re_search(regex, mem_data).group(1)
+ return int(res)
+
+ with open("/proc/meminfo", "r") as f:
+ mem_data = f.read()
+
+ total = find_value('MemTotal', mem_data)
+ available = find_value('MemAvailable', mem_data)
+ buffers = find_value('Buffers', mem_data)
+ cached = find_value('Cached', mem_data)
+
+ used = total - available
+
+ res = {
+ "total": total,
+ "free": available,
+ "used": used,
+ "buffers": buffers,
+ "cached": cached
+ }
+
+ return res
+
+def get_system_memory_human():
+ from vyos.util import bytes_to_human
+
+ mem = get_system_memory()
+
+ for key in mem:
+ # The Linux kernel exposes memory values in kilobytes,
+ # so we need to normalize them
+ mem[key] = bytes_to_human(mem[key], initial_exponent=10)
+
+ return mem
+
+if __name__ == '__main__':
+ mem = get_system_memory_human()
+
+ print("Total: {}".format(mem["total"]))
+ print("Free: {}".format(mem["free"]))
+ print("Used: {}".format(mem["used"]))
+
diff --git a/src/op_mode/show_ram.sh b/src/op_mode/show_ram.sh
deleted file mode 100755
index b013e16f8..000000000
--- a/src/op_mode/show_ram.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# Module: vyos-show-ram.sh
-# Displays memory usage information in minimalistic format
-#
-# Copyright (C) 2019 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 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/>.
-
-MB_DIVISOR=1024
-
-TOTAL=$(cat /proc/meminfo | grep -E "^MemTotal:" | awk -F ' ' '{print $2}')
-FREE=$(cat /proc/meminfo | grep -E "^MemFree:" | awk -F ' ' '{print $2}')
-BUFFERS=$(cat /proc/meminfo | grep -E "^Buffers:" | awk -F ' ' '{print $2}')
-CACHED=$(cat /proc/meminfo | grep -E "^Cached:" | awk -F ' ' '{print $2}')
-
-DISPLAY_FREE=$(( ($FREE + $BUFFERS + $CACHED) / $MB_DIVISOR ))
-DISPLAY_TOTAL=$(( $TOTAL / $MB_DIVISOR ))
-DISPLAY_USED=$(( $DISPLAY_TOTAL - $DISPLAY_FREE ))
-
-echo "Total: $DISPLAY_TOTAL"
-echo "Free: $DISPLAY_FREE"
-echo "Used: $DISPLAY_USED"
diff --git a/src/op_mode/show_uptime.py b/src/op_mode/show_uptime.py
new file mode 100755
index 000000000..c3dea52e6
--- /dev/null
+++ b/src/op_mode/show_uptime.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 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/>.
+
+def get_uptime_seconds():
+ from re import search
+ from vyos.util import read_file
+
+ data = read_file("/proc/uptime")
+ seconds = search("([0-9\.]+)\s", data).group(1)
+
+ return int(float(seconds))
+
+def get_load_averages():
+ from re import search
+ from vyos.util import cmd
+
+ data = cmd("uptime")
+ matches = search(r"load average:\s*(?P<one>[0-9\.]+)\s*,\s*(?P<five>[0-9\.]+)\s*,\s*(?P<fifteen>[0-9\.]+)\s*", data)
+
+ res = {}
+ res[1] = float(matches["one"])
+ res[5] = float(matches["five"])
+ res[15] = float(matches["fifteen"])
+
+ return res
+
+if __name__ == '__main__':
+ from vyos.util import seconds_to_human
+
+ print("Uptime: {}\n".format(seconds_to_human(get_uptime_seconds())))
+
+ avgs = get_load_averages()
+
+ print("Load averages:")
+ print("1 minute: {:.02f}%".format(avgs[1]*100))
+ print("5 minutes: {:.02f}%".format(avgs[5]*100))
+ print("15 minutes: {:.02f}%".format(avgs[15]*100))
diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py
index 06e227ccf..40854fa8f 100755
--- a/src/op_mode/vpn_ipsec.py
+++ b/src/op_mode/vpn_ipsec.py
@@ -48,7 +48,7 @@ def reset_peer(peer, tunnel):
result = True
for conn in conns:
try:
- call(f'sudo /usr/sbin/ipsec down {conn}', timeout = 10)
+ call(f'sudo /usr/sbin/ipsec down {conn}{{*}}', timeout = 10)
call(f'sudo /usr/sbin/ipsec up {conn}', timeout = 10)
except TimeoutExpired as e:
print(f'Timed out while resetting {conn}')