Public | Automated Build

Last pushed: 8 months ago
Short Description
Multinet provides a fast, controlled and resource-efficient way to boot large-scale SDN topologies.
Full Description






Multinet

The goal of Multinet is to provide a fast, controlled and resource-efficient way
to boot large-scale SDN topologies. It builds on the Mininet
project to emulate SDN networks via multiple isolated topologies, each launched on
a separate machine, and all connected to the same controller.

Multinet has been verified with the Lithium release of the OpenDaylight controller,
where we managed to boot and connect a topology of 3000+ OVS OF 1.3 switches to a
single controller instance in less than 10 minutes. The controller was running on a
moderate-sized VM (8 VCPUs, 32GB memory) and the multinet topology over 10 small-sized
VMs (1 VCPU, 4GB memory each).

Why isolated topologies?

The main motivation behind Multinet was to be able to stress an SDN controller
in terms of its switch scalable limits. In this context, Multinet contents
itself to booting topologies that are isolated from each other, without really caring
to be interconnected, as we believe this policy is simple and good enough
to approximate the behavior of large-scale realistic SDN networks and their
interaction with the controller. If creating large-scale interconnected
topologies is your primary concern, then you might want to look at other efforts
such as Maxinet
or the Cluster Edition Prototype
of Mininet. Instead, Multinet clearly emphasizes on creating scalable pressure to
the controller and provides options to control certain aspects that affect the
switches-controller interaction, such as the way these are being connected during start-up.

Why multiple VMs?

The cost to boot a large Mininet topology on a single machine grows
exponentially with the number of switches. To amortize this cost, we opted to
scale out and utilize multiple VMs to spawn multiple smaller topologies in parallel.
Eventually, one of the key questions that we try to answer through Multinet is:
what is the best time to boot-up a topology of S Mininet switches with the least
amount of resources
?

Features

  • Large-scale SDN networks emulation, using multiple isolated Mininet
    topologies distributed across multiple VMs
  • Controllable boot-up of switches in groups of configurable size and
    configurable intermediate delay. This enables studying different policies of
    connecting large-scale topologies to the controller.
  • Centralized and RESTful control of topologies via a master-worker architecture
  • Well-known topology types offered out-of-the-box (disconnected, linear,
    ring, mesh)
  • Smooth integration with custom topologies created via the high-level Mininet API,
    provided they have slightly modified their build method

Getting Started

Environment setup

To use Multinet you should have a distributed environment of machines configured
as follows:

  • Software dependencies:
    • Python 2.7
    • bottle, requests and paramiko Python packages
    • a recent version of Mininet (we support 2.2.1rc)
    • Mausezahn, tool for network traffic generation.
  • Connectivity:
    • the machines should be able to communicate with each other
    • the machines should have SSH connectivity

The above software dependencies are installed inside a virtualenv
(isolated Python environment),
which is created from the deploy/provision.sh script which is responsible
for the environment setup. In the next section we demonstrate how to prepare
such an environment using
Vagrant to provision and boot multiple VMs and
docker to provision multiple containers.

Environment setup using Vagrant

You can use Vagrant to setup a testing environment quickly.
Using the provided Vagrantfile you can boot a configurable number of
fully provisioned VMs in a private network and specify their IP scheme.

Under the deploy/vagrant directory we provide scripts and Vagrantfiles to
automatically setup a distributed environment of VMs to run Multinet. The steps
for this are:

  1. Provision the base box from which VMs will be instantiated:

    [user@machine multinet/]$ cd deploy/vagrant/base/
    

    If you sit behind a proxy, edit the http_proxy variable in the
    Vagrantfile. Then start provisioning:

    [user@machine multinet/deploy/vagrant/base]$ vagrant up
    

    When the above command finishes, package the base box that has been created:

    [user@machine multinet/deploy/vagrant/base]$ vagrant package --output mh-provisioned.box
    [user@machine multinet/deploy/vagrant/base]$ vagrant box add mh-provisioned mh-provisioned.box
    [user@machine multinet/deploy/vagrant/base]$ vagrant destroy
    

    For more info on Vagrant box packaging take a look at
    this guide

  2. Configure the VMs:

    [user@machine multinet/]$ cd deploy/vagrant/packaged_multi/
    

    Edit the Vagrantfile according to your preferences. For example:

    http_proxy = ''  # if you sit behind a corporate proxy, provide it here
    mh_vm_basebox = 'mh-provisioned' # the name of the Vagrant box we created in step 2
    mh_vm_ram_mini = '2048'  # RAM size per VM
    mh_vm_cpus_mini = '2'    # number of CPUs per VM
    num_multinet_vms = 10    # total number of VMs to boot
    mh_vm_private_network_ip_mini = '10.1.1.70'  # the first IP Address in the mininet VMs IP Address range
    

    Optional Configuration
    If you need port forwarding from the master guest machine to the
    host machine, edit these variables inside the Vagrantfile:

    forwarded_ports_master = [] # A list of the ports the guest VM needs
                                # to forward
    forwarded_ports_host = []   # The host ports where the guest ports will be
                                # forwarded to (1 - 1 correspondence)
    # Example:
    #   port 3300 from master VM will be forwarded to port 3300 of
    #   the host machine
    #   port 6634 from master VM will be forwarded to port 6635 of
    #   the host machine
    #   forwarded_ports_master = [3300, 6634]
    #   forwarded_ports_host = [3300, 6635]
    
  3. Boot the VMs:

      [user@machine multinet/deploy/vagrant/packaged_multi]$ vagrant up
    

