import site
import time
from subprocess import Popen
-import os
import re
-import urllib2
-from functools import partial
+import requests
from mininet.net import Containernet
from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController
from mininet.cli import CLI
from mininet.link import TCLink
+from mininet.clean import cleanup
import networkx as nx
from emuvim.dcemulator.monitoring import DCNetworkMonitor
from emuvim.dcemulator.node import Datacenter, EmulatorCompute
:param kwargs: path through for Mininet parameters
:return:
"""
+ # members
self.dcs = {}
+ self.ryu_process = None
- # make sure any remaining Ryu processes are killed
+ # always cleanup environment before we start the emulator
self.killRyu()
- # make sure no containers are left over from a previous emulator run.
- self.removeLeftoverContainers()
+ cleanup()
# call original Docker.__init__ and setup default controller
Containernet.__init__(
self, switch=OVSKernelSwitch, controller=controller, **kwargs)
-
# Ryu management
- self.ryu_process = None
if controller == RemoteController:
# start Ryu controller
self.startRyu(learning_switch=enable_learning)
self.vlans = range(4096)[::-1]
# link to Ryu REST_API
- ryu_ip = '0.0.0.0'
+ ryu_ip = 'localhost'
ryu_port = '8080'
self.ryu_REST_api = 'http://{0}:{1}'.format(ryu_ip, ryu_port)
+ self.RyuSession = requests.Session()
# monitoring agent
if monitor:
Containernet.stop(self)
# stop Ryu controller
- self.stopRyu()
+ self.killRyu()
def CLI(self):
CLI(self)
- # to remove chain do setChain( src, dst, cmd='del-flows')
def setChain(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs):
+ """
+ Chain 2 vnf interfaces together by installing the flowrules in the switches along their path.
+ Currently the path is found using the default networkx shortest path function.
+ Each chain gets a unique vlan id , so different chains wil not interfere.
+
+ :param vnf_src_name: vnf name (string)
+ :param vnf_dst_name: vnf name (string)
+ :param vnf_src_interface: source interface name (string)
+ :param vnf_dst_interface: destination interface name (string)
+ :param cmd: 'add-flow' (default) to add a chain, 'del-flows' to remove a chain
+ :param cookie: cookie for the installed flowrules (can be used later as identifier for a set of installed chains)
+ :param match: custom match entry to be added to the flowrules (default: only in_port and vlan tag)
+ :param priority: custom flowrule priority
+ :return: output log string
+ """
cmd = kwargs.get('cmd')
if cmd == 'add-flow':
ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs)
path = kwargs.get('path')
current_hop = kwargs.get('current_hop')
vlan = kwargs.get('vlan')
+ priority = kwargs.get('priority')
s = ','
if match_input:
if cookie:
flow['cookie'] = int(cookie)
-
+ if priority:
+ flow['priority'] = int(priority)
flow['actions'] = []
self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
time.sleep(1)
- def stopRyu(self):
+ def killRyu(self):
+ """
+ Stop the Ryu controller that might be started by son-emu.
+ :return:
+ """
+ # try it nicely
if self.ryu_process is not None:
self.ryu_process.terminate()
self.ryu_process.kill()
- self.killRyu()
-
- @staticmethod
- def removeLeftoverContainers():
- # TODO can be more python-based using eg. docker-py?
- Popen('docker ps -a -q --filter="name=mn.*" | xargs -r docker rm -f', shell=True)
-
- @staticmethod
- def killRyu():
+ # ensure its death ;-)
Popen(['pkill', '-f', 'ryu-manager'])
def ryu_REST(self, prefix, dpid=None, data=None):
- try:
- if dpid:
- url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
- else:
- url = self.ryu_REST_api + '/' + str(prefix)
- if data:
- #LOG.info('POST: {0}'.format(str(data)))
- req = urllib2.Request(url, str(data))
- else:
- req = urllib2.Request(url)
- ret = urllib2.urlopen(req).read()
+ if dpid:
+ url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
+ else:
+ url = self.ryu_REST_api + '/' + str(prefix)
+ if data:
+ req = self.RyuSession.post(url, json=data)
+ else:
+ req = self.RyuSession.get(url)
+
+
+ # do extra logging if status code is not 200 (OK)
+ if req.status_code is not requests.codes.ok:
+ logging.info(
+ 'type {0} encoding: {1} text: {2} headers: {3} history: {4}'.format(req.headers['content-type'],
+ req.encoding, req.text,
+ req.headers, req.history))
+ LOG.info('url: {0}'.format(str(url)))
+ if data: LOG.info('POST: {0}'.format(str(data)))
+ LOG.info('status: {0} reason: {1}'.format(req.status_code, req.reason))
+
+
+ if 'json' in req.headers['content-type']:
+ ret = req.json()
return ret
- except:
- LOG.info('error url: {0}'.format(str(url)))
- if data: LOG.info('error POST: {0}'.format(str(data)))
+
+ ret = req.text.rstrip()
+ return ret
+
# need to respect that some match fields must be integers
# http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#description-of-match-and-actions