Skip to content
Snippets Groups Projects
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()