2 Distributed Cloud Emulator (dcemulator)
3 (c) 2015 by Manuel Peuster <manuel.peuster@upb.de>
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
13 from monitoring
import DCNetworkMonitor
15 from node
import Datacenter
, EmulatorCompute
18 class DCNetwork(Dockernet
):
20 Wraps the original Mininet/Dockernet class and provides
21 methods to add data centers, switches, etc.
23 This class is used by topology definition scripts.
26 def __init__(self
, **kwargs
):
28 # create a Mininet/Dockernet network
29 # call original Docker.__init__ and setup default controller
31 # self, controller=RemoteController, switch=OVSKernelSwitch, **kwargs)
33 self
, controller
=RemoteController
, switch
=OVSKernelSwitch
, **kwargs
)
34 self
.addController('c0', controller
=RemoteController
)
36 # graph of the complete DC network
37 self
.DCNetwork_graph
=nx
.DiGraph()
40 self
.monitor_agent
= DCNetworkMonitor(self
)
43 def addDatacenter(self
, label
, metadata
={}):
45 Create and add a logical cloud data center to the network.
48 raise Exception("Data center label already exists: %s" % label
)
49 dc
= Datacenter(label
, metadata
=metadata
)
50 dc
.net
= self
# set reference to network
52 dc
.create() # finally create the data center in our Mininet instance
53 logging
.info("added data center: %s" % label
)
56 def addLink(self
, node1
, node2
, **params
):
58 Able to handle Datacenter objects as link
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
):
67 node1
= self
.dcs
[node1
].switch
68 if isinstance( node1
, Datacenter
):
70 # ensure type of node2
71 if isinstance( node2
, basestring
):
73 node2
= self
.dcs
[node2
].switch
74 if isinstance( node2
, Datacenter
):
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()
88 link
= Dockernet
.addLink(self
, node1
, node2
, **params
) # TODO we need TCLinks with user defined performance here
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
]})
98 def addDocker( self
, label
, **params
):
100 Wrapper for addDocker method to use custom container class.
102 self
.DCNetwork_graph
.add_node(label
)
103 return Dockernet
.addDocker(self
, label
, cls
=EmulatorCompute
, **params
)
105 def removeDocker( self
, label
, **params
):
107 Wrapper for removeDocker method to update graph.
109 self
.DCNetwork_graph
.remove_node(label
)
110 return Dockernet
.removeDocker(self
, label
, **params
)
112 def addSwitch( self
, name
, add_to_graph
=True, **params
):
114 Wrapper for addSwitch method to store switch also in graph.
117 self
.DCNetwork_graph
.add_node(name
)
118 return Dockernet
.addSwitch(self
, name
, protocols
='OpenFlow10,OpenFlow12,OpenFlow13', **params
)
120 def getAllContainers(self
):
122 Returns a list with all containers within all data centers.
125 for dc
in self
.dcs
.itervalues():
126 all_containers
+= dc
.listCompute()
127 return all_containers
131 for dc
in self
.dcs
.itervalues():
133 Dockernet
.start(self
)
141 # to remove chain do setChain( src, dst, cmd='del-flows')
142 def setChain(self
, vnf_src_name
, vnf_dst_name
, cmd
='add-flow'):
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
))
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
)
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
)
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']
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
170 action
= 'action=%s' % switch_outport
172 ofcmd
= s
.join([match
,action
])
173 elif cmd
=='del-flows':
178 next_node
.dpctl(cmd
, ofcmd
)
180 current_hop
= next_hop
182 return "destination node: {0} not reached".format(vnf_dst_name
)