move NETem packer files to tinycore-linux

This commit is contained in:
Bernhard Ehlers
2016-01-21 11:05:02 +01:00
parent a24cfae748
commit 30a7e86bd7
11 changed files with 59 additions and 183 deletions

View File

@ -0,0 +1,5 @@
{
"vm_name": "NETem.qcow2",
"setup_script": "NETem.sh",
"disk_size": "32"
}

View File

@ -47,6 +47,16 @@ An appliance for simulating a domestic modem. eth0 as a dhcp and eth1 should be
packer build -var-file=core-linux-internet.json core-linux.json
NETem
''''''
A build of Core with the NETem (Network link emulator) preinstalled.
.. code:: bash
packer build -var-file=NETem.json core-linux.json
Tiny Core Linux installation
****************************

View File

@ -25,7 +25,7 @@
"http_directory": "http",
"boot_wait": "5s",
"boot_command": [
"mc user=gns3<enter><wait10><wait10><wait10><wait10><wait10><wait10><wait10>",
"mc user=gns3 http={{ .HTTPIP }}:{{ .HTTPPort }}<enter><wait10><wait10><wait10><wait10><wait10><wait10><wait10>",
"sudo udhcpc<enter>",
"sudo passwd gns3<enter>gns3<enter>gns3<enter>",
"tce-load -wi openssh<enter><wait10>",

View 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)

View File

@ -0,0 +1,132 @@
set -e
set -x
# 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
# clean up build environment
cd ..
rm -r build
# NETem menu system
. /etc/init.d/tc-functions
http=http://`getbootparam http`
wget $http/NETem/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 -i -e '3,$ d' /opt/bootlocal.sh
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 >> /opt/bootsync.sh <<'EOF'
. /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
sysctl -q -w net.ipv6.conf.br0.disable_ipv6=1
sysctl -q -w net.ipv6.conf.eth0.disable_ipv6=1
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
sysctl -q -w net.ipv6.conf.eth1.disable_ipv6=1
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
EOF
cat /tmp/bootsync.tail >> /opt/bootsync.sh
# Done