add monitoring optimizations
diff --git a/src/emuvim/api/rest/network.py b/src/emuvim/api/rest/network.py
index 83fbde7..63696a0 100755
--- a/src/emuvim/api/rest/network.py
+++ b/src/emuvim/api/rest/network.py
@@ -87,6 +87,10 @@
bidirectional = data.get("bidirectional")
cookie = data.get("cookie")
priority = data.get("priority")
+ skip_vlan_tag = data.get("skip_vlan_tag")
+ monitor = data.get("monitor")
+ monitor_placement = data.get("monitor_placement")
+
c = net.setChain(
vnf_src_name, vnf_dst_name,
vnf_src_interface=vnf_src_interface,
@@ -96,7 +100,10 @@
match=match,
bidirectional=bidirectional,
cookie=cookie,
- priority=priority)
+ priority=priority,
+ skip_vlan_tag=skip_vlan_tag,
+ monitor=monitor,
+ monitor_placement=monitor_placement)
# return setChain response
return str(c), 200
except Exception as ex:
diff --git a/src/emuvim/api/sonata/dummygatekeeper.py b/src/emuvim/api/sonata/dummygatekeeper.py
index 00201f9..735860c 100755
--- a/src/emuvim/api/sonata/dummygatekeeper.py
+++ b/src/emuvim/api/sonata/dummygatekeeper.py
@@ -75,6 +75,7 @@
def __init__(self):
self.services = dict()
self.dcs = dict()
+ self.net = None
self.vnf_counter = 0 # used to generate short names for VNFs (Mininet limitation)
LOG.info("Create SONATA dummy gatekeeper.")
@@ -180,6 +181,9 @@
eline_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-Line")]
elan_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-LAN")]
+ GK.net.deployed_elines.extend(eline_fwd_links)
+ GK.net.deployed_elans.extend(elan_fwd_links)
+
# 4a. deploy E-Line links
# cookie is used as identifier for the flowrules installed by the dummygatekeeper
# eg. different services get a unique cookie for their flowrules
@@ -228,7 +232,7 @@
base = 10
for link in elan_fwd_links:
- elan=[]
+ elan_vnf_list=[]
# generate lan ip address
ip = 1
@@ -257,12 +261,11 @@
# add this vnf and interface to the E-LAN for tagging
network = self.vnfds[vnf_name].get("dc").net # there should be a cleaner way to find the DCNetwork
- elan.append({'name':src_docker_name,'interface':intf_name})
+ elan_vnf_list.append({'name':src_docker_name,'interface':intf_name})
# install the VLAN tags for this E-LAN
- print(elan)
- network.setLAN(elan)
+ network.setLAN(elan_vnf_list)
# increase the base ip address for the next E-LAN
base += 1
@@ -385,6 +388,7 @@
self.package_content_path,
make_relative_path(self.manifest.get("entry_service_template")))
self.nsd = load_yaml(nsd_path)
+ GK.net.deployed_nsds.append(self.nsd)
LOG.debug("Loaded NSD: %r" % self.nsd.get("name"))
def _load_vnfd(self):
@@ -630,6 +634,7 @@
def start_rest_api(host, port, datacenters=dict()):
GK.dcs = datacenters
+ GK.net = get_dc_network()
# start the Flask server (not the best performance but ok for our use case)
app.run(host=host,
port=port,
@@ -678,6 +683,14 @@
r.append("%d.0.0.%d/%d" % (i, ip, subnet_size))
return r
+def get_dc_network():
+ """
+ retrieve the DCnetwork where this dummygatekeeper (GK) connects to.
+ Assume at least 1 datacenter is connected to this GK, and that all datacenters belong to the same DCNetwork
+ :return:
+ """
+ assert (len(GK.dcs) > 0)
+ return GK.dcs.values()[0].net
if __name__ == '__main__':
"""
diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py
index 3d9dcb0..ec0aee5 100755
--- a/src/emuvim/dcemulator/net.py
+++ b/src/emuvim/dcemulator/net.py
@@ -69,6 +69,12 @@
# members
self.dcs = {}
self.ryu_process = None
+ #list of deployed nsds.E_Lines and E_LANs (uploaded from the dummy gatekeeper)
+ self.deployed_nsds = []
+ self.deployed_elines = []
+ self.deployed_elans = []
+ self.installed_chains = []
+
# always cleanup environment before we start the emulator
self.killRyu()
@@ -330,8 +336,139 @@
switch_node = self.getNodeByName(src_sw)
self._set_vlan_tag(switch_node, src_sw_inport_name, vlan)
+ def _addMonitorFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None,
+ tag=None, **kwargs):
+
+ src_sw = None
+ src_sw_inport_nr = 0
+ src_sw_inport_name = None
+ dst_sw = None
+ dst_sw_outport_nr = 0
+ dst_sw_outport_name = None
+
+ LOG.debug("call AddMonitorFlow vnf_src_name=%r, vnf_src_interface=%r, vnf_dst_name=%r, vnf_dst_interface=%r",
+ vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface)
+
+ #check if port is specified (vnf:port)
+ if vnf_src_interface is None:
+ # take first interface by default
+ connected_sw = self.DCNetwork_graph.neighbors(vnf_src_name)[0]
+ link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
+ vnf_src_interface = link_dict[0]['src_port_id']
+
+ for connected_sw in self.DCNetwork_graph.neighbors(vnf_src_name):
+ link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
+ for link in link_dict:
+ if (link_dict[link]['src_port_id'] == vnf_src_interface or
+ link_dict[link]['src_port_name'] == vnf_src_interface): # Fix: we might also get interface names, e.g, from a son-emu-cli call
+ # found the right link and connected switch
+ src_sw = connected_sw
+ src_sw_inport_nr = link_dict[link]['dst_port_nr']
+ src_sw_inport_name = link_dict[link]['dst_port_name']
+ break
+
+ if vnf_dst_interface is None:
+ # take first interface by default
+ connected_sw = self.DCNetwork_graph.neighbors(vnf_dst_name)[0]
+ link_dict = self.DCNetwork_graph[connected_sw][vnf_dst_name]
+ vnf_dst_interface = link_dict[0]['dst_port_id']
+
+ vnf_dst_name = vnf_dst_name.split(':')[0]
+ for connected_sw in self.DCNetwork_graph.neighbors(vnf_dst_name):
+ link_dict = self.DCNetwork_graph[connected_sw][vnf_dst_name]
+ for link in link_dict:
+ if link_dict[link]['dst_port_id'] == vnf_dst_interface or \
+ link_dict[link]['dst_port_name'] == vnf_dst_interface: # Fix: we might also get interface names, e.g, from a son-emu-cli call
+ # found the right link and connected switch
+ dst_sw = connected_sw
+ dst_sw_outport_nr = link_dict[link]['src_port_nr']
+ dst_sw_outport_name = link_dict[link]['src_port_name']
+ break
+
+ if not tag >= 0:
+ LOG.exception('tag not valid: {0}'.format(tag))
+
+ # get shortest path
+ try:
+ # returns the first found shortest path
+ # if all shortest paths are wanted, use: all_shortest_paths
+ path = nx.shortest_path(self.DCNetwork_graph, src_sw, dst_sw, weight=kwargs.get('weight'))
+ except:
+ LOG.exception("No path could be found between {0} and {1} using src_sw={2} and dst_sw={3}".format(
+ vnf_src_name, vnf_dst_name, src_sw, dst_sw))
+ LOG.debug("Graph nodes: %r" % self.DCNetwork_graph.nodes())
+ LOG.debug("Graph edges: %r" % self.DCNetwork_graph.edges())
+ for e, v in self.DCNetwork_graph.edges():
+ LOG.debug("%r" % self.DCNetwork_graph[e][v])
+ return "No path could be found between {0} and {1}".format(vnf_src_name, vnf_dst_name)
+
+ LOG.info("Path between {0} and {1}: {2}".format(vnf_src_name, vnf_dst_name, path))
+
+ current_hop = src_sw
+ switch_inport_nr = src_sw_inport_nr
+
+ cmd = kwargs.get('cmd')
+
+ #iterate through the path to install the flow-entries
+ for i in range(0,len(path)):
+ current_node = self.getNodeByName(current_hop)
+
+ if path.index(current_hop) < len(path)-1:
+ next_hop = path[path.index(current_hop)+1]
+ else:
+ #last switch reached
+ next_hop = vnf_dst_name
+
+ next_node = self.getNodeByName(next_hop)
+
+ if next_hop == vnf_dst_name:
+ switch_outport_nr = dst_sw_outport_nr
+ LOG.info("end node reached: {0}".format(vnf_dst_name))
+ elif not isinstance( next_node, OVSSwitch ):
+ LOG.info("Next node: {0} is not a switch".format(next_hop))
+ return "Next node: {0} is not a switch".format(next_hop)
+ else:
+ # take first link between switches by default
+ index_edge_out = 0
+ switch_outport_nr = self.DCNetwork_graph[current_hop][next_hop][index_edge_out]['src_port_nr']
+ # set of entry via ovs-ofctl
+ if isinstance( current_node, OVSSwitch ):
+ kwargs['vlan'] = tag
+ kwargs['path'] = path
+ kwargs['current_hop'] = current_hop
+ kwargs['switch_inport_name'] = src_sw_inport_name
+ kwargs['switch_outport_name'] = dst_sw_outport_name
+ kwargs['skip_vlan_tag'] = True
+
+ monitor_placement = kwargs.get('monitor_placement')
+ # put monitor flow at the dst switch
+ insert_flow = False
+ if monitor_placement == 'tx' and path.index(current_hop) == 0: # first node:
+ insert_flow = True
+ # put monitoring flow at the src switch
+ elif monitor_placement == 'rx' and path.index(current_hop) == len(path) - 1: # last node:
+ insert_flow = True
+ else:
+ LOG.exception('invalid monitor command: {0}'.format(monitor_placement))
+
+
+ if self.controller == RemoteController and insert_flow:
+ ## set flow entry via ryu rest api
+ self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
+ break
+ elif insert_flow:
+ ## set flow entry via ovs-ofctl
+ self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
+ break
+
+ # take first link between switches by default
+ if isinstance( next_node, OVSSwitch ):
+ switch_inport_nr = self.DCNetwork_graph[current_hop][next_hop][0]['dst_port_nr']
+ current_hop = next_hop
+
+ return "path {2} between {0} and {1}".format(vnf_src_name, vnf_dst_name, cmd)
def setChain(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs):
@@ -350,6 +487,31 @@
:param priority: custom flowrule priority
:return: output log string
"""
+
+ # special procedure for monitoring flows
+ if kwargs.get('monitor'):
+
+ # check if chain already exists
+ found_chains = [chain_dict for chain_dict in self.installed_chains if
+ (chain_dict['vnf_src_name'] == vnf_src_name and chain_dict['vnf_src_interface'] == vnf_src_interface
+ and chain_dict['vnf_dst_name'] == vnf_dst_name and chain_dict['vnf_dst_interface'] == vnf_dst_interface)]
+
+ if len(found_chains) > 0:
+ # this chain exists, so need an extra monitoring flow
+ # assume only 1 chain per vnf/interface pair
+ LOG.debug('*** installing monitoring chain on top of pre-defined chain from {0}:{1} -> {2}:{3}'.
+ format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface))
+ tag = found_chains[0]['tag']
+ ret = self._addMonitorFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface,
+ tag=tag, table_id=0, **kwargs)
+ return ret
+ else:
+ # no chain existing (or E-LAN) -> install normal chain
+ LOG.warning('*** installing monitoring chain without pre-defined chain from {0}:{1} -> {2}:{3}'.
+ format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface))
+ pass
+
+
cmd = kwargs.get('cmd')
if cmd == 'add-flow' or cmd == 'del-flows':
ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs)
@@ -435,10 +597,23 @@
cmd = kwargs.get('cmd')
vlan = None
if cmd == 'add-flow':
- vlan = self.vlans.pop()
- ##if len(path) > 1:
- ## vlan = self.vlans.pop()
+ if kwargs.get('tag'):
+ # use pre-defined tag
+ vlan = kwargs.get('tag')
+ else:
+ vlan = self.vlans.pop()
+ # store the used vlan tag to identify this chain
+ if not kwargs.get('monitor'):
+ chain_dict = {}
+ chain_dict['vnf_src_name'] = vnf_src_name
+ chain_dict['vnf_dst_name'] = vnf_dst_name
+ chain_dict['vnf_src_interface'] = vnf_src_interface
+ chain_dict['vnf_dst_interface'] = vnf_dst_interface
+ chain_dict['tag'] = vlan
+ self.installed_chains.append(chain_dict)
+
+ #iterate through the path to install the flow-entries
for i in range(0,len(path)):
current_node = self.getNodeByName(current_hop)
@@ -477,8 +652,6 @@
## set flow entry via ovs-ofctl
self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
-
-
# take first link between switches by default
if isinstance( next_node, OVSSwitch ):
switch_inport_nr = self.DCNetwork_graph[current_hop][next_hop][0]['dst_port_nr']
@@ -496,6 +669,12 @@
current_hop = kwargs.get('current_hop')
vlan = kwargs.get('vlan')
priority = kwargs.get('priority')
+ # flag to not set the ovs port vlan tag
+ skip_vlan_tag = kwargs.get('skip_vlan_tag')
+ # table id to put this flowentry
+ table_id = kwargs.get('table_id')
+ if not table_id:
+ table_id = 0
s = ','
if match_input:
@@ -509,6 +688,8 @@
if priority:
flow['priority'] = int(priority)
+ flow['table_id'] = table_id
+
flow['actions'] = []
# possible Ryu actions, match fields:
@@ -518,8 +699,9 @@
if vlan != None:
if path.index(current_hop) == 0: # first node
# set vlan tag in ovs instance (to isolate E-LANs)
- in_port_name = kwargs.get('switch_inport_name')
- self._set_vlan_tag(node, in_port_name, vlan)
+ if not skip_vlan_tag:
+ in_port_name = kwargs.get('switch_inport_name')
+ self._set_vlan_tag(node, in_port_name, vlan)
# set vlan push action if more than 1 switch in the path
if len(path) > 1:
action = {}
@@ -534,8 +716,9 @@
if path.index(current_hop) == len(path) - 1: # last node
# set vlan tag in ovs instance (to isolate E-LANs)
- out_port_name = kwargs.get('switch_outport_name')
- self._set_vlan_tag(node, out_port_name, vlan)
+ if not skip_vlan_tag:
+ out_port_name = kwargs.get('switch_outport_name')
+ self._set_vlan_tag(node, out_port_name, vlan)
# set vlan pop action if more than 1 switch in the path
if len(path) > 1:
match += ',dl_vlan=%s' % vlan
diff --git a/src/emuvim/dcemulator/node.py b/src/emuvim/dcemulator/node.py
index b88913b..ae658f0 100755
--- a/src/emuvim/dcemulator/node.py
+++ b/src/emuvim/dcemulator/node.py
@@ -61,6 +61,7 @@
Helper method to receive information about the virtual networks
this compute instance is connected to.
"""
+
# format list of tuples (name, Ip, MAC, isUp, status)
return [{'intf_name':str(i), 'ip':i.IP(), 'mac':i.MAC(), 'up':i.isUp(), 'status':i.status()}
for i in self.intfList()]
diff --git a/src/emuvim/dcemulator/son_emu_simple_switch_13.py b/src/emuvim/dcemulator/son_emu_simple_switch_13.py
index ce2f544..53d1a2e 100755
--- a/src/emuvim/dcemulator/son_emu_simple_switch_13.py
+++ b/src/emuvim/dcemulator/son_emu_simple_switch_13.py
@@ -44,11 +44,12 @@
# truncated packet data. In that case, we cannot output packets
# correctly. The bug has been fixed in OVS v2.1.0.
match = parser.OFPMatch()
- actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
- ofproto.OFPCML_NO_BUFFER)]
+ #actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
+ # ofproto.OFPCML_NO_BUFFER)]
+ actions = [parser.OFPActionOutput(ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
- def add_flow(self, datapath, priority, match, actions, buffer_id=None):
+ def add_flow(self, datapath, priority, match, actions, buffer_id=None, table_id=0):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
@@ -57,10 +58,10 @@
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
priority=priority, match=match,
- instructions=inst)
+ instructions=inst, table_id=table_id)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
- match=match, instructions=inst)
+ match=match, instructions=inst, table_id=table_id)
datapath.send_msg(mod)
# new switch detected
@@ -74,7 +75,7 @@
# send NORMAL action for all undefined flows
ofp_parser = datapath.ofproto_parser
actions = [ofp_parser.OFPActionOutput(ofproto_v1_3.OFPP_NORMAL)]
- self.add_flow(datapath, 0, None, actions)
+ self.add_flow(datapath, 0, None, actions, table_id=0)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
diff --git a/src/emuvim/examples/sonata_y1_demo_topology_1_w_ls_and_sap.py b/src/emuvim/examples/sonata_y1_demo_topology_1_w_ls_and_sap.py
old mode 100644
new mode 100755