-
Michael Marsh authoredMichael Marsh authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
start_testbed.py 3.82 KiB
#! /usr/bin/env python
import os
import subprocess
import yaml
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument(
'-f', '--file',
dest='file',
default='example.yml',
help='YML file containing the testbed configuration'
)
args = parser.parse_args()
with open(args.file) as cfile:
conf = yaml.load(cfile)
ntwk = conf['network']
base_addr = reduce(
lambda a,b : a*256+b,
[ int(q) for q in ntwk['subnet_addr'].split('.') ]
)
subnet = ntwk['subnet_addr']+'/'+str(ntwk['subnet_len'])
nodelist = list()
retval = subprocess.call(['docker','pull',conf['image']])
def make_ip(i):
addr = base_addr + i
masks = [ 24, 16, 8, 0 ]
return '.'.join([ str((addr>>mask)&0xFF) for mask in masks ])
def make_route(f,net,fdev):
subprocess.call([
'sudo', 'ip', 'netns', 'exec', f['name'],
'ip', 'route', 'add', net,
'dev', fdev, 'proto', 'static', 'scope', 'global',
'src', f['addr']
])
def connect_nodes(node1, node2):
ctr1 = nodelist[node1]
ctr2 = nodelist[node2]
print ctr1, ctr2
# Create a virtual ethernet link.
dev_1 = '{name}_{n1}_{n2}'.format(name=ntwk['name'],n1=node1,n2=node2)
dev_2 = '{name}_{n2}_{n1}'.format(name=ntwk['name'],n1=node1,n2=node2)
subprocess.call([
'sudo',
'ip', 'link', 'add', dev_1, 'type', 'veth',
'peer', 'name', dev_2
])
# Add the link endpoints to the container namespaces.
def add_to_ns(dev,ns):
subprocess.call([
'sudo', 'ip', 'link', 'set', dev, 'netns', ns
])
add_to_ns(dev_1,ctr1['name'])
add_to_ns(dev_2,ctr2['name'])
# Set the IP addresses.
def set_addr(dev,ns,addr):
subprocess.call([
'sudo', 'ip', 'netns', 'exec', ns,
'ip', 'addr', 'add', addr+'/32', 'dev', dev
])
set_addr(dev_1,ctr1['name'],ctr1['addr'])
set_addr(dev_2,ctr2['name'],ctr2['addr'])
# Bring the link up.
def link_up(ns,dev):
subprocess.call([
'sudo', 'ip', 'netns', 'exec', ns,
'ip', 'link', 'set', dev, 'up'
])
link_up(ctr1['name'],dev_1)
link_up(ctr2['name'],dev_2)
# Set initial routes.
def route_from_to(f,t,fdev):
make_route(f,t['addr']+'/32',fdev)
route_from_to(ctr1,ctr2,dev_1)
route_from_to(ctr2,ctr1,dev_2)
def make_mesh():
global nodelist
num_nodes = len(nodelist)
[ connect_nodes(n1,n2)
for n1 in range(num_nodes-1)
for n2 in range(n1+1,num_nodes) ]
def make_star():
global nodelist
def get_dev(i):
return 'bac0_{i}_0'.format(i=i)
num_nodes = len(nodelist)
[ connect_nodes(0,n) for n in range(1,num_nodes) ]
[ make_route(n,subnet,get_dev(n)) for n in range(1,num_nodes) ]
def start_node(base_name,i):
global nodelist
name = base_name + str(i)
# Start the container.
command = ['docker',
'run',
'-d',
'--name',
name,
conf['image']
]
command.extend(conf['image_cmd'].split())
retval = subprocess.call(command)
entry = dict()
entry['name'] = name
# Determine the IP address to use for this node.
entry['addr'] = make_ip(i)
# Find the container's process ID.
entry['pid'] = subprocess.check_output([
'docker',
'inspect',
'-f',
'{{.State.Pid}}',
name
]).strip()
# Set up a network namespace.
subprocess.call([
'sudo',
'ln',
'-s',
'/proc/{pid}/ns/net'.format(pid=entry['pid']),
'/var/run/netns/{name}'.format(name=name)
])
# Add the node's info to our list.
nodelist.append(entry)
for n in range(ntwk['num_nodes']):
start_node(ntwk['name'],n)
if ntwk['topology'] == 'mesh':
make_mesh()
else:
make_star()