Fix: Better name-handling of Docker-based VNFs
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / nova_dummy_api.py
1 """
2 Copyright (c) 2017 SONATA-NFV and Paderborn University
3 ALL RIGHTS RESERVED.
4
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
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
17 Neither the name of the SONATA-NFV, Paderborn University
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
20 permission.
21
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).
27 """
28 from flask_restful import Resource
29 from flask import Response, request
30 from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
31 from emuvim.api.openstack.helper import get_host
32 import logging
33 import json
34 import uuid
35 from mininet.link import Link
36
37
38 LOG = logging.getLogger("api.openstack.nova")
39
40
41 class NovaDummyApi(BaseOpenstackDummy):
42 def __init__(self, in_ip, in_port, compute):
43 super(NovaDummyApi, self).__init__(in_ip, in_port)
44 self.compute = compute
45 self.compute.add_flavor('m1.tiny', 1, 512, "MB", 1, "GB")
46 self.compute.add_flavor('m1.nano', 1, 64, "MB", 0, "GB")
47 self.compute.add_flavor('m1.micro', 1, 128, "MB", 0, "GB")
48 self.compute.add_flavor('m1.small', 1, 1024, "MB", 2, "GB")
49
50 self.api.add_resource(NovaVersionsList, "/",
51 resource_class_kwargs={'api': self})
52 self.api.add_resource(NovaVersionShow, "/v2.1/<id>",
53 resource_class_kwargs={'api': self})
54 self.api.add_resource(NovaListServersApi, "/v2.1/<id>/servers",
55 resource_class_kwargs={'api': self})
56 self.api.add_resource(NovaListServersAndPortsApi, "/v2.1/<id>/servers/andPorts",
57 resource_class_kwargs={'api': self})
58 self.api.add_resource(NovaListServersDetailed, "/v2.1/<id>/servers/detail",
59 resource_class_kwargs={'api': self})
60 self.api.add_resource(NovaShowServerDetails, "/v2.1/<id>/servers/<serverid>",
61 resource_class_kwargs={'api': self})
62 self.api.add_resource(NovaInterfaceToServer, "/v2.1/<id>/servers/<serverid>/os-interface",
63 resource_class_kwargs={'api': self})
64 self.api.add_resource(NovaShowAndDeleteInterfaceAtServer, "/v2.1/<id>/servers/<serverid>/os-interface/<port_id>",
65 resource_class_kwargs={'api': self})
66 self.api.add_resource(NovaListFlavors, "/v2.1/<id>/flavors", "/v2/<id>/flavors",
67 resource_class_kwargs={'api': self})
68 self.api.add_resource(NovaListFlavorsDetails, "/v2.1/<id>/flavors/detail", "/v2/<id>/flavors/detail",
69 resource_class_kwargs={'api': self})
70 self.api.add_resource(NovaListFlavorById, "/v2.1/<id>/flavors/<flavorid>", "/v2/<id>/flavors/<flavorid>",
71 resource_class_kwargs={'api': self})
72 self.api.add_resource(NovaListImages, "/v2.1/<id>/images",
73 resource_class_kwargs={'api': self})
74 self.api.add_resource(NovaListImagesDetails, "/v2.1/<id>/images/detail",
75 resource_class_kwargs={'api': self})
76 self.api.add_resource(NovaListImageById, "/v2.1/<id>/images/<imageid>",
77 resource_class_kwargs={'api': self})
78 self.api.add_resource(NovaLimits, "/v2.1/<id>/limits",
79 resource_class_kwargs={'api': self})
80
81
82 class NovaVersionsList(Resource):
83 def __init__(self, api):
84 self.api = api
85
86 def get(self):
87 """
88 Lists API versions.
89
90 :return: Returns a json with API versions.
91 :rtype: :class:`flask.response`
92 """
93 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
94 try:
95 resp = """
96 {
97 "versions": [
98 {
99 "id": "v2.1",
100 "links": [
101 {
102 "href": "http://%s:%d/v2.1/",
103 "rel": "self"
104 }
105 ],
106 "status": "CURRENT",
107 "version": "2.38",
108 "min_version": "2.1",
109 "updated": "2013-07-23T11:33:21Z"
110 }
111 ]
112 }
113 """ % (get_host(request), self.api.port)
114
115 response = Response(resp, status=200, mimetype="application/json")
116 response.headers['Access-Control-Allow-Origin'] = '*'
117 return response
118
119 except Exception as ex:
120 LOG.exception(u"%s: Could not show list of versions." % __name__)
121 return ex.message, 500
122
123
124 class NovaVersionShow(Resource):
125 def __init__(self, api):
126 self.api = api
127
128 def get(self, id):
129 """
130 Returns API details.
131
132 :param id:
133 :type id: ``str``
134 :return: Returns a json with API details.
135 :rtype: :class:`flask.response`
136 """
137 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
138
139 try:
140 resp = """
141 {
142 "version": {
143 "id": "v2.1",
144 "links": [
145 {
146 "href": "http://%s:%d/v2.1/",
147 "rel": "self"
148 },
149 {
150 "href": "http://docs.openstack.org/",
151 "rel": "describedby",
152 "type": "text/html"
153 }
154 ],
155 "media-types": [
156 {
157 "base": "application/json",
158 "type": "application/vnd.openstack.compute+json;version=2.1"
159 }
160 ],
161 "status": "CURRENT",
162 "version": "2.38",
163 "min_version": "2.1",
164 "updated": "2013-07-23T11:33:21Z"
165 }
166 }
167 """ % (get_host(request), self.api.port)
168
169 response = Response(resp, status=200, mimetype="application/json")
170 response.headers['Access-Control-Allow-Origin'] = '*'
171 return response
172
173 except Exception as ex:
174 LOG.exception(u"%s: Could not show list of versions." % __name__)
175 return ex.message, 500
176
177
178 class NovaListServersApi(Resource):
179 def __init__(self, api):
180 self.api = api
181
182 def get(self, id):
183 """
184 Creates a list with all running servers and their detailed information.
185
186 :param id: Used to create a individual link to quarry further information.
187 :type id: ``str``
188 :return: Returns a json response with a dictionary that contains the server information.
189 :rtype: :class:`flask.response`
190 """
191 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
192
193 try:
194 resp = dict()
195 resp['servers'] = list()
196 for server in self.api.compute.computeUnits.values():
197 s = server.create_server_dict(self.api.compute)
198 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
199 self.api.port,
200 id,
201 server.id)}]
202
203 resp['servers'].append(s)
204
205 response = Response(json.dumps(resp), status=200, mimetype="application/json")
206 response.headers['Access-Control-Allow-Origin'] = '*'
207 return response
208
209 except Exception as ex:
210 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
211 return ex.message, 500
212
213 def post(self, id):
214 """
215 Creates a server instance.
216
217 :param id: tenant id, we ignore this most of the time
218 :type id: ``str``
219 :return: Returns a flask response, with detailed information about the just created server.
220 :rtype: :class:`flask.response`
221 """
222 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
223 try:
224 server_dict = json.loads(request.data)['server']
225 networks = server_dict.get('networks', None)
226 name = str(self.api.compute.dc.label) + "_" + server_dict["name"]
227
228 if self.api.compute.find_server_by_name_or_id(name) is not None:
229 return Response("Server with name %s already exists." % name, status=409)
230 # TODO: not finished!
231 resp = dict()
232
233 server = self.api.compute.create_server(name)
234 server.full_name = str(self.api.compute.dc.label) + "_" + server_dict["name"]
235 server.template_name = server_dict["name"]
236 if "metadata" in server_dict:
237 server.properties = server_dict["metadata"]
238
239 for flavor in self.api.compute.flavors.values():
240 if flavor.id == server_dict.get('flavorRef', ''):
241 server.flavor = flavor.name
242 for image in self.api.compute.images.values():
243 if image.id in server_dict['imageRef']:
244 server.image = image.name
245
246 if networks is not None:
247 for net in networks:
248 port = self.api.compute.find_port_by_name_or_id(net.get('port', ""))
249 if port is not None:
250 server.port_names.append(port.name)
251 else:
252 return Response("Currently only networking by port is supported.", status=400)
253
254 self.api.compute._start_compute(server)
255
256 response = NovaShowServerDetails(self.api).get(id, server.id)
257 response.headers['Access-Control-Allow-Origin'] = '*'
258 return response
259
260 except Exception as ex:
261 LOG.exception(u"%s: Could not create the server." % __name__)
262 return ex.message, 500
263
264
265 class NovaListServersAndPortsApi(Resource):
266 def __init__(self, api):
267 self.api = api
268
269 def get(self, id):
270 """
271 Creates a list with all running servers and their detailed information. This function also presents all
272 port information of each server.
273
274 :param id: Used to create a individual link to quarry further information.
275 :type id: ``str``
276 :return: Returns a json response with a dictionary that contains the server information.
277 :rtype: :class:`flask.response`
278 """
279 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
280
281 try:
282 resp = dict()
283 resp['servers'] = list()
284 for server in self.api.compute.computeUnits.values():
285 s = server.create_server_dict(self.api.compute)
286 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
287 self.api.port,
288 id,
289 server.id)}]
290
291 s['ports'] = list()
292 for port_name in server.port_names:
293 port = self.api.compute.find_port_by_name_or_id(port_name)
294 if port is None:
295 continue
296
297 tmp = port.create_port_dict(self.api.compute)
298 tmp['intf_name'] = port.intf_name
299 s['ports'].append(tmp)
300
301 resp['servers'].append(s)
302
303 response = Response(json.dumps(resp), status=200, mimetype="application/json")
304 response.headers['Access-Control-Allow-Origin'] = '*'
305 return response
306
307 except Exception as ex:
308 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
309 return ex.message, 500
310
311
312 class NovaListServersDetailed(Resource):
313 def __init__(self, api):
314 self.api = api
315
316 def get(self, id):
317 """
318 As List Servers, it lists all running servers and their details but furthermore it also states the
319 used flavor and the server image.
320
321 :param id: tenant id, used for the 'href' link.
322 :type id: ``str``
323 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
324 :rtype: :class:`flask.response`
325 """
326 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
327
328 try:
329 resp = {"servers": list()}
330 for server in self.api.compute.computeUnits.values():
331 s = server.create_server_dict(self.api.compute)
332 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
333 self.api.port,
334 id,
335 server.id)}]
336 flavor = self.api.compute.flavors[server.flavor]
337 s['flavor'] = {
338 "id": flavor.id,
339 "links": [
340 {
341 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
342 self.api.port,
343 id,
344 flavor.id),
345 "rel": "bookmark"
346 }
347 ]
348 }
349 image = self.api.compute.images[server.image]
350 s['image'] = {
351 "id": image.id,
352 "links": [
353 {
354 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
355 self.api.port,
356 id,
357 image.id),
358 "rel": "bookmark"
359 }
360 ]
361 }
362
363 resp['servers'].append(s)
364
365 response = Response(json.dumps(resp), status=200, mimetype="application/json")
366 response.headers['Access-Control-Allow-Origin'] = '*'
367 return response
368
369 except Exception as ex:
370 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
371 return ex.message, 500
372
373
374 class NovaListFlavors(Resource):
375 def __init__(self, api):
376 self.api = api
377
378 def get(self, id):
379 """
380 Lists all available flavors.
381
382 :param id: tenant id, used for the 'href' link
383 :type id: ``str``
384 :return: Returns a flask response with a list of all flavors.
385 :rtype: :class:`flask.response`
386 """
387 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
388 try:
389 resp = dict()
390 resp['flavors'] = list()
391 for flavor in self.api.compute.flavors.values():
392 f = flavor.__dict__.copy()
393 f['id'] = flavor.id
394 f['name'] = flavor.name
395 f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
396 self.api.port,
397 id,
398 flavor.id)}]
399 resp['flavors'].append(f)
400
401 response = Response(json.dumps(resp), status=200, mimetype="application/json")
402 response.headers['Access-Control-Allow-Origin'] = '*'
403 return response
404
405 except Exception as ex:
406 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
407 return ex.message, 500
408
409 def post(self, id):
410 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
411 data = json.loads(request.data).get("flavor")
412 LOG.warning("Create Flavor: %s" % str(data))
413 # add to internal dict
414 f = self.api.compute.add_flavor(
415 data.get("name"),
416 data.get("vcpus"),
417 data.get("ram"), "MB",
418 data.get("disk"), "GB")
419 # create response based on incoming data
420 data["id"] = f.id
421 data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
422 self.api.port,
423 id,
424 f.id)}]
425 resp = {"flavor": data}
426 return Response(json.dumps(resp), status=200, mimetype="application/json")
427
428
429 class NovaListFlavorsDetails(Resource):
430 def __init__(self, api):
431 self.api = api
432
433 def get(self, id):
434 """
435 Lists all flavors with additional information like ram and disk space.
436
437 :param id: tenant id, used for the 'href' link
438 :type id: ``str``
439 :return: Returns a flask response with a list of all flavors with additional information.
440 :rtype: :class:`flask.response`
441 """
442 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
443 try:
444 resp = dict()
445 resp['flavors'] = list()
446 for flavor in self.api.compute.flavors.values():
447 # use the class dict. it should work fine
448 # but use a copy so we don't modifiy the original
449 f = flavor.__dict__.copy()
450 # add additional expected stuff stay openstack compatible
451 f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
452 self.api.port,
453 id,
454 flavor.id)}]
455 f['OS-FLV-DISABLED:disabled'] = False
456 f['OS-FLV-EXT-DATA:ephemeral'] = 0
457 f['os-flavor-access:is_public'] = True
458 f['ram'] = flavor.memory
459 f['vcpus'] = flavor.cpu
460 f['swap'] = 0
461 f['disk'] = flavor.storage
462 f['rxtx_factor'] = 1.0
463 resp['flavors'].append(f)
464
465 response = Response(json.dumps(resp), status=200, mimetype="application/json")
466 response.headers['Access-Control-Allow-Origin'] = '*'
467 return response
468
469 except Exception as ex:
470 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
471 return ex.message, 500
472
473 def post(self, id):
474 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
475 data = json.loads(request.data).get("flavor")
476 LOG.warning("Create Flavor: %s" % str(data))
477 # add to internal dict
478 f = self.api.compute.add_flavor(
479 data.get("name"),
480 data.get("vcpus"),
481 data.get("ram"), "MB",
482 data.get("disk"), "GB")
483 # create response based on incoming data
484 data["id"] = f.id
485 data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
486 self.api.port,
487 id,
488 f.id)}]
489 resp = {"flavor": data}
490 return Response(json.dumps(resp), status=200, mimetype="application/json")
491
492
493 class NovaListFlavorById(Resource):
494 def __init__(self, api):
495 self.api = api
496
497 def get(self, id, flavorid):
498 """
499 Returns details about one flavor.
500
501 :param id: tenant id, used for the 'href' link
502 :type id: ``str``
503 :param flavorid: Represents the flavor.
504 :type flavorid: ``str``
505 :return: Returns a flask response with detailed information about the flavor.
506 :rtype: :class:`flask.response`
507 """
508 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
509 try:
510 resp = dict()
511 resp['flavor'] = dict()
512 flavor = self.api.compute.flavors.get(flavorid, None)
513 if flavor is None:
514 for f in self.api.compute.flavors.values():
515 if f.id == flavorid:
516 flavor = f
517 break
518 resp['flavor']['id'] = flavor.id
519 resp['flavor']['name'] = flavor.name
520 resp['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
521 self.api.port,
522 id,
523 flavor.id)}]
524 response = Response(json.dumps(resp), status=200, mimetype="application/json")
525 response.headers['Access-Control-Allow-Origin'] = '*'
526 return response
527
528 except Exception as ex:
529 LOG.exception(u"%s: Could not retrieve flavor with id %s" % (__name__, flavorid))
530 return ex.message, 500
531
532 def delete(self, id, flavorid):
533 """
534 Removes the given flavor.
535 Does not really remove anything from the machine, just fakes an OK.
536 """
537 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
538 return Response("{}", status=204, mimetype="application/json")
539
540
541 class NovaListImages(Resource):
542 def __init__(self, api):
543 self.api = api
544
545 def get(self, id):
546 """
547 Creates a list of all usable images.
548
549 :param id: tenant id, used for the 'href' link
550 :type id: ``str``
551 :return: Returns a flask response with a list of available images.
552 :rtype: :class:`flask.response`
553 """
554 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
555 try:
556 resp = dict()
557 resp['images'] = list()
558 for image in self.api.compute.images.values():
559 f = dict()
560 f['id'] = image.id
561 f['name'] = str(image.name).replace(":latest", "")
562 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
563 self.api.port,
564 id,
565 image.id)}]
566 resp['images'].append(f)
567 response = Response(json.dumps(resp), status=200, mimetype="application/json")
568 response.headers['Access-Control-Allow-Origin'] = '*'
569 return response
570
571 except Exception as ex:
572 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
573 return ex.message, 500
574
575
576 class NovaListImagesDetails(Resource):
577 def __init__(self, api):
578 self.api = api
579
580 def get(self, id):
581 """
582 As List Images but with additional metadata.
583
584 :param id: tenant id, used for the 'href' link
585 :type id: ``str``
586 :return: Returns a flask response with a list of images and their metadata.
587 :rtype: :class:`flask.response`
588 """
589 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
590 try:
591 resp = dict()
592 resp['images'] = list()
593 for image in self.api.compute.images.values():
594 # use the class dict. it should work fine
595 # but use a copy so we don't modifiy the original
596 f = image.__dict__.copy()
597 # add additional expected stuff stay openstack compatible
598 f['name'] = str(image.name).replace(":latest", "")
599 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
600 self.api.port,
601 id,
602 image.id)}]
603 f['metadata'] = {
604 "architecture": "x86_64",
605 "auto_disk_config": "True",
606 "kernel_id": "nokernel",
607 "ramdisk_id": "nokernel"
608 }
609 resp['images'].append(f)
610
611 response = Response(json.dumps(resp), status=200, mimetype="application/json")
612 response.headers['Access-Control-Allow-Origin'] = '*'
613 return response
614
615 except Exception as ex:
616 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
617 return ex.message, 500
618
619
620 class NovaListImageById(Resource):
621 def __init__(self, api):
622 self.api = api
623
624 def get(self, id, imageid):
625 """
626 Gets an image by id from the emulator with openstack nova compliant return values.
627
628 :param id: tenantid, we ignore this most of the time
629 :type id: ``str``
630 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
631 :type imageid: ``str``
632 :return: Returns a flask response with the information about one image.
633 :rtype: :class:`flask.response`
634 """
635 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
636 try:
637 resp = dict()
638 i = resp['image'] = dict()
639 for image in self.api.compute.images.values():
640 if image.id == imageid or image.name == imageid:
641 i['id'] = image.id
642 i['name'] = image.name
643
644 return Response(json.dumps(resp), status=200, mimetype="application/json")
645
646 response = Response("Image with id or name %s does not exists." % imageid, status=404)
647 response.headers['Access-Control-Allow-Origin'] = '*'
648 return response
649
650 except Exception as ex:
651 LOG.exception(u"%s: Could not retrieve image with id %s." % (__name__, imageid))
652 return ex.message, 500
653
654 def delete(self, id, imageid):
655 """
656 Removes the given image.
657 Does not really remove anything from the machine, just fakes an OK.
658 """
659 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
660 return Response("{}", status=204, mimetype="application/json")
661
662
663 class NovaShowServerDetails(Resource):
664 def __init__(self, api):
665 self.api = api
666
667 def get(self, id, serverid):
668 """
669 Returns detailed information about the specified server.
670
671 :param id: tenant id, used for the 'href' link
672 :type id: ``str``
673 :param serverid: Specifies the requested server.
674 :type serverid: ``str``
675 :return: Returns a flask response with details about the server.
676 :rtype: :class:`flask.response`
677 """
678 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
679 try:
680 server = self.api.compute.find_server_by_name_or_id(serverid)
681 if server is None:
682 return Response("Server with id or name %s does not exists." % serverid, status=404)
683 s = server.create_server_dict()
684 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
685 self.api.port,
686 id,
687 server.id)}]
688
689 flavor = self.api.compute.flavors[server.flavor]
690 s['flavor'] = {
691 "id": flavor.id,
692 "links": [
693 {
694 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
695 self.api.port,
696 id,
697 flavor.id),
698 "rel": "bookmark"
699 }
700 ]
701 }
702 image = self.api.compute.images[server.image]
703 s['image'] = {
704 "id": image.id,
705 "links": [
706 {
707 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
708 self.api.port,
709 id,
710 image.id),
711 "rel": "bookmark"
712 }
713 ]
714 }
715
716 response = Response(json.dumps({'server': s}), status=200, mimetype="application/json")
717 response.headers['Access-Control-Allow-Origin'] = '*'
718 return response
719
720 except Exception as ex:
721 LOG.exception(u"%s: Could not retrieve the server details." % __name__)
722 return ex.message, 500
723
724 def delete(self, id, serverid):
725 """
726 Delete a server instance.
727
728 :param id: tenant id, we ignore this most of the time
729 :type id: ``str``
730 :param serverid: The UUID of the server
731 :type serverid: ``str``
732 :return: Returns 200 if everything is fine.
733 :rtype: :class:`flask.response`
734 """
735 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
736 try:
737 server = self.api.compute.find_server_by_name_or_id(serverid)
738 if server is None:
739 return Response('Could not find server.', status=404, mimetype="application/json")
740
741 self.api.compute.stop_compute(server)
742
743 response = Response('Server deleted.', status=204, mimetype="application/json")
744 response.headers['Access-Control-Allow-Origin'] = '*'
745 return response
746
747 except Exception as ex:
748 LOG.exception(u"%s: Could not create the server." % __name__)
749 return ex.message, 500
750
751
752 class NovaInterfaceToServer(Resource):
753 def __init__(self, api):
754 self.api = api
755
756 def post(self, id, serverid):
757 """
758 Add an interface to the specified server.
759
760 :param id: tenant id, we ignore this most of the time
761 :type id: ``str``
762 :param serverid: Specifies the server.
763 :type serverid: ``str``
764 :return: Returns a flask response with information about the attached interface.
765 :rtype: :class:`flask.response`
766 """
767 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
768 try:
769 server = self.api.compute.find_server_by_name_or_id(serverid)
770 if server is None:
771 return Response("Server with id or name %s does not exists." % serverid, status=404)
772
773 if server.emulator_compute is None:
774 LOG.error("The targeted container does not exist.")
775 return Response("The targeted container of %s does not exist." % serverid, status=404)
776 data = json.loads(request.data).get("interfaceAttachment")
777 resp = dict()
778 port = data.get("port_id", None)
779 net = data.get("net_id", None)
780 dc = self.api.compute.dc
781 network_dict = dict()
782 network = None
783
784 if net is not None and port is not None:
785 port = self.api.compute.find_port_by_name_or_id(port)
786 network = self.api.compute.find_network_by_name_or_id(net)
787 network_dict['id'] = port.intf_name
788 network_dict['ip'] = port.ip_address
789 network_dict[network_dict['id']] = network.name
790 elif net is not None:
791 network = self.api.compute.find_network_by_name_or_id(net)
792 if network is None:
793 return Response("Network with id or name %s does not exists." % net, status=404)
794 port = self.api.compute.create_port("port:cp%s:fl:%s" %
795 (len(self.api.compute.ports), str(uuid.uuid4())))
796
797 port.net_name = network.name
798 port.ip_address = network.get_new_ip_address(port.name)
799 network_dict['id'] = port.intf_name
800 network_dict['ip'] = port.ip_address
801 network_dict[network_dict['id']] = network.name
802 elif port is not None:
803 port = self.api.compute.find_port_by_name_or_id(port)
804 network_dict['id'] = port.intf_name
805 network_dict['ip'] = port.ip_address
806 network = self.api.compute.find_network_by_name_or_id(port.net_name)
807 network_dict[network_dict['id']] = network.name
808 else:
809 raise Exception("You can only attach interfaces by port or network at the moment")
810
811 if network == self.api.manage.floating_network:
812 dc.net.addLink(server.emulator_compute, self.api.manage.floating_switch,
813 params1=network_dict, cls=Link, intfName1=port.intf_name)
814 else:
815 dc.net.addLink(server.emulator_compute, dc.switch,
816 params1=network_dict, cls=Link, intfName1=port.intf_name)
817 resp["port_state"] = "ACTIVE"
818 resp["port_id"] = port.id
819 resp["net_id"] = self.api.compute.find_network_by_name_or_id(port.net_name).id
820 resp["mac_addr"] = port.mac_address
821 resp["fixed_ips"] = list()
822 fixed_ips = dict()
823 fixed_ips["ip_address"] = port.ip_address
824 fixed_ips["subnet_id"] = network.subnet_name
825 resp["fixed_ips"].append(fixed_ips)
826 response = Response(json.dumps({"interfaceAttachment": resp}), status=202, mimetype="application/json")
827 response.headers['Access-Control-Allow-Origin'] = '*'
828 return response
829
830 except Exception as ex:
831 LOG.exception(u"%s: Could not add interface to the server." % __name__)
832 return ex.message, 500
833
834
835 class NovaShowAndDeleteInterfaceAtServer(Resource):
836 def __init__(self, api):
837 self.api = api
838
839 def delete(self, id, serverid, port_id):
840 """
841 Deletes an existing interface.
842
843 :param id: tenant id, we ignore this most of the time
844 :type id: ``str``
845 :param serverid: Specifies the server, where the interface will be deleted.
846 :type serverid: ``str``
847 :param port_id: Specifies the port of the interface.
848 :type port_id: ``str``
849 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
850 error message.
851 :rtype: :class:`flask.response`
852 """
853 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
854 try:
855 server = self.api.compute.find_server_by_name_or_id(serverid)
856 if server is None:
857 return Response("Server with id or name %s does not exists." % serverid, status=404)
858 port = self.api.compute.find_port_by_name_or_id(port_id)
859 if port is None:
860 return Response("Port with id or name %s does not exists." % port_id, status=404)
861
862 for link in self.api.compute.dc.net.links:
863 if str(link.intf1) == port.intf_name and \
864 str(link.intf1.ip) == port.ip_address.split('/')[0]:
865 self.api.compute.dc.net.removeLink(link)
866 break
867
868 response = Response("", status=202, mimetype="application/json")
869 response.headers['Access-Control-Allow-Origin'] = '*'
870 return response
871
872 except Exception as ex:
873 LOG.exception(u"%s: Could not detach interface from the server." % __name__)
874 return ex.message, 500
875
876
877 class NovaLimits(Resource):
878 def __init__(self, api):
879 self.api = api
880
881 def get(self, id):
882 """
883 Returns the resource limits of the emulated cloud.
884 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
885
886 TODO: For now we only return fixed limits, not based on the real deployment.
887
888 :param id: tenant id, used for the 'href' link
889 :type id: ``str``
890 :return: Returns the resource limits.
891 :rtype: :class:`flask.response`
892 """
893 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
894 try:
895 resp = {
896 "limits": {
897 "absolute": {
898 "maxImageMeta": 12800,
899 "maxPersonality": 500,
900 "maxPersonalitySize": 1024000,
901 "maxSecurityGroupRules": 2000,
902 "maxSecurityGroups": 1000,
903 "maxServerMeta": 12800,
904 "maxTotalCores": 2000,
905 "maxTotalFloatingIps": 1000,
906 "maxTotalInstances": 1000,
907 "maxTotalKeypairs": 1000,
908 "maxTotalRAMSize": 5120000,
909 "maxServerGroups": 1000,
910 "maxServerGroupMembers": 1000,
911 "totalCoresUsed": 0,
912 "totalInstancesUsed": 0,
913 "totalRAMUsed": 0,
914 "totalSecurityGroupsUsed": 0,
915 "totalFloatingIpsUsed": 0,
916 "totalServerGroupsUsed": 0
917 },
918 "rate": []
919 }
920 }
921 response = Response(json.dumps(resp), status=200, mimetype="application/json")
922 response.headers['Access-Control-Allow-Origin'] = '*'
923 return response
924
925 except Exception as ex:
926 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
927 return ex.message, 500