From 76f498220c796a39a92ce3bed47c1cc5bac1a99d Mon Sep 17 00:00:00 2001 From: Dmytro Aleksandrov Date: Fri, 30 Aug 2019 18:43:44 +0300 Subject: [op-mode] T1621 rewrite misc commands to python/xml syntax --- op-mode-definitions/bandwidth-test.xml | 29 +++++ op-mode-definitions/disks.xml | 50 ++++++++ op-mode-definitions/generate-ssh-server-key.xml | 16 +++ op-mode-definitions/show-disk.xml | 23 ---- op-mode-definitions/show-history.xml | 31 +++++ op-mode-definitions/show-host.xml | 12 ++ op-mode-definitions/telnet.xml | 8 +- op-mode-definitions/terminal.xml | 93 +++++++++++++++ python/vyos/util.py | 8 ++ src/completion/list_disks.py | 36 ++++++ src/completion/list_disks.sh | 5 - src/helpers/vyos-sudo.py | 9 +- src/op_mode/format_disk.py | 148 ++++++++++++++++++++++++ src/op_mode/generate_ssh_server_key.py | 27 +++++ src/op_mode/toggle_help_binding.sh | 25 ++++ 15 files changed, 480 insertions(+), 40 deletions(-) create mode 100644 op-mode-definitions/bandwidth-test.xml create mode 100644 op-mode-definitions/disks.xml create mode 100644 op-mode-definitions/generate-ssh-server-key.xml delete mode 100644 op-mode-definitions/show-disk.xml create mode 100644 op-mode-definitions/show-history.xml create mode 100755 src/completion/list_disks.py delete mode 100755 src/completion/list_disks.sh create mode 100755 src/op_mode/format_disk.py create mode 100755 src/op_mode/generate_ssh_server_key.py create mode 100755 src/op_mode/toggle_help_binding.sh diff --git a/op-mode-definitions/bandwidth-test.xml b/op-mode-definitions/bandwidth-test.xml new file mode 100644 index 000000000..d1e459b17 --- /dev/null +++ b/op-mode-definitions/bandwidth-test.xml @@ -0,0 +1,29 @@ + + + + + + + Initiate or wait for bandwidth test + + + + + Wait for bandwidth test connections (port TCP/5001) + + iperf -s + + + + Initiate a bandwidth test to specified host (port TCP/5001) + + <hostname> <x.x.x.x> <h:h:h:h:h:h:h:h> + + + iperf -c $4 + + + + + + diff --git a/op-mode-definitions/disks.xml b/op-mode-definitions/disks.xml new file mode 100644 index 000000000..fb39c4f3c --- /dev/null +++ b/op-mode-definitions/disks.xml @@ -0,0 +1,50 @@ + + + + + Format a device + + + + + Format a disk drive + + + + + + + + Format this disk the same as another disk + + + + + ${vyos_op_scripts_dir}/format_disk.py --target $3 --proto $5 + + + + + + + + + + + Show status of disk device + + + + + + + + Show disk drive formatting + + ${vyos_op_scripts_dir}/show_disk_format.sh $3 + + + + + + diff --git a/op-mode-definitions/generate-ssh-server-key.xml b/op-mode-definitions/generate-ssh-server-key.xml new file mode 100644 index 000000000..a6ebf1b78 --- /dev/null +++ b/op-mode-definitions/generate-ssh-server-key.xml @@ -0,0 +1,16 @@ + + + + + Generate an object + + + + + Regenerate the host SSH keys and restart the SSH server + + ${vyos_op_scripts_dir}/generate_ssh_server_key.py + + + + diff --git a/op-mode-definitions/show-disk.xml b/op-mode-definitions/show-disk.xml deleted file mode 100644 index 37da07fbe..000000000 --- a/op-mode-definitions/show-disk.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Show status of disk device - - - - - - - - Show disk drive formatting - - ${vyos_op_scripts_dir}/show_disk_format.sh $3 - - - - - - diff --git a/op-mode-definitions/show-history.xml b/op-mode-definitions/show-history.xml new file mode 100644 index 000000000..7fb286264 --- /dev/null +++ b/op-mode-definitions/show-history.xml @@ -0,0 +1,31 @@ + + + + + + + Show command history + + HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history + + + + Show recent command history + + HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history 20 + + + + + + + Show last N commands in history + + <NUMBER> + + + HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history $3 + + + + diff --git a/op-mode-definitions/show-host.xml b/op-mode-definitions/show-host.xml index d7f8104aa..eee1288a1 100644 --- a/op-mode-definitions/show-host.xml +++ b/op-mode-definitions/show-host.xml @@ -7,6 +7,12 @@ Show host information + + + Show host current date + + /bin/date + Show domain name @@ -25,6 +31,12 @@ /usr/bin/host $4 + + + Show host operating system details + + /bin/uname -a + diff --git a/op-mode-definitions/telnet.xml b/op-mode-definitions/telnet.xml index 7ec75be88..c5bb6d283 100644 --- a/op-mode-definitions/telnet.xml +++ b/op-mode-definitions/telnet.xml @@ -5,23 +5,23 @@ Telnet to a node - + Telnet to a host <hostname> <x.x.x.x> <h:h:h:h:h:h:h:h> - /usr/bin/telnet $2 + /usr/bin/telnet $3 - + Telnet to a host:port <0-65535> - /usr/bin/telnet $2 $3 + /usr/bin/telnet $3 $5 diff --git a/op-mode-definitions/terminal.xml b/op-mode-definitions/terminal.xml index db74f867e..9c4e629cb 100644 --- a/op-mode-definitions/terminal.xml +++ b/op-mode-definitions/terminal.xml @@ -26,4 +26,97 @@ + + + Set operational options + + + + + Bash builtin set command + + <OPTION> + + + builtin $3 + + + + + Control console behaviors + + + + + Reconfigure console keyboard layout + + sudo dpkg-reconfigure -f dialog keyboard-configuration && sudo systemctl restart keyboard-setup + + + + + + + Control terminal behaviors + + + + + + Set key behaviors + + + + + Enable/disable getting help using question mark (default enabled) + + enable disable + + + ${vyos_op_scripts_dir}/toggle_help_binding.sh $5 + + + + + + + Set terminal pager to default (less) + + VYATTA_PAGER=${_vyatta_default_pager} + + + + Set terminal pager + + <PROGRAM> + + + VYATTA_PAGER=$4 + + + + + Set terminal to given number of rows (0 disables paging) + + <NUMBER> + + + if [ "$4" -eq 0 ]; then VYATTA_PAGER=cat; else VYATTA_PAGER=${_vyatta_default_pager}; stty rows $4; fi + + + + + Set terminal to given number of columns + + <NUMBER> + + + stty columns $4 + + + + + + + diff --git a/python/vyos/util.py b/python/vyos/util.py index 88bb0c8f4..67a602f7a 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -15,6 +15,7 @@ import os import re +import getpass import grp import time import subprocess @@ -191,3 +192,10 @@ def ask_yes_no(question, default=False) -> bool: return False else: sys.stdout.write("Please respond with yes/y or no/n\n") + + +def is_admin() -> bool: + """Look if current user is in sudo group""" + current_user = getpass.getuser() + (_, _, _, admin_group_members) = grp.getgrnam('sudo') + return current_user in admin_group_members diff --git a/src/completion/list_disks.py b/src/completion/list_disks.py new file mode 100755 index 000000000..ff1135e23 --- /dev/null +++ b/src/completion/list_disks.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# 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 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 . + +# Completion script used by show disks to collect physical disk + +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--exclude", type=str, help="Exclude specified device from the result list") +args = parser.parse_args() + +disks = set() +with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + +if args.exclude: + disks.remove(args.exclude) + +for disk in disks: + print(disk) diff --git a/src/completion/list_disks.sh b/src/completion/list_disks.sh deleted file mode 100755 index f32e558fd..000000000 --- a/src/completion/list_disks.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# Completion script used by show disks to collect physical disk - -awk 'NR > 2 && $4 !~ /[0-9]$/ { print $4 }' . -import getpass -import grp import os import sys - -def is_admin() -> bool: - """Look if current user is in sudo group""" - current_user = getpass.getuser() - (_, _, _, admin_group_members) = grp.getgrnam('sudo') - return current_user in admin_group_members +from vyos.util import is_admin if __name__ == '__main__': diff --git a/src/op_mode/format_disk.py b/src/op_mode/format_disk.py new file mode 100755 index 000000000..5a3b250ee --- /dev/null +++ b/src/op_mode/format_disk.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# +# 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 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 . + +import argparse +import os +import re +import subprocess +import sys +from datetime import datetime +from time import sleep + +from vyos.util import is_admin, ask_yes_no + + +def list_disks(): + disks = set() + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + return disks + + +def is_busy(disk: str): + """Check if given disk device is busy by re-reading it's partition table""" + + cmd = 'sudo blockdev --rereadpt /dev/{}'.format(disk) + status = subprocess.call([cmd], shell=True, stderr=subprocess.DEVNULL) + return status != 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 = 'sudo /sbin/sfdisk -d {} > {}'.format(device_path, backup_file) + subprocess.check_call([cmd], shell=True) + + +def list_partitions(disk: str): + """List partition numbers of a given disk""" + + parts = set() + part_num_expr = re.compile(disk + '([0-9]+)') + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3] != 'name' and part_num_expr.match(fields[3]): + part_idx = part_num_expr.match(fields[3]).group(1) + parts.add(int(part_idx)) + return parts + + +def delete_partition(disk: str, partition_idx: int): + cmd = 'sudo /sbin/parted /dev/{} rm {}'.format(disk, partition_idx) + subprocess.check_call([cmd], shell=True) + + +def format_disk_like(target: str, proto: str): + cmd = 'sudo /sbin/sfdisk -d /dev/{} | sudo /sbin/sfdisk --force /dev/{}'.format(proto, target) + subprocess.check_call([cmd], shell=True) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + group = parser.add_argument_group() + group.add_argument('-t', '--target', type=str, required=True, help='Target device to format') + 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() + + proto_disk = args.proto + 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) + + 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('/dev/' + target_disk): + print('Device /dev/{target_disk} does not exist'.format_map(fmt)) + sys.exit(1) + + if target_disk not in eligible_target_disks: + print('Device {target_disk} can not be formatted'.format_map(fmt)) + sys.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) + + if is_busy(target_disk): + print("Disk device {target_disk} is busy. Can't format it now".format_map(fmt)) + sys.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)) + + 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) + + print('OK. Re-formating disk drive {target_disk}...'.format_map(fmt)) + + 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)) + format_disk_like(target=target_disk, proto=proto_disk) + print('Done.') diff --git a/src/op_mode/generate_ssh_server_key.py b/src/op_mode/generate_ssh_server_key.py new file mode 100755 index 000000000..f205919b8 --- /dev/null +++ b/src/op_mode/generate_ssh_server_key.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# 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 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 . + +import subprocess +import sys + +from vyos.util import ask_yes_no + +if not ask_yes_no('Do you really want to remove the existing SSH host keys?'): + sys.exit(0) +else: + subprocess.check_call(['sudo rm -v /etc/ssh/ssh_host_*'], shell=True) + subprocess.check_call(['sudo dpkg-reconfigure openssh-server'], shell=True) + subprocess.check_call(['sudo systemctl restart ssh'], shell=True) diff --git a/src/op_mode/toggle_help_binding.sh b/src/op_mode/toggle_help_binding.sh new file mode 100755 index 000000000..a8708f3da --- /dev/null +++ b/src/op_mode/toggle_help_binding.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# 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 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 . + +# Script for [un-]binding the question mark key for getting help +if [ "$1" == 'disable' ]; then + sed -i "/^bind '\"?\": .* # vyatta key binding$/d" $HOME/.bashrc + echo "bind '\"?\": self-insert' # vyatta key binding" >> $HOME/.bashrc + bind '"?": self-insert' +else + sed -i "/^bind '\"?\": .* # vyatta key binding$/d" $HOME/.bashrc + bind '"?": possible-completions' +fi -- cgit v1.2.3