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
41 class EmulatorCompute(Docker
):
43 Emulator specific compute node class.
44 Inherits from Containernet's Docker host class.
45 Represents a single container connected to a (logical)
47 We can add emulator specific helper functions to it.
51 self
, name
, dimage
, **kwargs
):
52 self
.datacenter
= kwargs
.get("datacenter") # pointer to current DC
53 self
.flavor_name
= kwargs
.get("flavor_name")
54 LOG
.debug("Starting compute instance %r in data center %r" % (name
, str(self
.datacenter
)))
55 # call original Docker.__init__
56 Docker
.__init
__(self
, name
, dimage
, **kwargs
)
58 def getNetworkStatus(self
):
60 Helper method to receive information about the virtual networks
61 this compute instance is connected to.
63 # get all links and find dc switch interface
64 networkStatusList
= []
65 for i
in self
.intfList():
67 vnf_interface
= str(i
)
68 dc_port_name
= self
.datacenter
.net
.find_connected_dc_interface(vnf_name
, vnf_interface
)
69 # format list of tuples (name, Ip, MAC, isUp, status, dc_portname)
70 intf_dict
= {'intf_name': str(i
), 'ip': i
.IP(), 'mac': i
.MAC(), 'up': i
.isUp(), 'status': i
.status(), 'dc_portname': dc_port_name
}
71 networkStatusList
.append(intf_dict
)
73 return networkStatusList
77 Helper method to receive information about this compute instance.
80 status
["name"] = self
.name
81 status
["network"] = self
.getNetworkStatus()
82 status
["docker_network"] = self
.dcinfo
['NetworkSettings']['IPAddress']
83 status
["image"] = self
.dimage
84 status
["flavor_name"] = self
.flavor_name
85 status
["cpu_quota"] = self
.cpu_quota
86 status
["cpu_period"] = self
.cpu_period
87 status
["cpu_shares"] = self
.cpu_shares
88 status
["cpuset"] = self
.cpuset
89 status
["mem_limit"] = self
.mem_limit
90 status
["memswap_limit"] = self
.memswap_limit
91 status
["state"] = self
.dcli
.inspect_container(self
.dc
)["State"]
92 status
["id"] = self
.dcli
.inspect_container(self
.dc
)["Id"]
93 status
["datacenter"] = (None if self
.datacenter
is None
94 else self
.datacenter
.label
)
98 class Datacenter(object):
100 Represents a logical data center to which compute resources
101 (Docker containers) can be added at runtime.
103 Will also implement resource bookkeeping in later versions.
108 def __init__(self
, label
, metadata
={}, resource_log_path
=None):
109 self
.net
= None # DCNetwork to which we belong
110 # each node (DC) has a short internal name used by Mininet
111 # this is caused by Mininets naming limitations for swtiches etc.
112 self
.name
= "dc%d" % Datacenter
.DC_COUNTER
113 Datacenter
.DC_COUNTER
+= 1
114 # use this for user defined names that can be longer than self.name
116 # dict to store arbitrary metadata (e.g. latitude and longitude)
117 self
.metadata
= metadata
118 # path to which resource information should be logged (e.g. for experiments). None = no logging
119 self
.resource_log_path
= resource_log_path
120 # first prototype assumes one "bigswitch" per DC
122 # keep track of running containers
124 # pointer to assigned resource model
125 self
._resource
_model
= None
130 def _get_next_dc_dpid(self
):
137 Each data center is represented by a single switch to which
138 compute resources can be connected at run time.
140 TODO: This will be changed in the future to support multiple networks
143 self
.switch
= self
.net
.addSwitch(
144 "%s.s1" % self
.name
, dpid
=hex(self
._get
_next
_dc
_dpid
())[2:])
145 LOG
.debug("created data center switch: %s" % str(self
.switch
))
150 def startCompute(self
, name
, image
=None, command
=None, network
=None, flavor_name
="tiny", **kwargs
):
152 Create a new container as compute resource and connect it to this
154 :param name: name (string)
155 :param image: image name (string)
156 :param command: command (string)
157 :param network: networks list({"ip": "10.0.0.254/8"}, {"ip": "11.0.0.254/24"})
158 :param flavor_name: name of the flavor for this compute container
161 assert name
is not None
163 if name
in [c
.name
for c
in self
.net
.getAllContainers()]:
164 raise Exception("Container with name %s already exists." % name
)
165 # set default parameter
167 image
= "ubuntu:trusty"
169 network
= {} # {"ip": "10.0.0.254/8"}
170 if isinstance(network
, dict):
171 network
= [network
] # if we have only one network, put it in a list
172 if isinstance(network
, list):
176 # apply hard-set resource limits=0
177 cpu_percentage
= kwargs
.get('cpu_percent')
179 cpu_period
= self
.net
.cpu_period
180 cpu_quota
= self
.net
.cpu_period
* float(cpu_percentage
)
185 # create the container
186 d
= self
.net
.addDocker(
191 flavor_name
=flavor_name
,
192 cpu_period
= cpu_period
,
193 cpu_quota
= cpu_quota
198 # apply resource limits to container if a resource model is defined
199 if self
._resource
_model
is not None:
201 self
._resource
_model
.allocate(d
)
202 self
._resource
_model
.write_allocation_log(d
, self
.resource_log_path
)
203 except NotEnoughResourcesAvailable
as ex
:
204 LOG
.warning("Allocation of container %r was blocked by resource model." % name
)
206 # ensure that we remove the container
207 self
.net
.removeDocker(name
)
210 # connect all given networks
211 # if no --net option is given, network = [{}], so 1 empty dict in the list
212 # this results in 1 default interface with a default ip address
214 # clean up network configuration (e.g. RTNETLINK does not allow ':' in intf names
215 if nw
.get("id") is not None:
216 nw
["id"] = self
._clean
_ifname
(nw
["id"])
217 # TODO we cannot use TCLink here (see: https://github.com/mpeuster/containernet/issues/3)
218 self
.net
.addLink(d
, self
.switch
, params1
=nw
, cls
=Link
, intfName1
=nw
.get('id'))
220 self
.containers
[name
] = d
221 return d
# we might use UUIDs for naming later on
223 def stopCompute(self
, name
):
225 Stop and remove a container from this data center.
227 assert name
is not None
228 if name
not in self
.containers
:
229 raise Exception("Container with name %s not found." % name
)
230 LOG
.debug("Stopping compute instance %r in data center %r" % (name
, str(self
)))
232 # stop the monitored metrics
233 if self
.net
.monitor_agent
is not None:
234 self
.net
.monitor_agent
.stop_metric(name
)
236 # call resource model and free resources
237 if self
._resource
_model
is not None:
238 self
._resource
_model
.free(self
.containers
[name
])
239 self
._resource
_model
.write_free_log(self
.containers
[name
], self
.resource_log_path
)
243 link
=None, node1
=self
.containers
[name
], node2
=self
.switch
)
246 self
.net
.removeDocker("%s" % (name
))
247 del self
.containers
[name
]
251 def listCompute(self
):
253 Return a list of all running containers assigned to this
256 return list(self
.containers
.itervalues())
260 Return a dict with status information about this DC.
264 "internalname": self
.name
,
265 "switch": self
.switch
.name
,
266 "n_running_containers": len(self
.containers
),
267 "metadata": self
.metadata
270 def assignResourceModel(self
, rm
):
272 Assign a resource model to this DC.
273 :param rm: a BaseResourceModel object
276 if self
._resource
_model
is not None:
277 raise Exception("There is already an resource model assigned to this DC.")
278 self
._resource
_model
= rm
279 self
.net
.rm_registrar
.register(self
, rm
)
280 LOG
.info("Assigned RM: %r to DC: %r" % (rm
, self
))
283 def _clean_ifname(name
):
285 Cleans up given string to be a
286 RTNETLINK compatible interface name.
292 name
= name
.replace(":", "-")
293 name
= name
.replace(" ", "-")
294 name
= name
.replace(".", "-")
295 name
= name
.replace("_", "-")