2 Distributed Cloud Emulator (dcemulator)
3 (c) 2015 by Manuel Peuster <manuel.peuster@upb.de>
9 from subprocess
import Popen
12 from mininet
.net
import Dockernet
13 from mininet
.node
import Controller
, DefaultController
, OVSSwitch
, OVSKernelSwitch
, Docker
, RemoteController
14 from mininet
.cli
import CLI
15 from mininet
.link
import TCLink
17 from emuvim
.dcemulator
.monitoring
import DCNetworkMonitor
18 from emuvim
.dcemulator
.node
import Datacenter
, EmulatorCompute
19 from emuvim
.dcemulator
.resourcemodel
import ResourceModelRegistrar
22 class DCNetwork(Dockernet
):
24 Wraps the original Mininet/Dockernet class and provides
25 methods to add data centers, switches, etc.
27 This class is used by topology definition scripts.
30 def __init__(self
, controller
=RemoteController
, dc_emulation_max_cpu
=1.0, **kwargs
):
32 Create an extended version of a Dockernet network
33 :param dc_emulation_max_cpu: max. CPU time used by containers in data centers
34 :param kwargs: path through for Mininet parameters
39 # call original Docker.__init__ and setup default controller
41 self
, switch
=OVSKernelSwitch
, **kwargs
)
44 self
.ryu_process
= None
45 if controller
== RemoteController
:
46 # start Ryu controller
49 # add the specified controller
50 self
.addController('c0', controller
=controller
)
52 # graph of the complete DC network
53 self
.DCNetwork_graph
= nx
.DiGraph()
56 self
.monitor_agent
= DCNetworkMonitor(self
)
58 # initialize resource model registrar
59 self
.rm_registrar
= ResourceModelRegistrar(dc_emulation_max_cpu
)
61 def addDatacenter(self
, label
, metadata
={}):
63 Create and add a logical cloud data center to the network.
66 raise Exception("Data center label already exists: %s" % label
)
67 dc
= Datacenter(label
, metadata
=metadata
)
68 dc
.net
= self
# set reference to network
70 dc
.create() # finally create the data center in our Mininet instance
71 logging
.info("added data center: %s" % label
)
74 def addLink(self
, node1
, node2
, **params
):
76 Able to handle Datacenter objects as link
79 assert node1
is not None
80 assert node2
is not None
81 logging
.debug("addLink: n1=%s n2=%s" % (str(node1
), str(node2
)))
82 # ensure type of node1
83 if isinstance( node1
, basestring
):
85 node1
= self
.dcs
[node1
].switch
86 if isinstance( node1
, Datacenter
):
88 # ensure type of node2
89 if isinstance( node2
, basestring
):
91 node2
= self
.dcs
[node2
].switch
92 if isinstance( node2
, Datacenter
):
94 # try to give containers a default IP
95 if isinstance( node1
, Docker
):
96 if "params1" not in params
:
97 params
["params1"] = {}
98 if "ip" not in params
["params1"]:
99 params
["params1"]["ip"] = self
.getNextIp()
100 if isinstance( node2
, Docker
):
101 if "params2" not in params
:
102 params
["params2"] = {}
103 if "ip" not in params
["params2"]:
104 params
["params2"]["ip"] = self
.getNextIp()
105 # ensure that we allow TCLinks between data centers
106 # TODO this is not optimal, we use cls=Link for containers and TCLink for data centers
107 # see Dockernet issue: https://github.com/mpeuster/dockernet/issues/3
108 if "cls" not in params
:
109 params
["cls"] = TCLink
111 link
= Dockernet
.addLink(self
, node1
, node2
, **params
)
113 # add edge and assigned port number to graph in both directions between node1 and node2
114 self
.DCNetwork_graph
.add_edge(node1
.name
, node2
.name
, \
115 {'src_port': node1
.ports
[link
.intf1
], 'dst_port': node2
.ports
[link
.intf2
]})
116 self
.DCNetwork_graph
.add_edge(node2
.name
, node1
.name
, \
117 {'src_port': node2
.ports
[link
.intf2
], 'dst_port': node1
.ports
[link
.intf1
]})
121 def addDocker( self
, label
, **params
):
123 Wrapper for addDocker method to use custom container class.
125 self
.DCNetwork_graph
.add_node(label
)
126 return Dockernet
.addDocker(self
, label
, cls
=EmulatorCompute
, **params
)
128 def removeDocker( self
, label
, **params
):
130 Wrapper for removeDocker method to update graph.
132 self
.DCNetwork_graph
.remove_node(label
)
133 return Dockernet
.removeDocker(self
, label
, **params
)
135 def addSwitch( self
, name
, add_to_graph
=True, **params
):
137 Wrapper for addSwitch method to store switch also in graph.
140 self
.DCNetwork_graph
.add_node(name
)
141 return Dockernet
.addSwitch(self
, name
, protocols
='OpenFlow10,OpenFlow12,OpenFlow13', **params
)
143 def getAllContainers(self
):
145 Returns a list with all containers within all data centers.
148 for dc
in self
.dcs
.itervalues():
149 all_containers
+= dc
.listCompute()
150 return all_containers
154 for dc
in self
.dcs
.itervalues():
156 Dockernet
.start(self
)
159 # stop Ryu controller
166 # to remove chain do setChain( src, dst, cmd='del-flows')
167 def setChain(self
, vnf_src_name
, vnf_dst_name
, cmd
='add-flow'):
169 path
= nx
.shortest_path(self
.DCNetwork_graph
, vnf_src_name
, vnf_dst_name
)
170 logging
.info("Path between {0} and {1}: {2}".format(vnf_src_name
, vnf_dst_name
, path
))
172 current_hop
= vnf_src_name
173 for i
in range(0,len(path
)):
174 next_hop
= path
[path
.index(current_hop
)+1]
175 next_node
= self
.getNodeByName(next_hop
)
177 if next_hop
== vnf_dst_name
:
178 return "path added between {0} and {1}".format(vnf_src_name
, vnf_dst_name
)
179 elif not isinstance( next_node
, OVSSwitch
):
180 logging
.info("Next node: {0} is not a switch".format(next_hop
))
181 return "Next node: {0} is not a switch".format(next_hop
)
184 switch_inport
= self
.DCNetwork_graph
[current_hop
][next_hop
]['dst_port']
185 next2_hop
= path
[path
.index(current_hop
)+2]
186 switch_outport
= self
.DCNetwork_graph
[next_hop
][next2_hop
]['src_port']
188 logging
.info("add flow in switch: {0} in_port: {1} out_port: {2}".format(next_node
.name
, switch_inport
, switch_outport
))
189 # set of entry via ovs-ofctl
190 # TODO use rest API of ryu to set flow entries to correct witch dpid
191 if isinstance( next_node
, OVSSwitch
):
192 match
= 'in_port=%s' % switch_inport
195 action
= 'action=%s' % switch_outport
197 ofcmd
= s
.join([match
,action
])
198 elif cmd
=='del-flows':
203 next_node
.dpctl(cmd
, ofcmd
)
205 current_hop
= next_hop
207 return "destination node: {0} not reached".format(vnf_dst_name
)
209 # start Ryu Openflow controller as Remote Controller for the DCNetwork
211 # start Ryu controller with rest-API
212 python_install_path
= site
.getsitepackages()[0]
213 ryu_path
= python_install_path
+ '/ryu/app/simple_switch_13.py'
214 ryu_path2
= python_install_path
+ '/ryu/app/ofctl_rest.py'
215 # change the default Openflow controller port to 6653 (official IANA-assigned port number), as used by Mininet
216 # Ryu still uses 6633 as default
217 ryu_option
= '--ofp-tcp-listen-port'
219 ryu_cmd
= 'ryu-manager'
220 FNULL
= open("/tmp/ryu.log", 'w')
221 self
.ryu_process
= Popen([ryu_cmd
, ryu_path
, ryu_path2
, ryu_option
, ryu_of_port
], stdout
=FNULL
, stderr
=FNULL
)
225 if self
.ryu_process
is not None:
226 self
.ryu_process
.terminate()
227 self
.ryu_process
.kill()