2 Copyright (c) 2015 SONATA-NFV and Paderborn University
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
17 Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
22 This work has been performed in the framework of the SONATA project,
23 funded by the European Commission under Grant number 671517 through
24 the Horizon 2020 and 5G-PPP programmes. The authors would like to
25 acknowledge the contributions of their colleagues of the SONATA
26 partner consortium (www.sonata-nfv.eu).
28 from mininet
.node
import Docker
29 from mininet
.link
import Link
30 from emuvim
.dcemulator
.resourcemodel
import NotEnoughResourcesAvailable
35 LOG
= logging
.getLogger("dcemulator.node")
36 LOG
.setLevel(logging
.DEBUG
)
39 DCDPID_BASE
= 1000 # start of switch dpid's used for data center switches
42 class EmulatorCompute(Docker
):
44 Emulator specific compute node class.
45 Inherits from Containernet's Docker host class.
46 Represents a single container connected to a (logical)
48 We can add emulator specific helper functions to it.
52 self
, name
, dimage
, **kwargs
):
53 self
.datacenter
= kwargs
.get("datacenter") # pointer to current DC
54 self
.flavor_name
= kwargs
.get("flavor_name")
55 LOG
.debug("Starting compute instance %r in data center %r" % (name
, str(self
.datacenter
)))
56 # call original Docker.__init__
57 Docker
.__init
__(self
, name
, dimage
, **kwargs
)
59 def getNetworkStatus(self
):
61 Helper method to receive information about the virtual networks
62 this compute instance is connected to.
65 # format list of tuples (name, Ip, MAC, isUp, status)
66 return [{'intf_name':str(i
), 'ip':i
.IP(), 'mac':i
.MAC(), 'up':i
.isUp(), 'status':i
.status()}
67 for i
in self
.intfList()]
71 Helper method to receive information about this compute instance.
74 status
["name"] = self
.name
75 status
["network"] = self
.getNetworkStatus()
76 status
["docker_network"] = self
.dcinfo
['NetworkSettings']['IPAddress']
77 status
["image"] = self
.dimage
78 status
["flavor_name"] = self
.flavor_name
79 status
["cpu_quota"] = self
.cpu_quota
80 status
["cpu_period"] = self
.cpu_period
81 status
["cpu_shares"] = self
.cpu_shares
82 status
["cpuset"] = self
.cpuset
83 status
["mem_limit"] = self
.mem_limit
84 status
["memswap_limit"] = self
.memswap_limit
85 status
["state"] = self
.dcli
.inspect_container(self
.dc
)["State"]
86 status
["id"] = self
.dcli
.inspect_container(self
.dc
)["Id"]
87 status
["datacenter"] = (None if self
.datacenter
is None
88 else self
.datacenter
.label
)
92 class Datacenter(object):
94 Represents a logical data center to which compute resources
95 (Docker containers) can be added at runtime.
97 Will also implement resource bookkeeping in later versions.
102 def __init__(self
, label
, metadata
={}, resource_log_path
=None):
103 self
.net
= None # DCNetwork to which we belong
104 # each node (DC) has a short internal name used by Mininet
105 # this is caused by Mininets naming limitations for swtiches etc.
106 self
.name
= "dc%d" % Datacenter
.DC_COUNTER
107 Datacenter
.DC_COUNTER
+= 1
108 # use this for user defined names that can be longer than self.name
110 # dict to store arbitrary metadata (e.g. latitude and longitude)
111 self
.metadata
= metadata
112 # path to which resource information should be logged (e.g. for experiments). None = no logging
113 self
.resource_log_path
= resource_log_path
114 # first prototype assumes one "bigswitch" per DC
116 # keep track of running containers
118 # pointer to assigned resource model
119 self
._resource
_model
= None
124 def _get_next_dc_dpid(self
):
131 Each data center is represented by a single switch to which
132 compute resources can be connected at run time.
134 TODO: This will be changed in the future to support multiple networks
137 self
.switch
= self
.net
.addSwitch(
138 "%s.s1" % self
.name
, dpid
=hex(self
._get
_next
_dc
_dpid
())[2:])
139 LOG
.debug("created data center switch: %s" % str(self
.switch
))
144 def startCompute(self
, name
, image
=None, command
=None, network
=None, flavor_name
="tiny"):
146 Create a new container as compute resource and connect it to this
148 :param name: name (string)
149 :param image: image name (string)
150 :param command: command (string)
151 :param network: networks list({"ip": "10.0.0.254/8"}, {"ip": "11.0.0.254/24"})
152 :param flavor_name: name of the flavor for this compute container
155 assert name
is not None
157 if name
in [c
.name
for c
in self
.net
.getAllContainers()]:
158 raise Exception("Container with name %s already exists." % name
)
159 # set default parameter
161 image
= "ubuntu:trusty"
163 network
= {} # {"ip": "10.0.0.254/8"}
164 if isinstance(network
, dict):
165 network
= [network
] # if we have only one network, put it in a list
166 if isinstance(network
, list):
170 # create the container
171 d
= self
.net
.addDocker(
176 flavor_name
=flavor_name
179 # apply resource limits to container if a resource model is defined
180 if self
._resource
_model
is not None:
182 self
._resource
_model
.allocate(d
)
183 self
._resource
_model
.write_allocation_log(d
, self
.resource_log_path
)
184 except NotEnoughResourcesAvailable
as ex
:
185 LOG
.warning("Allocation of container %r was blocked by resource model." % name
)
187 # ensure that we remove the container
188 self
.net
.removeDocker(name
)
191 # connect all given networks
192 # if no --net option is given, network = [{}], so 1 empty dict in the list
193 # this results in 1 default interface with a default ip address
195 # clean up network configuration (e.g. RTNETLINK does not allow ':' in intf names
196 if nw
.get("id") is not None:
197 nw
["id"] = self
._clean
_ifname
(nw
["id"])
198 # TODO we cannot use TCLink here (see: https://github.com/mpeuster/containernet/issues/3)
199 self
.net
.addLink(d
, self
.switch
, params1
=nw
, cls
=Link
, intfName1
=nw
.get('id'))
201 self
.containers
[name
] = d
202 return d
# we might use UUIDs for naming later on
204 def stopCompute(self
, name
):
206 Stop and remove a container from this data center.
208 assert name
is not None
209 if name
not in self
.containers
:
210 raise Exception("Container with name %s not found." % name
)
211 LOG
.debug("Stopping compute instance %r in data center %r" % (name
, str(self
)))
213 # stop the monitored metrics
214 if self
.net
.monitor_agent
is not None:
215 self
.net
.monitor_agent
.stop_metric(name
)
217 # call resource model and free resources
218 if self
._resource
_model
is not None:
219 self
._resource
_model
.free(self
.containers
[name
])
220 self
._resource
_model
.write_free_log(self
.containers
[name
], self
.resource_log_path
)
224 link
=None, node1
=self
.containers
[name
], node2
=self
.switch
)
227 self
.net
.removeDocker("%s" % (name
))
228 del self
.containers
[name
]
232 def listCompute(self
):
234 Return a list of all running containers assigned to this
237 return list(self
.containers
.itervalues())
241 Return a dict with status information about this DC.
245 "internalname": self
.name
,
246 "switch": self
.switch
.name
,
247 "n_running_containers": len(self
.containers
),
248 "metadata": self
.metadata
251 def assignResourceModel(self
, rm
):
253 Assign a resource model to this DC.
254 :param rm: a BaseResourceModel object
257 if self
._resource
_model
is not None:
258 raise Exception("There is already an resource model assigned to this DC.")
259 self
._resource
_model
= rm
260 self
.net
.rm_registrar
.register(self
, rm
)
261 LOG
.info("Assigned RM: %r to DC: %r" % (rm
, self
))
264 def _clean_ifname(name
):
266 Cleans up given string to be a
267 RTNETLINK compatible interface name.
273 name
= name
.replace(":", "-")
274 name
= name
.replace(" ", "-")
275 name
= name
.replace(".", "-")
276 name
= name
.replace("_", "-")