It's somewhat difficult to use OFTest, in my opinion, because you need
to be root, work with OS network devices, and so on.  Today I played
around with some code to avoid those two needs.

First, I have a patch series starting here:
        http://openvswitch.org/pipermail/dev/2012-October/022300.html
that adds an ability for OVS to use Unix domain sockets as simulated
network devices.

Second, I'm attaching a file meant to be put in platforms/dummy.py in
the OFTest tree to use these simulated network devices.

Finally, I'm attaching a shell script that starts OVS in a sandboxed
mode using these dummy devices.

It passes a few tests; I've only tried a few.

Now that I look at it, there are some bugs here that will prevent
traffic from actually passing through, but they are not fundamental to
the approach.  I'm mostly passing this along in case anyone wants to
comment on the idea; I've already spent more time on it than I should
have.
"""
Dummy platform

This platform uses Open vSwitch dummy interfaces.
"""

import logging
import os
import select
import socket
import struct
import sys
import time
from threading import Thread
from threading import Lock

RCV_TIMEOUT = 10000
SOCKET_DIR = os.environ.get("OVS_RUNDIR", """/var/run""") + "/openvswitch"

class DataPlanePortUnix:
    """
    Class defining a port monitoring object that uses Unix domain
    sockets for ports, intended for connecting to Open vSwitch "dummy"
    netdevs.
    """

    def __init__(self, interface_name, port_number, parent, max_pkts=1024):
        """
        Set up a port monitor object
        @param interface_name The name of the physical interface like eth1
        @param port_number The port number associated with this port
        @param parent The controlling dataplane object; for pkt wait CV
        @param max_pkts Maximum number of pkts to keep in queue
        """
        Thread.__init__(self)
        self.interface_name = interface_name
        self.max_pkts = max_pkts
        self.packets_total = 0
        self.packets = []
        self.packets_discarded = 0
        self.port_number = port_number
        self.txq = []
        self.txq_lock = Lock()
        logname = "dp-" + interface_name
        self.logger = logging.getLogger(logname)
        try:
            self.socket = DataPlanePortUnix.interface_open(interface_name)
        except:
            self.logger.info("Could not open socket")
            raise
        self.logger.info("Opened port monitor (class %s)", type(self).__name__)
        self.parent = parent

    @staticmethod
    def interface_open(interface_name):
        """
        Open a Unix domain socket interface.
        @param interface_name port name as a string such as 'eth1'
        @retval s socket
        """
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.settimeout(RCV_TIMEOUT)
        s.setblocking(0)
        s.connect("%s/%s" % (SOCKET_DIR, interface_name))
        return s

    def run(self):
        """
        Activity function for class
        """
        self.running = True
        rxbuf = ""
        while self.running:
            try:
                self.txq_lock.acquire()
                if self.txq:
                    wlist = [self.socket]
                else:
                    wlist = []
                self.txq_lock.release()

                rout, wout, eout = select.select([self.socket], wlist, [], 1)
            except:
                print sys.exc_info()
                self.logger.error("Select error, exiting")
                break

            if not self.running:
                break

            if wout:
                self.txq_lock.acquire()
                if self.txq:
                    retval = self.socket.send(self.txq[0])
                    if retval > 0:
                        self.txq[0] = self.txq[0][retval:]
                        if len(self.txq[0]) == 0:
                            self.txq = self.txq[1:]
                self.txq_lock.release()

            if rout:
                if len(rxbuf) < 2:
                    n = 2 - len(rxbuf)
                else:
                    frame_len = struct.unpack('>h', rxbuf[:2])[0]
                    n = (2 + frame_len) - len(rxbuf)

                data = self.socket.recv(n)
                rxbuf += data
                if len(data) == n and len(rxbuf) > 2:
                    rcvtime = time.clock()
                    self.logger.debug("Pkt len " + str(len(rxbuf)) +
                             " in at " + str(rcvtime) + " on port " +
                             str(self.port_number))

                    # Enqueue packet
                    with self.parent.pkt_sync:
                        if len(self.packets) >= self.max_pkts:
                            # Queue full, throw away oldest
                            self.packets.pop(0)
                            self.packets_discarded += 1
                            self.logger.debug("Discarding oldest packet to make room")
                        self.packets.append((rxbuf, rcvtime))
                        self.packets_total += 1
                        self.parent.pkt_sync.notify_all()

                    rxbuf = ''

        self.logger.info("Thread exit")

    def kill(self):
        """
        Terminate the running thread
        """
        self.logger.debug("Port monitor kill")
        self.running = False
        try:
            self.socket.close()
        except:
            self.logger.info("Ignoring dataplane soc shutdown error")

    def timestamp_head(self):
        """
        Return the timestamp of the head of queue or None if empty
        """
        rv = None
        try:
            rv = self.packets[0][1]
        except:
            rv = None
        return rv

    def flush(self):
        """
        Clear the packet queue
        """
        with self.parent.pkt_sync:
            self.packets_discarded += len(self.packets)
            self.packets = []

    def send(self, packet):
        """
        Send a packet to the dataplane port
        @param packet The packet data to send to the port
        @retval The number of bytes sent
        """
        self.txq_lock.acquire()
        if len(self.txq) < self.max_pkts:
            self.txq.append(struct.pack('>h', len(packet)) + packet)
        self.txq_lock.release()

    def register(self, handler):
        """
        Register a callback function to receive packets from this
        port.  The callback will be passed the packet, the
        interface name and the port number (if set) on which the
        packet was received.

        To be implemented
        """
        pass

    def show(self, prefix=''):
        print prefix + "Name:          " + self.interface_name
        print prefix + "Pkts pending:  " + str(len(self.packets))
        print prefix + "Pkts total:    " + str(self.packets_total)
        print prefix + "socket:        " + str(self.socket)

# Update this dictionary to suit your environment.
dummy_port_map = {
    1 : "p1",
    2 : "p2",
    3 : "p3",
    4 : "p4"
}

def platform_config_update(config):
    """
    Update configuration for the dummy platform

    @param config The configuration dictionary to use/update
    """
    global dummy_port_map
    config["port_map"] = dummy_port_map.copy()
    config["caps_table_idx"] = 0
    config["dataplane"] = {"portclass": DataPlanePortUnix}
    config["allow_user"] = True
#! /bin/sh

set -ex

srcdir=$HOME/ovs
builddir=$srcdir/_build
PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$PATH

cd $builddir
rm -rf sandbox
mkdir sandbox
cd sandbox

OVS_RUNDIR=`pwd`; export OVS_RUNDIR
OVS_LOGDIR=`pwd`; export OVS_LOGDIR
OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR

trap 'kill `cat *.pid`' 0

touch .conf.db.~lock~
rm -f conf.db
ovsdb-tool create conf.db $srcdir/vswitchd/vswitch.ovsschema

ovsdb-server --detach --no-chdir --pidfile --log-file 
--remote=punix:$OVS_RUNDIR/db.sock
ovs-vsctl --no-wait init

ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy --disable-system 
-vvconn -vofproto_dpif

ovs-vsctl \
    -- add-br br0 \
    -- set bridge br0 datapath-type=dummy fail-mode=secure \
    -- add-port br0 p1 -- set interface p1 type=dummy \
    -- add-port br0 p2 -- set interface p2 type=dummy \
    -- add-port br0 p3 -- set interface p3 type=dummy \
    -- add-port br0 p4 -- set interface p4 type=dummy \
    -- set-controller br0 tcp:127.0.0.1

read line
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to