You should now have a number of interconnected VMs with all the dependencies installed.

Environment setup using Docker

In order to create a docker container we must first create an image which will
be used as a base for creating one or more docker containers. For the creation
of a docker image we provide a dockerfile

  1. Install docker: docker installation guides

  2. Creation of docker image (without proxy settings):

    [user@machine multinet/]$ cd deploy/docker/no_proxy/
    [user@machine multinet/deploy/docker/no_proxy/]$ sudo docker build -t multinet_image .
    

    After this step when you run the command

    [user@machine multinet/deploy/docker/no_proxy/]$ sudo docker images
    

    You should see something like the following output

    REPOSITORY          TAG                   IMAGE ID            CREATED             SIZE
    <repo_name>         multinet_image        a75c906f03c7        1 minute ago        1.72 GB
    
  3. Create containers from the created image: Open 2 terminals and execute the
    following command in order to create 2 docker containers

    [user@machine]$ sudo docker run -it <repo_name>:multinet_image /bin/bash
    

    After running the above commands on each terminal you should see the command
    prompt of the container. It should be something like the following

    root@cfb6dccfc41d:/#
    

    Multinet, inside a docker container, is under path /opt/multinet

    root@cfb6dccfc41d:/#cd /opt/multinet
    

The 2 containers are interconnected you can get the ip address information if
you run the command

root@cfb6dccfc41d:/opt/multinet# ifconfig

The default docker network for the containers is 172.17.0.0/16. Use the IP
addresses of docker containers in the configuration file for the
"master_ip": and "worker_ip_list":. See next in the document in the
configuration section of multinet. For more information about docker
container networks visit the link
Understand Docker container networks

Configuration

To start using Multinet, the first step is to clone the Multinet repository on your
local machine. This machine is supposed to act as the client machine to Multinet, and is
denoted in the following examples with the [user@machine] prompt.

Once you have the repository checked out, you may proceed with some configuration.

Deployment configuration

Edit the configuration file to your IP sceme:

  [user@machine multinet/]$ vim config/config.json
  {
    "master_ip" : "10.1.1.80",
    "master_port": 3300,
    "worker_ip_list": ["10.1.1.80", "10.1.1.81"],
    "worker_port": 3333,

    "deploy": {
      "multinet_base_dir": "/home/vagrant/multinet",
      "ssh_port": 22,
      "username": "vagrant",
      "password": "vagrant"
    }
  }
  • master_ip is the IP address of the machine where the master will run
  • master_port is the port where the master listens for REST requests
    from the user or any external client application
  • worker_ip_list is the list with the IPs of all machines where workers
    will be created to launch topologies
  • worker_port is the port where each worker listens for REST requests
    from the master
  • multinet_base_dir is the location where the Multinet repo was cloned on the
    client machine
  • ssh_port is the port where the master and worker machines listen for SSH connections
  • username, password are the credentials used to access via SSH the master and
    worker machines

Topology configuration

