Merge branch 'master' of https://github.com/stevenvanrossem/son-emu
[osm/vim-emu.git] / emuvim / dcemulator / net.py
1 """
2 Distributed Cloud Emulator (dcemulator)
3 (c) 2015 by Manuel Peuster <manuel.peuster@upb.de>
4 """
5 import logging
6
7 from mininet.net import Dockernet
8 from mininet.node import Controller, OVSSwitch, OVSKernelSwitch, Switch, Docker, Host, RemoteController
9 from mininet.cli import CLI
10 from mininet.log import setLogLevel, info, debug
11 from mininet.link import TCLink, Link
12 import networkx as nx
13 from monitoring import DCNetworkMonitor
14
15 from node import Datacenter, EmulatorCompute
16
17
18 class DCNetwork(Dockernet):
19 """
20 Wraps the original Mininet/Dockernet class and provides
21 methods to add data centers, switches, etc.
22
23 This class is used by topology definition scripts.
24 """
25
26 def __init__(self, **kwargs):
27 self.dcs = {}
28 # create a Mininet/Dockernet network
29 # call original Docker.__init__ and setup default controller
30 #Dockernet.__init__(
31 # self, controller=RemoteController, switch=OVSKernelSwitch, **kwargs)
32 Dockernet.__init__(
33 self, controller=RemoteController, switch=OVSKernelSwitch, **kwargs)
34 self.addController('c0', controller=RemoteController)
35
36 # graph of the complete DC network
37 self.DCNetwork_graph=nx.DiGraph()
38
39 # monitoring agent
40 self.monitor_agent = DCNetworkMonitor(self)
41
42
43 def addDatacenter(self, label, metadata={}):
44 """
45 Create and add a logical cloud data center to the network.
46 """
47 if label in self.dcs:
48 raise Exception("Data center label already exists: %s" % label)
49 dc = Datacenter(label, metadata=metadata)
50 dc.net = self # set reference to network
51 self.dcs[label] = dc
52 dc.create() # finally create the data center in our Mininet instance
53 logging.info("added data center: %s" % label)
54 return dc
55
56 def addLink(self, node1, node2, **params):
57 """
58 Able to handle Datacenter objects as link
59 end points.
60 """
61 assert node1 is not None
62 assert node2 is not None
63 logging.debug("addLink: n1=%s n2=%s" % (str(node1), str(node2)))
64 # ensure type of node1
65 if isinstance( node1, basestring ):
66 if node1 in self.dcs:
67 node1 = self.dcs[node1].switch
68 if isinstance( node1, Datacenter ):
69 node1 = node1.switch
70 # ensure type of node2
71 if isinstance( node2, basestring ):
72 if node2 in self.dcs:
73 node2 = self.dcs[node2].switch
74 if isinstance( node2, Datacenter ):
75 node2 = node2.switch
76 # try to give containers a default IP
77 if isinstance( node1, Docker ):
78 if not "params1" in params:
79 params["params1"] = {}
80 if not "ip" in params["params1"]:
81 params["params1"]["ip"] = self.getNextIp()
82 if isinstance( node2, Docker ):
83 if not "params2" in params:
84 params["params2"] = {}
85 if not "ip" in params["params2"]:
86 params["params2"]["ip"] = self.getNextIp()
87
88 link = Dockernet.addLink(self, node1, node2, **params) # TODO we need TCLinks with user defined performance here
89
90 # add edge and assigned port number to graph in both directions between node1 and node2
91 self.DCNetwork_graph.add_edge(node1.name, node2.name, \
92 {'src_port': node1.ports[link.intf1], 'dst_port': node2.ports[link.intf2]})
93 self.DCNetwork_graph.add_edge(node2.name, node1.name, \
94 {'src_port': node2.ports[link.intf2], 'dst_port': node1.ports[link.intf1]})
95
96 return link
97
98 def addDocker( self, label, **params ):
99 """
100 Wrapper for addDocker method to use custom container class.
101 """
102 self.DCNetwork_graph.add_node(label)
103 return Dockernet.addDocker(self, label, cls=EmulatorCompute, **params)
104
105 def removeDocker( self, label, **params ):
106 """
107 Wrapper for removeDocker method to update graph.
108 """
109 self.DCNetwork_graph.remove_node(label)
110 return Dockernet.removeDocker(self, label, **params)
111
112 def addSwitch( self, name, add_to_graph=True, **params ):
113 """
114 Wrapper for addSwitch method to store switch also in graph.
115 """
116 if add_to_graph:
117 self.DCNetwork_graph.add_node(name)
118 return Dockernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params)
119
120 def getAllContainers(self):
121 """
122 Returns a list with all containers within all data centers.
123 """
124 all_containers = []
125 for dc in self.dcs.itervalues():
126 all_containers += dc.listCompute()
127 return all_containers
128
129 def start(self):
130 # start
131 for dc in self.dcs.itervalues():
132 dc.start()
133 Dockernet.start(self)
134
135 def stop(self):
136 Dockernet.stop(self)
137
138 def CLI(self):
139 CLI(self)
140
141 # to remove chain do setChain( src, dst, cmd='del-flows')
142 def setChain(self, vnf_src_name, vnf_dst_name, cmd='add-flow'):
143 # get shortest path
144 path = nx.shortest_path(self.DCNetwork_graph, vnf_src_name, vnf_dst_name)
145 logging.info("Path between {0} and {1}: {2}".format(vnf_src_name, vnf_dst_name, path))
146
147 current_hop = vnf_src_name
148 for i in range(0,len(path)):
149 next_hop = path[path.index(current_hop)+1]
150 next_node = self.getNodeByName(next_hop)
151
152 if next_hop == vnf_dst_name:
153 return "path added between {0} and {1}".format(vnf_src_name, vnf_dst_name)
154 elif not isinstance( next_node, OVSSwitch ):
155 logging.info("Next node: {0} is not a switch".format(next_hop))
156 return "Next node: {0} is not a switch".format(next_hop)
157
158
159 switch_inport = self.DCNetwork_graph[current_hop][next_hop]['dst_port']
160 next2_hop = path[path.index(current_hop)+2]
161 switch_outport = self.DCNetwork_graph[next_hop][next2_hop]['src_port']
162
163 logging.info("add flow in switch: {0} in_port: {1} out_port: {2}".format(next_node.name, switch_inport, switch_outport))
164 # set of entry via ovs-ofctl
165 # TODO use rest API of ryu to set flow entries to correct witch dpid
166 if isinstance( next_node, OVSSwitch ):
167 match = 'in_port=%s' % switch_inport
168
169 if cmd=='add-flow':
170 action = 'action=%s' % switch_outport
171 s = ','
172 ofcmd = s.join([match,action])
173 elif cmd=='del-flows':
174 ofcmd = match
175 else:
176 ofcmd=''
177
178 next_node.dpctl(cmd, ofcmd)
179
180 current_hop = next_hop
181
182 return "destination node: {0} not reached".format(vnf_dst_name)