summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--op-mode-definitions/bandwidth-test.xml29
-rw-r--r--op-mode-definitions/disks.xml50
-rw-r--r--op-mode-definitions/generate-ssh-server-key.xml16
-rw-r--r--op-mode-definitions/show-disk.xml23
-rw-r--r--op-mode-definitions/show-history.xml31
-rw-r--r--op-mode-definitions/show-host.xml12
-rw-r--r--op-mode-definitions/telnet.xml8
-rw-r--r--op-mode-definitions/terminal.xml93
-rw-r--r--python/vyos/util.py8
-rwxr-xr-xsrc/completion/list_disks.py36
-rwxr-xr-xsrc/completion/list_disks.sh5
-rwxr-xr-xsrc/helpers/vyos-sudo.py9
-rwxr-xr-xsrc/op_mode/format_disk.py148
-rwxr-xr-xsrc/op_mode/generate_ssh_server_key.py27
-rwxr-xr-xsrc/op_mode/toggle_help_binding.sh25
15 files changed, 480 insertions, 40 deletions
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 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="monitor">
+ <children>
+ <node name="bandwidth-test">
+ <properties>
+ <help>Initiate or wait for bandwidth test</help>
+ </properties>
+ <children>
+ <leafNode name="accept">
+ <properties>
+ <help>Wait for bandwidth test connections (port TCP/5001)</help>
+ </properties>
+ <command>iperf -s</command>
+ </leafNode>
+ <tagNode name="initiate">
+ <properties>
+ <help>Initiate a bandwidth test to specified host (port TCP/5001)</help>
+ <completionHelp>
+ <list>&lt;hostname&gt; &lt;x.x.x.x&gt; &lt;h:h:h:h:h:h:h:h&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>iperf -c $4</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
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 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="format">
+ <properties>
+ <help>Format a device</help>
+ </properties>
+ <children>
+ <tagNode name="disk">
+ <properties>
+ <help>Format a disk drive</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_disks.py</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="like">
+ <properties>
+ <help>Format this disk the same as another disk</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_disks.py --exclude ${COMP_WORDS[2]}</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/format_disk.py --target $3 --proto $5</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+
+ <node name="show">
+ <children>
+ <tagNode name="disk">
+ <properties>
+ <help>Show status of disk device</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_disks.py</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="format">
+ <properties>
+ <help>Show disk drive formatting</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_disk_format.sh $3</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
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 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="generate">
+ <properties>
+ <help>Generate an object</help>
+ </properties>
+ <children>
+ <node name="ssh-server-key">
+ <properties>
+ <help>Regenerate the host SSH keys and restart the SSH server</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/generate_ssh_server_key.py</command>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
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 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="show">
- <children>
- <tagNode name="disk">
- <properties>
- <help>Show status of disk device</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_disks.sh</script>
- </completionHelp>
- </properties>
- <children>
- <leafNode name="format">
- <properties>
- <help>Show disk drive formatting</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_disk_format.sh $3</command>
- </leafNode>
- </children>
- </tagNode>
- </children>
- </node>
-</interfaceDefinition>
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 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="history">
+ <properties>
+ <help>Show command history</help>
+ </properties>
+ <command>HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history</command>
+ <children>
+ <leafNode name="brief">
+ <properties>
+ <help>Show recent command history</help>
+ </properties>
+ <command>HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history 20</command>
+ </leafNode>
+ </children>
+ </node>
+
+ <tagNode name="history">
+ <properties>
+ <help>Show last N commands in history</help>
+ <completionHelp>
+ <list>&lt;NUMBER&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history $3</command>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
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 @@
<help>Show host information</help>
</properties>
<children>
+ <leafNode name="date">
+ <properties>
+ <help>Show host current date</help>
+ </properties>
+ <command>/bin/date</command>
+ </leafNode>
<leafNode name="domain">
<properties>
<help>Show domain name</help>
@@ -25,6 +31,12 @@
</properties>
<command>/usr/bin/host $4</command>
</tagNode>
+ <leafNode name="os">
+ <properties>
+ <help>Show host operating system details</help>
+ </properties>
+ <command>/bin/uname -a</command>
+ </leafNode>
</children>
</node>
</children>
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 @@
<help>Telnet to a node</help>
</properties>
<children>
- <tagNode name="">
+ <tagNode name="to">
<properties>
<help>Telnet to a host</help>
<completionHelp>
<list>&lt;hostname&gt; &lt;x.x.x.x&gt; &lt;h:h:h:h:h:h:h:h&gt;</list>
</completionHelp>
</properties>
- <command>/usr/bin/telnet $2</command>
+ <command>/usr/bin/telnet $3</command>
<children>
- <tagNode name="">
+ <tagNode name="port">
<properties>
<help>Telnet to a host:port</help>
<completionHelp>
<list>&lt;0-65535&gt;</list>
</completionHelp>
</properties>
- <command>/usr/bin/telnet $2 $3</command>
+ <command>/usr/bin/telnet $3 $5</command>
</tagNode>
</children>
</tagNode>
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 @@
</node>
</children>
</node>
+ <node name="set">
+ <properties>
+ <help>Set operational options</help>
+ </properties>
+ <children>
+ <tagNode name="builtin">
+ <properties>
+ <help>Bash builtin set command</help>
+ <completionHelp>
+ <list>&lt;OPTION&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>builtin $3</command>
+ </tagNode>
+
+ <node name="console">
+ <properties>
+ <help>Control console behaviors</help>
+ </properties>
+ <children>
+ <leafNode name="keymap">
+ <properties>
+ <help>Reconfigure console keyboard layout</help>
+ </properties>
+ <command>sudo dpkg-reconfigure -f dialog keyboard-configuration &amp;&amp; sudo systemctl restart keyboard-setup</command>
+ </leafNode>
+ </children>
+ </node>
+
+ <node name="terminal">
+ <properties>
+ <help>Control terminal behaviors</help>
+ </properties>
+ <children>
+
+ <node name="key">
+ <properties>
+ <help>Set key behaviors</help>
+ </properties>
+ <children>
+ <tagNode name="query-help">
+ <properties>
+ <help>Enable/disable getting help using question mark (default enabled)</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/toggle_help_binding.sh $5</command>
+ </tagNode>
+ </children>
+ </node>
+
+ <node name="pager">
+ <properties>
+ <help>Set terminal pager to default (less)</help>
+ </properties>
+ <command>VYATTA_PAGER=${_vyatta_default_pager}</command>
+ </node>
+ <tagNode name="pager">
+ <properties>
+ <help>Set terminal pager</help>
+ <completionHelp>
+ <list>&lt;PROGRAM&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>VYATTA_PAGER=$4</command>
+ </tagNode>
+
+ <tagNode name="length">
+ <properties>
+ <help>Set terminal to given number of rows (0 disables paging)</help>
+ <completionHelp>
+ <list>&lt;NUMBER&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>if [ "$4" -eq 0 ]; then VYATTA_PAGER=cat; else VYATTA_PAGER=${_vyatta_default_pager}; stty rows $4; fi</command>
+ </tagNode>
+
+ <tagNode name="width">
+ <properties>
+ <help>Set terminal to given number of columns</help>
+ <completionHelp>
+ <list>&lt;NUMBER&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>stty columns $4</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+
+
</interfaceDefinition>
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 <http://www.gnu.org/licenses/>.
+
+# 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 }' </proc/partitions
diff --git a/src/helpers/vyos-sudo.py b/src/helpers/vyos-sudo.py
index 0101a0c95..3e4c196d9 100755
--- a/src/helpers/vyos-sudo.py
+++ b/src/helpers/vyos-sudo.py
@@ -15,17 +15,10 @@
# 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 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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+# 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