mirror of
https://github.com/GNS3/gns3-registry.git
synced 2025-01-02 19:16:41 +00:00
commit
d043363b69
60
packer/NETem/NETem.json
Normal file
60
packer/NETem/NETem.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"tc_iso_url": "http://distro.ibiblio.org/tinycorelinux/6.x/x86/release/Core-6.4.iso",
|
||||||
|
"tc_iso_checksum": "c8e04e26de234e5528e6eac8ecb1bdda",
|
||||||
|
"vm_name": "NETem.qcow2",
|
||||||
|
"setup_script": "NETem.sh",
|
||||||
|
"upload_dir": "uploads",
|
||||||
|
"disk_size": "32"
|
||||||
|
},
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "qemu",
|
||||||
|
"iso_url": "{{user `tc_iso_url`}}",
|
||||||
|
"iso_checksum": "{{user `tc_iso_checksum`}}",
|
||||||
|
"iso_checksum_type": "md5",
|
||||||
|
"shutdown_command": "sudo poweroff",
|
||||||
|
"format": "qcow2",
|
||||||
|
"headless": false,
|
||||||
|
"ssh_username": "gns3",
|
||||||
|
"ssh_password": "gns3",
|
||||||
|
"accelerator": "none",
|
||||||
|
"vm_name": "{{user `vm_name`}}",
|
||||||
|
"disk_interface": "ide",
|
||||||
|
"disk_size": "{{user `disk_size`}}",
|
||||||
|
"net_device": "e1000",
|
||||||
|
"http_directory": "http",
|
||||||
|
"boot_wait": "5s",
|
||||||
|
"boot_command": [
|
||||||
|
"mc user=gns3<enter><wait10><wait10><wait10><wait10><wait10><wait10>",
|
||||||
|
"sudo passwd gns3<enter>gns3<enter>gns3<enter>",
|
||||||
|
"tce-load -wi openssh<enter><wait10>",
|
||||||
|
"cd /usr/local/etc/ssh; [ -f sshd_config.example ] && sudo cp -a sshd_config.example sshd_config; cd<enter>",
|
||||||
|
"sudo /usr/local/etc/init.d/openssh start<enter>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"script": "scripts/hd-install.sh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"script": "scripts/serial.sh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "{{user `upload_dir`}}/",
|
||||||
|
"destination": "/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"script": "scripts/{{user `setup_script`}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"script": "scripts/post_setup.sh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
112
packer/NETem/scripts/NETem.sh
Normal file
112
packer/NETem/scripts/NETem.sh
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# get TinyCore mirror
|
||||||
|
. /etc/init.d/tc-functions
|
||||||
|
getMirror
|
||||||
|
|
||||||
|
# TCE directory back to ramdisk
|
||||||
|
mv /etc/sysconfig/tcedir /etc/sysconfig/tcedir.hd
|
||||||
|
ln -s /tmp/tce /etc/sysconfig/tcedir
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
tce-load -wi squashfs-tools
|
||||||
|
|
||||||
|
# create utf8-locale
|
||||||
|
tce-load -wi getlocale
|
||||||
|
sudo mkdir -p /usr/lib/locale
|
||||||
|
sudo localedef -i en_US -c -f UTF-8 en_US.UTF-8
|
||||||
|
sudo localedef -i en_US -c -f UTF-8 C.UTF-8
|
||||||
|
sudo mkdir -p /tmp/utf8-locale/usr/lib/locale
|
||||||
|
sudo cp -p /usr/lib/locale/* /tmp/utf8-locale/usr/lib/locale/
|
||||||
|
mksquashfs /tmp/utf8-locale utf8-locale.tcz
|
||||||
|
md5sum utf8-locale.tcz > utf8-locale.tcz.md5.txt
|
||||||
|
|
||||||
|
# create python3dialog
|
||||||
|
tce-load -wi python3-dev
|
||||||
|
wget https://bootstrap.pypa.io/get-pip.py
|
||||||
|
sudo python3 get-pip.py
|
||||||
|
rm get-pip.py
|
||||||
|
tce-load -wi dialog
|
||||||
|
sudo LANG=C.UTF-8 pip3 install pythondialog
|
||||||
|
sudo mkdir -p /tmp/python3dialog/usr/local/lib/python3.4/site-packages
|
||||||
|
sudo cp -a /usr/local/lib/python3.4/site-packages/dialog* /tmp/python3dialog/usr/local/lib/python3.4/site-packages/
|
||||||
|
sudo cp -a /usr/local/lib/python3.4/site-packages/pythondialog* /tmp/python3dialog/usr/local/lib/python3.4/site-packages/
|
||||||
|
mksquashfs /tmp/python3dialog python3dialog.tcz
|
||||||
|
md5sum python3dialog.tcz > python3dialog.tcz.md5.txt
|
||||||
|
echo -e 'python3.tcz\ndialog.tcz' > python3dialog.tcz.dep
|
||||||
|
|
||||||
|
# TCEDIR back to harddisk
|
||||||
|
rm -f /etc/sysconfig/tcedir; mv /etc/sysconfig/tcedir.hd /etc/sysconfig/tcedir
|
||||||
|
mkdir -p /etc/sysconfig/tcedir/optional
|
||||||
|
chmod 775 /etc/sysconfig/tcedir/optional
|
||||||
|
rm -f /usr/local/tce.installed/*
|
||||||
|
|
||||||
|
# install utf8-locale
|
||||||
|
cp -p utf8-locale.tcz* /etc/sysconfig/tcedir/optional/
|
||||||
|
echo 'utf8-locale.tcz' >> /etc/sysconfig/tcedir/onboot.lst
|
||||||
|
|
||||||
|
# install python3 without TK
|
||||||
|
cp -p /tmp/tce/optional/python3.tcz /etc/sysconfig/tcedir/optional/
|
||||||
|
cp -p /tmp/tce/optional/python3.tcz.md5.txt /etc/sysconfig/tcedir/optional/
|
||||||
|
sed -e '/^tk/ d' /tmp/tce/optional/python3.tcz.dep > /etc/sysconfig/tcedir/optional/python3.tcz.dep
|
||||||
|
echo 'python3.tcz' >> /etc/sysconfig/tcedir/onboot.lst
|
||||||
|
for pkg in `cat /etc/sysconfig/tcedir/optional/python3.tcz.dep`; do tce-load -w $pkg; done
|
||||||
|
|
||||||
|
# install python3dialog
|
||||||
|
cp -p python3dialog.tcz* /etc/sysconfig/tcedir/optional/
|
||||||
|
echo 'python3dialog.tcz' >> /etc/sysconfig/tcedir/onboot.lst
|
||||||
|
tce-load -w dialog
|
||||||
|
|
||||||
|
# additional linux networking modules
|
||||||
|
KERNEL=`uname -r`
|
||||||
|
tce-load -w net-bridging-$KERNEL
|
||||||
|
echo "net-bridging-$KERNEL.tcz" >> /etc/sysconfig/tcedir/onboot.lst
|
||||||
|
tce-load -w net-sched-$KERNEL
|
||||||
|
echo "net-sched-$KERNEL.tcz" >> /etc/sysconfig/tcedir/onboot.lst
|
||||||
|
|
||||||
|
# iproute2 without db library
|
||||||
|
# Bug in TinyCore 6.x, which makes arpd non-working:
|
||||||
|
# There is a mismatch of the library version between arpd and the db library.
|
||||||
|
# Therefore loading the db library has no advantage, it uses only disk space.
|
||||||
|
wget $MIRROR/iproute2.tcz
|
||||||
|
wget $MIRROR/iproute2.tcz.md5.txt
|
||||||
|
cp -p iproute2.tcz* /etc/sysconfig/tcedir/optional/
|
||||||
|
echo 'iproute2.tcz' >> /etc/sysconfig/tcedir/onboot.lst
|
||||||
|
|
||||||
|
# clean up build environment
|
||||||
|
cd ..
|
||||||
|
rm -r build
|
||||||
|
|
||||||
|
# NETem menu system
|
||||||
|
mv /tmp/netem-conf.py .
|
||||||
|
chmod +x netem-conf.py
|
||||||
|
|
||||||
|
# autologin on serial console
|
||||||
|
sudo sed -i -e '/^tty1:/ s/^.*/tty1::respawn:\/sbin\/getty 38400 tty1/' -e '/^ttyS0:/ s/^.*/ttyS0::askfirst:\/sbin\/getty -nl \/sbin\/autologin 38400 ttyS0 xterm/' /etc/inittab
|
||||||
|
sudo sed -i -e 's/tty1/`\/usr\/bin\/tty`/' /sbin/autologin
|
||||||
|
echo 'sbin/autologin' >> /opt/.filetool.lst
|
||||||
|
|
||||||
|
# autostart netem-conf
|
||||||
|
sed -i -e '/^TERMTYPE/,$ d' .profile
|
||||||
|
cat >> .profile << 'EOF'
|
||||||
|
# autostart netem-conf only on local terminals
|
||||||
|
TERMTYPE=`/usr/bin/tty`
|
||||||
|
if [ "${TERMTYPE:5:3}" = "tty" ]; then
|
||||||
|
./netem-conf.py
|
||||||
|
rm -f /var/log/autologin
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# disable automatic interface configuration with dhcp
|
||||||
|
sudo sed -i -e '/label microcore/,/append / s/\(append .*\)/\1 nodhcp/' /mnt/sda1/boot/extlinux/extlinux.conf
|
||||||
|
|
||||||
|
# set locale and configure network at startup
|
||||||
|
sed -n -e '1,/^\/opt\/bootlocal/ p' /opt/bootsync.sh | head -n -1 > /tmp/bootsync.head
|
||||||
|
sed -n -e '/^\/opt\/bootlocal/,$ p' /opt/bootsync.sh > /tmp/bootsync.tail
|
||||||
|
cat /tmp/bootsync.head > /opt/bootsync.sh
|
||||||
|
cat /tmp/boot_script >> /opt/bootsync.sh; echo >> /opt/bootsync.sh
|
||||||
|
cat /tmp/bootsync.tail >> /opt/bootsync.sh
|
||||||
|
|
||||||
|
# Done
|
43
packer/NETem/scripts/hd-install.sh
Normal file
43
packer/NETem/scripts/hd-install.sh
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Install tinycore on harddisk
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# format harddisk
|
||||||
|
echo -e 'n\np\n1\n\n\na\n1\nw' | sudo fdisk -H16 -S32 /dev/sda
|
||||||
|
sudo mkfs.ext2 /dev/sda1
|
||||||
|
|
||||||
|
# copy system to harddisk
|
||||||
|
sudo mkdir /mnt/sda1
|
||||||
|
sudo mount /dev/sda1 /mnt/sda1
|
||||||
|
sudo mount /mnt/sr0
|
||||||
|
sudo cp -a /mnt/sr0/boot /mnt/sda1/
|
||||||
|
sudo umount /mnt/sr0
|
||||||
|
|
||||||
|
# modify bootloader config
|
||||||
|
sudo mv /mnt/sda1/boot/isolinux /mnt/sda1/boot/extlinux
|
||||||
|
cd /mnt/sda1/boot/extlinux
|
||||||
|
sudo rm boot.cat isolinux.bin
|
||||||
|
sudo mv isolinux.cfg extlinux.conf
|
||||||
|
sudo sed -i -e '/append / s/$/ user=gns3/' -e 's/timeout .*/timeout 1/' extlinux.conf
|
||||||
|
cd
|
||||||
|
|
||||||
|
# make disk bootable
|
||||||
|
tce-load -wi syslinux
|
||||||
|
sudo sh -c 'cat /usr/local/share/syslinux/mbr.bin > /dev/sda'
|
||||||
|
sudo /usr/local/sbin/extlinux --install /mnt/sda1/boot/extlinux
|
||||||
|
|
||||||
|
# create extensions directory
|
||||||
|
sudo mkdir /mnt/sda1/tce
|
||||||
|
sudo chgrp staff /mnt/sda1/tce
|
||||||
|
sudo chmod 775 /mnt/sda1/tce
|
||||||
|
|
||||||
|
# change tcedir to harddisk
|
||||||
|
mv /etc/sysconfig/tcedir /etc/sysconfig/tcedir.bak
|
||||||
|
ln -s /mnt/sda1/tce /etc/sysconfig/tcedir
|
||||||
|
rm -rf /usr/local/tce.installed/*
|
||||||
|
|
||||||
|
# base system modifications
|
||||||
|
sudo sed -i -e '/^\/opt\/bootlocal/ i' /opt/bootsync.sh
|
||||||
|
echo -e "\nusername 'gns3', password 'gns3'\n" >> /etc/issue
|
||||||
|
echo 'etc/issue' >> /opt/.filetool.lst
|
||||||
|
echo 'etc/shadow' >> /opt/.filetool.lst
|
9
packer/NETem/scripts/post_setup.sh
Normal file
9
packer/NETem/scripts/post_setup.sh
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# post-installation script
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# save changes
|
||||||
|
rm -f .ash_history
|
||||||
|
filetool.sh -b sda1
|
||||||
|
|
||||||
|
# write 0, not really necessary
|
||||||
|
#sudo dd if=/dev/zero of=/mnt/sda1/zero ; sudo rm -f /mnt/sda1/zero
|
21
packer/NETem/scripts/serial.sh
Normal file
21
packer/NETem/scripts/serial.sh
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add serial console support
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Boot configuration
|
||||||
|
# Serial interface is secondary console, the vga console remains main console
|
||||||
|
# To change that, swap the two 'console=' boot parameter
|
||||||
|
sudo sed -i -e '1 i serial 0 38400' -e '/label microcore/,/append / s/\(append .*\)/\1 console=ttyS0,38400 console=tty0/' /mnt/sda1/boot/extlinux/extlinux.conf
|
||||||
|
|
||||||
|
# /etc/inittab
|
||||||
|
sudo sed -i -e '/tty6/ a ttyS0::respawn:/sbin/getty 38400 ttyS0 xterm' /etc/inittab
|
||||||
|
|
||||||
|
# /etc/securetty
|
||||||
|
sudo sed -i -e 's/^# *ttyS0/ttyS0/' /etc/securetty
|
||||||
|
|
||||||
|
# reload inittab on startup
|
||||||
|
sudo sed -i -e '/^\/opt\/bootlocal/ i # reload inittab' -e '/^\/opt\/bootlocal/ i kill -HUP 1' -e '/^\/opt\/bootlocal/ i' /opt/bootsync.sh
|
||||||
|
|
||||||
|
# add modified files to backup list
|
||||||
|
echo 'etc/inittab' >> /opt/.filetool.lst
|
||||||
|
echo 'etc/securetty' >> /opt/.filetool.lst
|
26
packer/NETem/uploads/boot_script
Normal file
26
packer/NETem/uploads/boot_script
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
. /etc/init.d/tc-functions
|
||||||
|
|
||||||
|
# default LANG=C.UTF-8
|
||||||
|
[ ! -f /etc/sysconfig/language ] || [ "`cat /etc/sysconfig/language`" = "LANG=C" ] && \
|
||||||
|
echo "LANG=C.UTF-8" > /etc/sysconfig/language
|
||||||
|
|
||||||
|
# Configure network interfaces only when boot parameter "nodhcp" is used
|
||||||
|
if grep -q -w nodhcp /proc/cmdline; then
|
||||||
|
echo -en "${BLUE}Configuring network interfaces... ${NORMAL}"
|
||||||
|
|
||||||
|
# This waits until all devices have registered
|
||||||
|
/sbin/udevadm settle --timeout=10
|
||||||
|
|
||||||
|
ip link add name br0 type bridge
|
||||||
|
ip link set dev eth0 promisc on
|
||||||
|
ip link set dev eth0 mtu 2000
|
||||||
|
ip link set dev eth0 up
|
||||||
|
ip link set dev eth0 master br0
|
||||||
|
ip link set dev eth1 promisc on
|
||||||
|
ip link set dev eth1 mtu 2000
|
||||||
|
ip link set dev eth1 up
|
||||||
|
ip link set dev eth1 master br0
|
||||||
|
ip link set dev br0 up
|
||||||
|
|
||||||
|
echo -e "${GREEN}Done.${NORMAL}"
|
||||||
|
fi
|
397
packer/NETem/uploads/netem-conf.py
Normal file
397
packer/NETem/uploads/netem-conf.py
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# netem-conf - configure NETem parameter
|
||||||
|
#
|
||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
from dialog import Dialog
|
||||||
|
|
||||||
|
# minimal config
|
||||||
|
config = { 'eth0_to_eth1': {}, 'symmetric': True }
|
||||||
|
|
||||||
|
# open dialog system
|
||||||
|
d = Dialog(dialog="dialog", autowidgetsize=True)
|
||||||
|
d.add_persistent_args(["--no-collapse"])
|
||||||
|
|
||||||
|
|
||||||
|
# configure NETem parameter in linux
|
||||||
|
def conf_netem(link, dev):
|
||||||
|
# remove current config
|
||||||
|
subprocess.call(['sudo', '-S', 'tc', 'qdisc', 'del', 'dev', dev, 'root'],
|
||||||
|
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
# base NETem command line
|
||||||
|
netem_cmd = ['sudo', '-S', 'tc', 'qdisc', 'add', 'dev', dev]
|
||||||
|
|
||||||
|
# configure bandwidth with htb
|
||||||
|
if config[link].get('bandwidth') is not None:
|
||||||
|
buffer = max(int(0.3*config[link]['bandwidth']+0.5), 1600)
|
||||||
|
bw_cmd = ['sudo', '-S', 'tc', 'qdisc', 'add', 'dev', dev,
|
||||||
|
'root', 'handle', '1:',
|
||||||
|
'tbf', 'rate', str(config[link]['bandwidth'])+"kbit",
|
||||||
|
'buffer', str(buffer), 'latency', '20ms']
|
||||||
|
proc = subprocess.Popen(bw_cmd, stdin=subprocess.DEVNULL,
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
if err:
|
||||||
|
err = err.decode('ascii').strip()
|
||||||
|
if err == "Password:":
|
||||||
|
err = "sudo needs password"
|
||||||
|
d.msgbox("Can't configure bandwidth !!!\n\n" + \
|
||||||
|
" ".join(bw_cmd) + "\n\n" + str(err))
|
||||||
|
return False
|
||||||
|
netem_cmd += ['parent', '1:1', 'handle', '10']
|
||||||
|
else:
|
||||||
|
netem_cmd += ['root', 'handle', '1']
|
||||||
|
|
||||||
|
netem_cmd.append('netem')
|
||||||
|
|
||||||
|
# add delay to command line
|
||||||
|
if config[link].get('delay') is not None:
|
||||||
|
netem_cmd.append("delay")
|
||||||
|
netem_cmd.append(str(config[link]['delay']) + "ms")
|
||||||
|
if config[link].get('jitter') is not None:
|
||||||
|
netem_cmd.append(str(config[link]['jitter']) + "ms")
|
||||||
|
|
||||||
|
# add loss to command line
|
||||||
|
# see http://netgroup.uniroma2.it/TR/TR-loss-netem.pdf
|
||||||
|
if config[link].get('loss') is not None:
|
||||||
|
if config[link].get('loss_burst') is None:
|
||||||
|
p13 = config[link]['loss']
|
||||||
|
p31 = 100 - config[link]['loss']
|
||||||
|
else:
|
||||||
|
p13 = config[link]['loss'] / \
|
||||||
|
(config[link]['loss_burst'] * (1 - config[link]['loss'] / 100))
|
||||||
|
p31 = 100 / config[link]['loss_burst']
|
||||||
|
netem_cmd.append("loss")
|
||||||
|
netem_cmd.append("gemodel")
|
||||||
|
netem_cmd.append(str(p13) + "%")
|
||||||
|
netem_cmd.append(str(p31) + "%")
|
||||||
|
netem_cmd.append("0")
|
||||||
|
netem_cmd.append("0")
|
||||||
|
|
||||||
|
# configure NETem parameter
|
||||||
|
proc = subprocess.Popen(netem_cmd, stdin=subprocess.DEVNULL,
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
if err:
|
||||||
|
err = err.decode('ascii').strip()
|
||||||
|
if err == "Password:":
|
||||||
|
err = "sudo needs password"
|
||||||
|
d.msgbox("Can't configure NETem !!!\n\n" + \
|
||||||
|
" ".join(netem_cmd) + "\n\n" + str(err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# bandwidth configuration of a link
|
||||||
|
def string_bandwidth(link):
|
||||||
|
if config[link].get('bandwidth') is None:
|
||||||
|
bw_text = "<No Limit>"
|
||||||
|
else:
|
||||||
|
bw_text = str(config[link]['bandwidth']) + " kBit/s"
|
||||||
|
return bw_text
|
||||||
|
|
||||||
|
|
||||||
|
# delay configuration of a link
|
||||||
|
def string_delay(link):
|
||||||
|
if config[link].get('delay') is None:
|
||||||
|
delay_text = "<None>"
|
||||||
|
else:
|
||||||
|
delay_text = str(config[link]['delay']) + " ms"
|
||||||
|
if config[link].get('jitter') is not None:
|
||||||
|
delay_text += ", Jitter: " + str(config[link]['jitter']) + " ms"
|
||||||
|
return delay_text
|
||||||
|
|
||||||
|
|
||||||
|
# loss configuration of a link
|
||||||
|
def string_loss(link):
|
||||||
|
if config[link].get('loss') is None:
|
||||||
|
loss_text = "<None>"
|
||||||
|
else:
|
||||||
|
loss_text = str(config[link]['loss']) + " %"
|
||||||
|
if config[link].get('loss_burst') is not None:
|
||||||
|
loss_text += ", Burst: " + str(config[link]['loss_burst'])
|
||||||
|
return loss_text
|
||||||
|
|
||||||
|
|
||||||
|
# convert string to number
|
||||||
|
def conv_num(string):
|
||||||
|
string = string.strip()
|
||||||
|
if string == "":
|
||||||
|
x = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
x = float(string)
|
||||||
|
if abs(x) < 1e9 and x == int(x):
|
||||||
|
x = int(x)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Invalid number: " + string)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# convert string to postitive number (or zero)
|
||||||
|
def conv_num_positive(string):
|
||||||
|
x = conv_num(string)
|
||||||
|
if x is not None and x < 0:
|
||||||
|
raise ValueError("Negative number: " + string)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# convert string to number greater or equal one
|
||||||
|
def conv_num_ge_one(string):
|
||||||
|
x = conv_num(string)
|
||||||
|
if x is not None and x < 1:
|
||||||
|
raise ValueError("Must be at least 1: " + string)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# convert string to percentage
|
||||||
|
def conv_num_percent(string):
|
||||||
|
x = conv_num(string)
|
||||||
|
if x is not None and (x < 0 or x > 100):
|
||||||
|
raise ValueError("Percentage must be 0..100: " + string)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# link parameter for parsing
|
||||||
|
# ( variable, label, unit, conversion_function, input_width )
|
||||||
|
link_param_bandwidth = [
|
||||||
|
( "bandwidth", "Bandwidth", "kBit/s", conv_num_positive, 10 ) ]
|
||||||
|
link_param_delay = [
|
||||||
|
( "delay", "Delay", "ms", conv_num_positive, 10 ),
|
||||||
|
( "jitter", "Jitter", "ms", conv_num_positive, 10 ) ]
|
||||||
|
link_param_loss = [
|
||||||
|
( "loss", "Packet Loss", "%", conv_num_percent, 10 ),
|
||||||
|
( "loss_burst", "Loss Burst", "Pkts", conv_num_ge_one, 10 ) ]
|
||||||
|
link_param_all = link_param_bandwidth + link_param_delay + link_param_loss
|
||||||
|
|
||||||
|
|
||||||
|
# get link configuration
|
||||||
|
def get_link(link, link_params):
|
||||||
|
global config
|
||||||
|
title = link.replace('_to_', ' -> ')
|
||||||
|
|
||||||
|
# convert link parameter to strings
|
||||||
|
fields = []
|
||||||
|
for param in link_params:
|
||||||
|
val = config[link].get(param[0])
|
||||||
|
if val is None:
|
||||||
|
val = ""
|
||||||
|
else:
|
||||||
|
val = str(val)
|
||||||
|
fields.append(val)
|
||||||
|
|
||||||
|
# get parameter, until no errors left
|
||||||
|
ok = False
|
||||||
|
while not ok:
|
||||||
|
# create elements array for dialog.form
|
||||||
|
elements = []
|
||||||
|
i = 0
|
||||||
|
for param in link_params:
|
||||||
|
label = param[1]
|
||||||
|
if param[2] is not None:
|
||||||
|
label += " [" + param[2] + "]"
|
||||||
|
elements.append((label, i+1, 2, fields[i], i+1, 22, param[4], 0))
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# get parameter
|
||||||
|
code, fields = d.form("Link configuration " + title, elements,
|
||||||
|
title=" "+title+" ")
|
||||||
|
if code != Dialog.OK:
|
||||||
|
break
|
||||||
|
|
||||||
|
# convert string fields to data
|
||||||
|
data = {}
|
||||||
|
ok = True
|
||||||
|
i = 0
|
||||||
|
for param in link_params:
|
||||||
|
try:
|
||||||
|
data[param[0]] = param[3](fields[i])
|
||||||
|
except ValueError as err:
|
||||||
|
ok = False
|
||||||
|
d.msgbox("Input error !!!\n\n" + param[1] + ":\n" + str(err))
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
# additinal checks
|
||||||
|
if ok and data.get('delay') is not None and \
|
||||||
|
data.get('jitter') is not None and \
|
||||||
|
data['jitter'] > data['delay']:
|
||||||
|
ok = False
|
||||||
|
d.msgbox("Input error !!!\n\nJitter must be less than delay.")
|
||||||
|
# all fine, handle some special values and copy data to link config
|
||||||
|
if ok:
|
||||||
|
if data.get('delay') == 0:
|
||||||
|
data['delay'] = None
|
||||||
|
if data.get('delay') is None or data.get('jitter') == 0:
|
||||||
|
data['jitter'] = None
|
||||||
|
if data.get('loss') == 0:
|
||||||
|
data['loss'] = None
|
||||||
|
if data.get('loss') is None or data.get('loss_burst') == 1:
|
||||||
|
data['loss_burst'] = None
|
||||||
|
for param in data:
|
||||||
|
config[link][param] = data[param]
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# menu functions
|
||||||
|
def menu_0to1():
|
||||||
|
get_link('eth0_to_eth1', link_param_all)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_0to1_bandwidth():
|
||||||
|
get_link('eth0_to_eth1', link_param_bandwidth)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_0to1_delay():
|
||||||
|
get_link('eth0_to_eth1', link_param_delay)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_0to1_loss():
|
||||||
|
get_link('eth0_to_eth1', link_param_loss)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_asymmetric():
|
||||||
|
global config
|
||||||
|
code = d.yesno("Do you want to change to symmetric mode?")
|
||||||
|
if code == Dialog.OK:
|
||||||
|
config['symmetric'] = True
|
||||||
|
del config['eth1_to_eth0']
|
||||||
|
|
||||||
|
|
||||||
|
def menu_symmetric():
|
||||||
|
global config
|
||||||
|
code = d.yesno("Do you want to change to asymmetric mode?")
|
||||||
|
if code == Dialog.OK:
|
||||||
|
config['symmetric'] = False
|
||||||
|
config['eth1_to_eth0'] = copy.deepcopy(config['eth0_to_eth1'])
|
||||||
|
|
||||||
|
|
||||||
|
def menu_1to0():
|
||||||
|
if config['symmetric']:
|
||||||
|
menu_symmetric()
|
||||||
|
else:
|
||||||
|
get_link('eth1_to_eth0', link_param_all)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_1to0_bandwidth():
|
||||||
|
get_link('eth1_to_eth0', link_param_bandwidth)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_1to0_delay():
|
||||||
|
get_link('eth1_to_eth0', link_param_delay)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_1to0_loss():
|
||||||
|
get_link('eth1_to_eth0', link_param_loss)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_load():
|
||||||
|
global config
|
||||||
|
title = " Load Configuration "
|
||||||
|
code, path = d.fselect("configs/", 10, 60, title=title)
|
||||||
|
if code == Dialog.OK:
|
||||||
|
try:
|
||||||
|
with open(path, "r") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
except (ValueError, IOError, OSError) as err:
|
||||||
|
d.msgbox("Error !!!\n\n" + str(err), title=title)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_save():
|
||||||
|
title = " Save Configuration "
|
||||||
|
code, path = d.fselect("configs/", 10, 60, title=title)
|
||||||
|
if code == Dialog.OK:
|
||||||
|
try:
|
||||||
|
with open(path, "w") as f:
|
||||||
|
json.dump(config, f, sort_keys=True, indent=4,
|
||||||
|
separators=(',', ': '))
|
||||||
|
f.write("\n")
|
||||||
|
except (ValueError, IOError, OSError) as err:
|
||||||
|
d.msgbox("Error !!!\n\n" + str(err), title=title)
|
||||||
|
|
||||||
|
# backup to persistent disk
|
||||||
|
subprocess.call(['filetool.sh', '-b'],
|
||||||
|
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_shell():
|
||||||
|
d.clear()
|
||||||
|
print('Starting sub-shell, return with "exit"...')
|
||||||
|
subprocess.call('/bin/sh')
|
||||||
|
|
||||||
|
|
||||||
|
def menu_shutdown():
|
||||||
|
d.clear()
|
||||||
|
subprocess.call(['sudo', 'poweroff'])
|
||||||
|
|
||||||
|
|
||||||
|
menu_functions = {
|
||||||
|
'eth0->eth1': menu_0to1,
|
||||||
|
' Bandwidth': menu_0to1_bandwidth,
|
||||||
|
' Delay': menu_0to1_delay,
|
||||||
|
' Loss': menu_0to1_loss,
|
||||||
|
'eth1->eth0': menu_1to0,
|
||||||
|
' Asymmetric': menu_asymmetric,
|
||||||
|
' Symmetric': menu_symmetric,
|
||||||
|
' Bandwidth ': menu_1to0_bandwidth,
|
||||||
|
' Delay ': menu_1to0_delay,
|
||||||
|
' Loss ': menu_1to0_loss,
|
||||||
|
'Load': menu_load,
|
||||||
|
'Save': menu_save,
|
||||||
|
'Shell': menu_shell,
|
||||||
|
'Shutdown': menu_shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Main starts here
|
||||||
|
try:
|
||||||
|
# create config subdirectory
|
||||||
|
os.makedirs("configs", exist_ok=True)
|
||||||
|
# try to load initial configuration
|
||||||
|
try:
|
||||||
|
with open("configs/init", "r") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
except (ValueError, IOError, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# input loop
|
||||||
|
while True:
|
||||||
|
# set parameter in linux
|
||||||
|
if conf_netem('eth0_to_eth1', 'eth1'):
|
||||||
|
if config['symmetric']:
|
||||||
|
conf_netem('eth0_to_eth1', 'eth0')
|
||||||
|
else:
|
||||||
|
conf_netem('eth1_to_eth0', 'eth0')
|
||||||
|
|
||||||
|
# main menue
|
||||||
|
choices = [ ('eth0->eth1', "Configure link eth0 -> eth1"),
|
||||||
|
(' Bandwidth', string_bandwidth('eth0_to_eth1')),
|
||||||
|
(' Delay', string_delay('eth0_to_eth1')),
|
||||||
|
(' Loss', string_loss('eth0_to_eth1')),
|
||||||
|
('eth1->eth0', "Configure link eth1 -> eth0") ]
|
||||||
|
if config['symmetric']:
|
||||||
|
choices += [ (' Symmetric', "Same config as eth0 -> eth1") ]
|
||||||
|
else:
|
||||||
|
choices += [ (' Asymmetric', "Use specific configuration"),
|
||||||
|
(' Bandwidth ', string_bandwidth('eth1_to_eth0')),
|
||||||
|
(' Delay ', string_delay('eth1_to_eth0')),
|
||||||
|
(' Loss ', string_loss('eth1_to_eth0')) ]
|
||||||
|
choices += [ ("Load", "Load configuration from file"),
|
||||||
|
("Save", "Save configuration to file"),
|
||||||
|
("Shell", "Open a console"),
|
||||||
|
("Shutdown", "Shutdown the VM") ]
|
||||||
|
code, tag = d.menu("NETem Configuration", choices=choices,
|
||||||
|
title=" NETem Configuration ", no_cancel=True)
|
||||||
|
if code == Dialog.OK and tag in menu_functions:
|
||||||
|
menu_functions[tag]()
|
||||||
|
|
||||||
|
# intercept Ctrl-C
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
d.clear()
|
||||||
|
exit(0)
|
Loading…
Reference in New Issue
Block a user