Edit the configuration file to the desired topology features:

  {
    "topo": {
      "controller_ip_address":"10.1.1.39",
      "controller_of_port":6653,
      "switch_type":"ovsk",
      "topo_type":"linear",
      "topo_size":30,
      "group_size":3,
      "group_delay":100,
      "hosts_per_switch":2,
      "traffic_generation_duration_ms":60000,
      "interpacket_delay_ms":5000
    }
  }
  • controller_ip_address is the IP address of the machine where the
    SDN controller will run
  • controller_of_port is the port where the controller listens for
    OpenFlow messages
  • switch_type is the type of soft switch used for the emulation
    (supported types: ovsk for OVS OF1.3 switches, user for CPqD OF1.3 switches)
  • topo_type is the type of topologies to be booted on every worker
    node (out of the box supported types: linear, mesh, ring, disconnected)
  • topo_size is the size of topologies to be booted on every worker node
  • group_size, group_delay are the parameters defining the gradual
    bootup groups (see section below)
  • hosts_per_switch is the number of hosts connected to each switch of
    the topology
  • traffic_generation_duration_ms is the amount of time in milliseconds, during
    which PACKET_IN's with ARP payload, will be transmitted.
  • interpacket_delay_ms is connected to the traffic_generation_duration_ms
    and is the interval between consecutive PACKET_IN's, with ARP
    payload, sent from a particular Multinet worker.

Deployment

The goal of this phase is to deploy the Multinet master and worker nodes on a set of
up and running physical or virtual machines that satisfy the conditions mentioned above.
The provided deploy script automates the process of copying the required files on
each machine and starting the master and worker REST servers. The deployment process
assumes the existence of the /opt/venv_multinet directory created by deploy/provsision.sh
desdribed in Environment setup section.

Run the deploy script from the client machine to copy the
necessary files and start the master and the workers:

   [user@machine /opt/multinet/]$./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/deploy /opt/multinet/config/config.json

Initialize Multinet topology

This step initializes the distributed topologies without connecting them to
the controller
. It creates every necessary component of the topology, such
as switches, links, hosts, etc.

Run the following command from the client machine:

   [user@machine /opt/multinet/]$./bin/venv_handler_master.sh /opt/multinet opt/multinet/bin/handlers/init_topos /opt/multinet/config/config.json

The above will send an init command to every worker node concurrently,
and an identical Mininet topology will be booted on every worker machine.
If all topologies are initialized successfully you should get a 200 OK
output message on the client machine. If any topology fails to initialize
successfully, an error message will be printed.

Start Multinet topology

In this step the initialized topologies are being connected to the SDN controller.
Multinet provides control over the way that the topologies are being
connected to the controller, allowing gradual connection in a group-wise
fashion. The rationale behind this is to prevent the controller from being
overwhelmed at cases where a large number of switches are being exposed to it at once.
As a result, the gradual start up enables investigating different policies of
connecting large-scale topologies to the controller.

The group_size and group_delay configuration options control the way that the
distributed topologies are being connected to the controller. The connection
process proceeds in intervals as follows: at every step, group_size
switches from every worker node will be connected to the controller, and
after that, a delay of group_delay milliseconds will follow before
proceeding to the next interval. Note that each worker node will execute the
gradual booting process independent from the others, without employing any
kind of intermediate synchronization.

To connect a Multinet topology after it has been initialized, run the following
command from the client machine:

   [user@machine /opt/multinet/]$./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/start_topos /opt/multinet/config/config.json

The above will send a start command to every worker node in parallel and
initiate the gradual connections of the topologies. In case we want to impose
serialization to this process, we can use the flag --serial-requests. In
this way all the requests from the master to the workers to start their
topology will be send one by one serially.

   [user@machine /opt/multinet/]$./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/start_topos /opt/multinet/config/config.json --serial-requests

If all topologies are connected successfully you should get a 200 OK
output message on the client machine. If any topology fails to connect,
an error message will be printed.

Interact with Multinet topology

Get the number of switches

To query Multinet for the number of booted switches on each worker node, run the
following command from the client machine:

   [user@machine multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/get_switches /opt/multinet/config/config.json

If the distributed topologies have been successfully booted, you should
get a 200 OK message and the number of switches booted on each worker node.

Get the number of installed flows on switches of the topology

To query Multinet for the number of all installed flows on topology switches on
each worker, we can use the following command:

   [user@machine /opt/multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/get_flows /opt/multinet/config/config.json

With this command on each switch we get a dump of its flows and we count them.
For each worker we add the different counts of switches flows and we get the
total installed flows for all the switches on the worker node. We return the
per Multinet worker total installed flows.

Do a pingall operation

To perform a "pingall" operation on every worker node in parallel run the following
command from the client machine:

   [user@machine /opt/multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/pingall /opt/multinet/config/config.json

If the operation runs on a successfully booted topology you should
synchronously get a 200 OK response code and the pingall output should
be logged. Note that a pingall operation may take a long time to complete if the
topology has many hosts.

Trigger host visibility

When connecting a Multinet topology to the OpenDaylight controller,
the hosts are not made automatically visible by the L2 switch plugin,
but rather when they generate traffic.
To trigger host visibility, we have opted to perform a dummy ping from each
host to the controller, which fires a PACKET_IN transmission.

To do this, run the following command from the client machine:

   [user@machine /opt/multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/detect_hosts /opt/multinet/config/config.json

If all the topologies are booted successfully you should synchronously
get a 200 OK response code. Note that a detect_hosts operation may take a long
time to complete if the topology has many hosts.

Stop Multinet topology

To stop a Multinet topology run the following command from the client machine:

   [user@machine /opt/multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/stop_topos /opt/multinet/config/config.json

The above will send a stop command to every worker node in parallel and destroy the
topologies. If all the topologies are destroyed successfully, you should synchronously
get a 200 OK output message.

Clean machines from Multinet installation

A dedicated script exist to revert the Multinet deployment. To clean all the Multinet
machines simply run:

[user@machine /opt/multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/cleanup /opt/multinet/config/config.json

Generate PACKET_IN events with ARP payload

Multinet has the capability to generate traffic from switches to the controller.
This traffic consists of PACKET_IN events, which contain in their Data field,
ARP payload. This operation requires to have defined 2, as a minimum value of
hosts_per_switch, in the topo section of the configuration file.
For more information see in the Configuration section above.

If we asume the case that we use OpenDaylight controller with a proper
configuration, to connect our multinet topology, the above mentioned
PACKET_IN traffic will have as a result the provoke of FLOW_MOD's,
transmitted from the controller to the switches. Controller must have the
following plugins installed:

  • odl-restconf-all
  • odl-openflowplugin-flow-services
  • odl-l2switch-switch

Aditionally we must change the values of the following configuration keys in
the mentioned XML configuration files of the controller:

  • <controller root directory>/etc/opendaylight/karaf/54-arphandler.xml
    • Inside the above mentioned file there is a configuration option, the
      is-proactive-flood-mode. We must set its value to false:
         <is-proactive-flood-mode>false</is-proactive-flood-mode>
      
  • <controller root directory>/etc/opendaylight/karaf/58-l2switchmain.xml
    • inside the above mentioned file we have two configuration options, the
      reactive-flow-idle-timeout and reactive-flow-hard-timeout. We must set
      the minimum value for these configuration keys, which is equal to 1:
         <reactive-flow-idle-timeout>1</reactive-flow-idle-timeout>
         <reactive-flow-hard-timeout>1</reactive-flow-hard-timeout>
      

If the XML configuration files do not exist, we must start and stop the
controller once to provoke the generation of these files.

The operational result of the above configuration is presented in the
following diagram:

In order to use the PACKET_IN generation capability, the following command must
be executed:

[user@machine /opt/multinet/]$ ./bin/venv_handler_master.sh /opt/multinet /opt/multinet/bin/handlers/traffic_gen /opt/multinet/config/config.json

System Architecture

The end goal of Multinet is to deploy a set of Mininet topologies over multiple
machines and have them connected to the same controller simultaneously. To make
this possible, every switch must have a unique DPID to avoid naming collisions
in the controller's housekeeping mechanism, and to achieve this, Multinet
automatically assigns a proper DPID offset to each Mininet topology.

The local Mininet topologies are identical in terms of size, structure and
configuration, and they are all being handled in the same fashion simultaneously.
For example, during start up, topologies are being
created simultaneously on the worker machines and populated in the same way. To
relieve the end user from having to manage each topology separately, we have
adopted a master-worker model for centralized control.

The master process acts as the Multinet front-end that accepts REST requests
from the client machine. At the same time, it orchestrates the pool of workers.
On a user request, the master creates a separate command for each local topology
which it dispatches simultaneously to the workers.
Each worker process controls a local Mininet topology. It accepts commands
from the master via REST, applies them to its topology, and responds back with
a result status. Every worker is unaware of the topologies being operated from
other workers.
The master is responsible to collect partial results and statuses from all workers
and reply to the user as soon as it has a complete global view.

For resource efficiency and speed, it is preferable to create each worker along
with its topology on a separate machine.

Code Design

Code structure

Path Description
.travis.yml Travis CI job
bin/ Binaries
bin/handlers/ Command Line Handlers
bin/cleanuph Cleanup script to reset the Multinet machines environment
bin/deploy Automation script to copy and start the master and the workers in the Multinet machines
config/ Configuration file for the handlers, the deployment and the master
figs/ Figures needed for documentation
multi/ Module containing the Master / Worker REST servers
net/ Module containing the Mininet related functionality
net/multinet.py Class inheriting from the core Mininet with added / modified functionality
net/topologies.py example topologies
test basic functionality tests
travis-jobs Travis CI machine provisioning helper scripts
util/ Utility modules
vagrant/ Vagrantfiles for fast provisioning of a running environment

Interacting with the master programmatically

Via REST

To make easier the communication between a client application and the master node, we augmented it
with a REST API. The client application can issue the POST requests shown below to interact with
Multinet programmatically. In essence, the command line handlers presented in the previous sections are
wrapper scripts to those POST requests.

  • Initialize Multinet topology

    @bottle.route('/init', method='POST')
    

    When making a POST request to the init endpoint, you must also send a JSON
    body with the following format (also see the
    Initialize Multinet topology section)

    {
          "controller_ip_address":"10.1.1.39",
          "controller_of_port":6653,
          "switch_type":"ovsk",
          "topo_type":"linear",
          "topo_size":30,
          "group_size":3,
          "group_delay":100,
          "hosts_per_switch":2,
          "traffic_generation_duration_ms":60000,
          "interpacket_delay_ms":5000
    }
    
  • Start Multinet topology

    @bottle.route('/start', method='POST')
    
  • Get the number of switches

    @bottle.route('/get_switches', method='POST')
    
  • Perform a pingall in each topology

    @bottle.route('/ping_all', method='POST')
    
  • Make the hosts visible

    @bottle.route('/detect_hosts', method='POST')
    
  • Stop the topologies

    @bottle.route('/stop', method='POST')
    
  • Generate PACKET_IN traffic

    @bottle.route('/generate_traffic', method='POST')
    
Via Python

You can also utilize the wrappers from the multinet_requests module for the
same purpose. Example:

  1. For initialization

    # Send a POST request to the master 'init' endpoint
    
    topo_data= {
        "controller_ip_address":"10.1.1.39",
        "controller_of_port":6653,
        "switch_type":"ovsk",
        "topo_type":"linear",
        "topo_size":30,
        "group_size":3,
        "group_delay":100,
        "hosts_per_switch":2,
        "traffic_generation_duration_ms":60000,
        "interpacket_delay_ms":5000
    }
    
    multinet_requests.master_cmd(master_ip,
                                 master_port,
                                 'init',
                                 data=topo_data)
    
  2. And for any other operation

    # Send a POST request to any master endpoint
    # The endpoint is specified by the 'opcode' parameter
    
    opcode = choose_one_of(['start', 'get_switches', 'ping_all', 'detect_hosts', 'stop'])
    multinet_requests.master_cmd(master_ip, master_port, opcode)
    

Core components

  • Multinet class
    • Extends the Mininet class.
    • Adds a dpid offset during the switch creation phase to distinguish between the switches in different instances.
    • Inserts the notion of gradual switch bootup, inserting some idle time
      (group_delay) between the bootup of groups of switches (group_size)
  • worker
    • creates its own Multinet instance
    • creates a REST API that wraps the exposed methods of that instance.
  • master
    • exposes a REST API to the end user.
    • broadcasts the commands to the workers
    • aggregates the responses and returns a summary response to the end user

Adding your own topologies

To be able to plug any topology created with the high level Mininet API to Multinet,
modify the build method to conform with the following method signature:

# k is the number of switches
# n is the number of hosts per switch
# dpid is the dpid offset
def build(self, k=2, n=1, dpid=1, **_opts):
  1. Create a topology with the Mininet high level API, for example

    ## mytopo.py
    class MyTopo(Topo):
        "Disconnected topology of k switches, with n hosts per switch."
    
        def build(self, k=2, n=1, dpid=1, **_opts):
            """
            k: number of switches
            n: number of hosts per switch
            dpid: the dpid offset (to enable distributed topology creation)
            """
            self.k = k
            self.n = n
    
            for i in xrange(k):
                # Add switch
                switch = self.addSwitch(genSwitchName(i, dpid))
                # Add hosts to switch
                for j in xrange(n):
                    host = self.addHost(genHostName(i, j, dpid, n))
                    self.addLink(host, switch)
    
  2. Add the new topology to the Multinet.TOPOS dictionary

    # worker.py
    import mytopo ...
    Multinet.TOPOS['mytopo'] = mytopo.MyTopo
    MININET_TOPO = Multinet( ... )
    
    # or from inside multinet.py
    TOPOS = {
       'linear': ...
       'mytopo': mytopo.MyTopo
    }
    
Docker Pull Command
Owner
intracom
Source Repository

Comments (0)