Fix: Instantiate the same NS without emulator restart
[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 LOG.error("Server with name %s already exists. 409" % name)
230 return Response("Server with name %s already exists." % name, status=409)
231 # TODO: not finished!
232 resp = dict()
233
234 server = self.api.compute.create_server(name)
235 server.full_name = str(self.api.compute.dc.label) + "_" + server_dict["name"]
236 server.template_name = server_dict["name"]
237 if "metadata" in server_dict:
238 server.properties = server_dict["metadata"]
239
240 for flavor in self.api.compute.flavors.values():
241 if flavor.id == server_dict.get('flavorRef', ''):
242 server.flavor = flavor.name
243 for image in self.api.compute.images.values():
244 if image.id in server_dict['imageRef']:
245 server.image = image.name
246
247 if networks is not None:
248 for net in networks:
249 port = self.api.compute.find_port_by_name_or_id(net.get('port', ""))
250 if port is not None:
251 server.port_names.append(port.name)
252 else:
253 return Response("Currently only networking by port is supported.", status=400)
254
255 self.api.compute._start_compute(server)
256
257 response = NovaShowServerDetails(self.api).get(id, server.id)
258 response.headers['Access-Control-Allow-Origin'] = '*'
259 return response
260
261 except Exception as ex:
262 LOG.exception(u"%s: Could not create the server." % __name__)
263 return ex.message, 500
264
265
266 class NovaListServersAndPortsApi(Resource):
267 def __init__(self, api):
268 self.api = api
269
270 def get(self, id):
271 """
272 Creates a list with all running servers and their detailed information. This function also presents all
273 port information of each server.
274
275 :param id: Used to create a individual link to quarry further information.
276 :type id: ``str``
277 :return: Returns a json response with a dictionary that contains the server information.
278 :rtype: :class:`flask.response`
279 """
280 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
281
282 try:
283 resp = dict()
284 resp['servers'] = list()
285 for server in self.api.compute.computeUnits.values():
286 s = server.create_server_dict(self.api.compute)
287 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
288 self.api.port,
289 id,
290 server.id)}]
291
292 s['ports'] = list()
293 for port_name in server.port_names:
294 port = self.api.compute.find_port_by_name_or_id(port_name)
295 if port is None:
296 continue
297
298 tmp = port.create_port_dict(self.api.compute)
299 tmp['intf_name'] = port.intf_name
300 s['ports'].append(tmp)
301
302 resp['servers'].append(s)
303
304 response = Response(json.dumps(resp), status=200, mimetype="application/json")
305 response.headers['Access-Control-Allow-Origin'] = '*'
306 return response
307
308 except Exception as ex:
309 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
310 return ex.message, 500
311
312
313 class NovaListServersDetailed(Resource):
314 def __init__(self, api):
315 self.api = api
316
317 def get(self, id):
318 """
319 As List Servers, it lists all running servers and their details but furthermore it also states the
320 used flavor and the server image.
321
322 :param id: tenant id, used for the 'href' link.
323 :type id: ``str``
324 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
325 :rtype: :class:`flask.response`
326 """
327 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
328
329 try:
330 resp = {"servers": list()}
331 for server in self.api.compute.computeUnits.values():
332 s = server.create_server_dict(self.api.compute)
333 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
334 self.api.port,
335 id,
336 server.id)}]
337 flavor = self.api.compute.flavors[server.flavor]
338 s['flavor'] = {
339 "id": flavor.id,
340 "links": [
341 {
342 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
343 self.api.port,
344 id,
345 flavor.id),
346 "rel": "bookmark"
347 }
348 ]
349 }
350 image = self.api.compute.images[server.image]
351 s['image'] = {
352 "id": image.id,
353 "links": [
354 {
355 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
356 self.api.port,
357 id,
358 image.id),
359 "rel": "bookmark"
360 }
361 ]
362 }
363
364 resp['servers'].append(s)
365
366 response = Response(json.dumps(resp), status=200, mimetype="application/json")
367 response.headers['Access-Control-Allow-Origin'] = '*'
368 return response
369
370 except Exception as ex:
371 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
372 return ex.message, 500
373
374
375 class NovaListFlavors(Resource):
376 def __init__(self, api):
377 self.api = api
378
379 def get(self, id):
380 """
381 Lists all available flavors.
382
383 :param id: tenant id, used for the 'href' link
384 :type id: ``str``
385 :return: Returns a flask response with a list of all flavors.
386 :rtype: :class:`flask.response`
387 """
388 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
389 try:
390 resp = dict()
391 resp['flavors'] = list()
392 for flavor in self.api.compute.flavors.values():
393 f = flavor.__dict__.copy()
394 f['id'] = flavor.id
395 f['name'] = flavor.name
396 f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
397 self.api.port,
398 id,
399 flavor.id)}]
400 resp['flavors'].append(f)
401
402 response = Response(json.dumps(resp), status=200, mimetype="application/json")
403 response.headers['Access-Control-Allow-Origin'] = '*'
404 return response
405
406 except Exception as ex:
407 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
408 return ex.message, 500
409
410 def post(self, id):
411 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
412 data = json.loads(request.data).get("flavor")
413 LOG.warning("Create Flavor: %s" % str(data))
414 # add to internal dict
415 f = self.api.compute.add_flavor(
416 data.get("name"),
417 data.get("vcpus"),
418 data.get("ram"), "MB",
419 data.get("disk"), "GB")
420 # create response based on incoming data
421 data["id"] = f.id
422 data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
423 self.api.port,
424 id,
425 f.id)}]
426 resp = {"flavor": data}
427 return Response(json.dumps(resp), status=200, mimetype="application/json")
428
429
430 class NovaListFlavorsDetails(Resource):
431 def __init__(self, api):
432 self.api = api
433
434 def get(self, id):
435 """
436 Lists all flavors with additional information like ram and disk space.
437
438 :param id: tenant id, used for the 'href' link
439 :type id: ``str``
440 :return: Returns a flask response with a list of all flavors with additional information.
441 :rtype: :class:`flask.response`
442 """
443 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
444 try:
445 resp = dict()
446 resp['flavors'] = list()
447 for flavor in self.api.compute.flavors.values():
448 # use the class dict. it should work fine
449 # but use a copy so we don't modifiy the original
450 f = flavor.__dict__.copy()
451 # add additional expected stuff stay openstack compatible
452 f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
453 self.api.port,
454 id,
455 flavor.id)}]
456 f['OS-FLV-DISABLED:disabled'] = False
457 f['OS-FLV-EXT-DATA:ephemeral'] = 0
458 f['os-flavor-access:is_public'] = True
459 f['ram'] = flavor.memory
460 f['vcpus'] = flavor.cpu
461 f['swap'] = 0
462 f['disk'] = flavor.storage
463 f['rxtx_factor'] = 1.0
464 resp['flavors'].append(f)
465
466 response = Response(json.dumps(resp), status=200, mimetype="application/json")
467 response.headers['Access-Control-Allow-Origin'] = '*'
468 return response
469
470 except Exception as ex:
471 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
472 return ex.message, 500
473
474 def post(self, id):
475 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
476 data = json.loads(request.data).get("flavor")
477 LOG.warning("Create Flavor: %s" % str(data))
478 # add to internal dict
479 f = self.api.compute.add_flavor(
480 data.get("name"),
481 data.get("vcpus"),
482 data.get("ram"), "MB",
483 data.get("disk"), "GB")
484 # create response based on incoming data
485 data["id"] = f.id
486 data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
487 self.api.port,
488 id,
489 f.id)}]
490 resp = {"flavor": data}
491 return Response(json.dumps(resp), status=200, mimetype="application/json")
492
493
494 class NovaListFlavorById(Resource):
495 def __init__(self, api):
496 self.api = api
497
498 def get(self, id, flavorid):
499 """
500 Returns details about one flavor.
501
502 :param id: tenant id, used for the 'href' link
503 :type id: ``str``
504 :param flavorid: Represents the flavor.
505 :type flavorid: ``str``
506 :return: Returns a flask response with detailed information about the flavor.
507 :rtype: :class:`flask.response`
508 """
509 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
510 try:
511 resp = dict()
512 resp['flavor'] = dict()
513 flavor = self.api.compute.flavors.get(flavorid, None)
514 if flavor is None:
515 for f in self.api.compute.flavors.values():
516 if f.id == flavorid:
517 flavor = f
518 break
519 resp['flavor']['id'] = flavor.id
520 resp['flavor']['name'] = flavor.name
521 resp['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
522 self.api.port,
523 id,
524 flavor.id)}]
525 response = Response(json.dumps(resp), status=200, mimetype="application/json")
526 response.headers['Access-Control-Allow-Origin'] = '*'
527 return response
528
529 except Exception as ex:
530 LOG.exception(u"%s: Could not retrieve flavor with id %s" % (__name__, flavorid))
531 return ex.message, 500
532
533 def delete(self, id, flavorid):
534 """
535 Removes the given flavor.
536 Does not really remove anything from the machine, just fakes an OK.
537 """
538 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
539 return Response("", status=204, mimetype="application/json")
540
541
542 class NovaListImages(Resource):
543 def __init__(self, api):
544 self.api = api
545
546 def get(self, id):
547 """
548 Creates a list of all usable images.
549
550 :param id: tenant id, used for the 'href' link
551 :type id: ``str``
552 :return: Returns a flask response with a list of available images.
553 :rtype: :class:`flask.response`
554 """
555 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
556 try:
557 resp = dict()
558 resp['images'] = list()
559 for image in self.api.compute.images.values():
560 f = dict()
561 f['id'] = image.id
562 f['name'] = str(image.name).replace(":latest", "")
563 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
564 self.api.port,
565 id,
566 image.id)}]
567 resp['images'].append(f)
568 response = Response(json.dumps(resp), status=200, mimetype="application/json")
569 response.headers['Access-Control-Allow-Origin'] = '*'
570 return response
571
572 except Exception as ex:
573 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
574 return ex.message, 500
575
576
577 class NovaListImagesDetails(Resource):
578 def __init__(self, api):
579 self.api = api
580
581 def get(self, id):
582 """
583 As List Images but with additional metadata.
584
585 :param id: tenant id, used for the 'href' link
586 :type id: ``str``
587 :return: Returns a flask response with a list of images and their metadata.
588 :rtype: :class:`flask.response`
589 """
590 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
591 try:
592 resp = dict()
593 resp['images'] = list()
594 for image in self.api.compute.images.values():
595 # use the class dict. it should work fine
596 # but use a copy so we don't modifiy the original
597 f = image.__dict__.copy()
598 # add additional expected stuff stay openstack compatible
599 f['name'] = str(image.name).replace(":latest", "")
600 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
601 self.api.port,
602 id,
603 image.id)}]
604 f['metadata'] = {
605 "architecture": "x86_64",
606 "auto_disk_config": "True",
607 "kernel_id": "nokernel",
608 "ramdisk_id": "nokernel"
609 }
610 resp['images'].append(f)
611
612 response = Response(json.dumps(resp), status=200, mimetype="application/json")
613 response.headers['Access-Control-Allow-Origin'] = '*'
614 return response
615
616 except Exception as ex:
617 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
618 return ex.message, 500
619
620
621 class NovaListImageById(Resource):
622 def __init__(self, api):
623 self.api = api
624
625 def get(self, id, imageid):
626 """
627 Gets an image by id from the emulator with openstack nova compliant return values.
628
629 :param id: tenantid, we ignore this most of the time
630 :type id: ``str``
631 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
632 :type imageid: ``str``
633 :return: Returns a flask response with the information about one image.
634 :rtype: :class:`flask.response`
635 """
636 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
637 try:
638 resp = dict()
639 i = resp['image'] = dict()
640 for image in self.api.compute.images.values():
641 if image.id == imageid or image.name == imageid:
642 i['id'] = image.id
643 i['name'] = image.name
644
645 return Response(json.dumps(resp), status=200, mimetype="application/json")
646
647 response = Response("Image with id or name %s does not exists." % imageid, status=404)
648 response.headers['Access-Control-Allow-Origin'] = '*'
649 return response
650
651 except Exception as ex:
652 LOG.exception(u"%s: Could not retrieve image with id %s." % (__name__, imageid))
653 return ex.message, 500
654
655 def delete(self, id, imageid):
656 """
657 Removes the given image.
658 Does not really remove anything from the machine, just fakes an OK.
659 """
660 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
661 return Response("", status=204, mimetype="application/json")
662
663
664 class NovaShowServerDetails(Resource):
665 def __init__(self, api):
666 self.api = api
667
668 def get(self, id, serverid):
669 """
670 Returns detailed information about the specified server.
671
672 :param id: tenant id, used for the 'href' link
673 :type id: ``str``
674 :param serverid: Specifies the requested server.
675 :type serverid: ``str``
676 :return: Returns a flask response with details about the server.
677 :rtype: :class:`flask.response`
678 """
679 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
680 try:
681 server = self.api.compute.find_server_by_name_or_id(serverid)
682 if server is None:
683 return Response("Server with id or name %s does not exists." % serverid, status=404)
684 s = server.create_server_dict()
685 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
686 self.api.port,
687 id,
688 server.id)}]
689
690 flavor = self.api.compute.flavors[server.flavor]
691 s['flavor'] = {
692 "id": flavor.id,
693 "links": [
694 {
695 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
696 self.api.port,
697 id,
698 flavor.id),
699 "rel": "bookmark"
700 }
701 ]
702 }
703 image = self.api.compute.images[server.image]
704 s['image'] = {
705 "id": image.id,
706 "links": [
707 {
708 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
709 self.api.port,
710 id,
711 image.id),
712 "rel": "bookmark"
713 }
714 ]
715 }
716
717 response = Response(json.dumps({'server': s}), status=200, mimetype="application/json")
718 response.headers['Access-Control-Allow-Origin'] = '*'
719 return response
720
721 except Exception as ex:
722 LOG.exception(u"%s: Could not retrieve the server details." % __name__)
723 return ex.message, 500
724
725 def delete(self, id, serverid):
726 """
727 Delete a server instance.
728
729 :param id: tenant id, we ignore this most of the time
730 :type id: ``str``
731 :param serverid: The UUID of the server
732 :type serverid: ``str``
733 :return: Returns 204 if everything is fine.
734 :rtype: :class:`flask.response`
735 """
736 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
737 try:
738 server = self.api.compute.find_server_by_name_or_id(serverid)
739 if server is None:
740 return Response('Could not find server.', status=404, mimetype="application/json")
741
742 self.api.compute.stop_compute(server)
743
744 response = Response('', status=204, mimetype="application/json")
745 response.headers['Access-Control-Allow-Origin'] = '*'
746 return response
747
748 except Exception as ex:
749 LOG.exception(u"%s: Could not create the server." % __name__)
750 return ex.message, 500
751
752
753 class NovaInterfaceToServer(Resource):
754 def __init__(self, api):
755 self.api = api
756
757 def post(self, id, serverid):
758 """
759 Add an interface to the specified server.
760
761 :param id: tenant id, we ignore this most of the time
762 :type id: ``str``
763 :param serverid: Specifies the server.
764 :type serverid: ``str``
765 :return: Returns a flask response with information about the attached interface.
766 :rtype: :class:`flask.response`
767 """
768 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
769 try:
770 server = self.api.compute.find_server_by_name_or_id(serverid)
771 if server is None:
772 return Response("Server with id or name %s does not exists." % serverid, status=404)
773
774 if server.emulator_compute is None:
775 LOG.error("The targeted container does not exist.")
776 return Response("The targeted container of %s does not exist." % serverid, status=404)
777 data = json.loads(request.data).get("interfaceAttachment")
778 resp = dict()
779 port = data.get("port_id", None)
780 net = data.get("net_id", None)
781 dc = self.api.compute.dc
782 network_dict = dict()
783 network = None
784
785 if net is not None and port is not None:
786 port = self.api.compute.find_port_by_name_or_id(port)
787 network = self.api.compute.find_network_by_name_or_id(net)
788 network_dict['id'] = port.intf_name
789 network_dict['ip'] = port.ip_address
790 network_dict[network_dict['id']] = network.name
791 elif net is not None:
792 network = self.api.compute.find_network_by_name_or_id(net)
793 if network is None:
794 return Response("Network with id or name %s does not exists." % net, status=404)
795 port = self.api.compute.create_port("port:cp%s:fl:%s" %
796 (len(self.api.compute.ports), str(uuid.uuid4())))
797
798 port.net_name = network.name
799 port.ip_address = network.get_new_ip_address(port.name)
800 network_dict['id'] = port.intf_name
801 network_dict['ip'] = port.ip_address
802 network_dict[network_dict['id']] = network.name
803 elif port is not None:
804 port = self.api.compute.find_port_by_name_or_id(port)
805 network_dict['id'] = port.intf_name
806 network_dict['ip'] = port.ip_address
807 network = self.api.compute.find_network_by_name_or_id(port.net_name)
808 network_dict[network_dict['id']] = network.name
809 else:
810 raise Exception("You can only attach interfaces by port or network at the moment")
811
812 if network == self.api.manage.floating_network:
813 dc.net.addLink(server.emulator_compute, self.api.manage.floating_switch,
814 params1=network_dict, cls=Link, intfName1=port.intf_name)
815 else:
816 dc.net.addLink(server.emulator_compute, dc.switch,
817 params1=network_dict, cls=Link, intfName1=port.intf_name)
818 resp["port_state"] = "ACTIVE"
819 resp["port_id"] = port.id
820 resp["net_id"] = self.api.compute.find_network_by_name_or_id(port.net_name).id
821 resp["mac_addr"] = port.mac_address
822 resp["fixed_ips"] = list()
823 fixed_ips = dict()
824 fixed_ips["ip_address"] = port.ip_address
825 fixed_ips["subnet_id"] = network.subnet_name
826 resp["fixed_ips"].append(fixed_ips)
827 response = Response(json.dumps({"interfaceAttachment": resp}), status=202, mimetype="application/json")
828 response.headers['Access-Control-Allow-Origin'] = '*'
829 return response
830
831 except Exception as ex:
832 LOG.exception(u"%s: Could not add interface to the server." % __name__)
833 return ex.message, 500
834
835
836 class NovaShowAndDeleteInterfaceAtServer(Resource):
837 def __init__(self, api):
838 self.api = api
839
840 def delete(self, id, serverid, port_id):
841 """
842 Deletes an existing interface.
843
844 :param id: tenant id, we ignore this most of the time
845 :type id: ``str``
846 :param serverid: Specifies the server, where the interface will be deleted.
847 :type serverid: ``str``
848 :param port_id: Specifies the port of the interface.
849 :type port_id: ``str``
850 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
851 error message.
852 :rtype: :class:`flask.response`
853 """
854 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
855 try:
856 server = self.api.compute.find_server_by_name_or_id(serverid)
857 if server is None:
858 return Response("Server with id or name %s does not exists." % serverid, status=404)
859 port = self.api.compute.find_port_by_name_or_id(port_id)
860 if port is None:
861 return Response("Port with id or name %s does not exists." % port_id, status=404)
862
863 for link in self.api.compute.dc.net.links:
864 if str(link.intf1) == port.intf_name and \
865 str(link.intf1.ip) == port.ip_address.split('/')[0]:
866 self.api.compute.dc.net.removeLink(link)
867 break
868
869 response = Response("", status=202, mimetype="application/json")
870 response.headers['Access-Control-Allow-Origin'] = '*'
871 return response
872
873 except Exception as ex:
874 LOG.exception(u"%s: Could not detach interface from the server." % __name__)
875 return ex.message, 500
876
877
878 class NovaLimits(Resource):
879 def __init__(self, api):
880 self.api = api
881
882 def get(self, id):
883 """
884 Returns the resource limits of the emulated cloud.
885 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
886
887 TODO: For now we only return fixed limits, not based on the real deployment.
888
889 :param id: tenant id, used for the 'href' link
890 :type id: ``str``
891 :return: Returns the resource limits.
892 :rtype: :class:`flask.response`
893 """
894 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
895 try:
896 resp = {
897 "limits": {
898 "absolute": {
899 "maxImageMeta": 12800,
900 "maxPersonality": 500,
901 "maxPersonalitySize": 1024000,
902 "maxSecurityGroupRules": 2000,
903 "maxSecurityGroups": 1000,
904 "maxServerMeta": 12800,
905 "maxTotalCores": 2000,
906 "maxTotalFloatingIps": 1000,
907 "maxTotalInstances": 1000,
908 "maxTotalKeypairs": 1000,
909 "maxTotalRAMSize": 5120000,
910 "maxServerGroups": 1000,
911 "maxServerGroupMembers": 1000,
912 "totalCoresUsed": 0,
913 "totalInstancesUsed": 0,
914 "totalRAMUsed": 0,
915 "totalSecurityGroupsUsed": 0,
916 "totalFloatingIpsUsed": 0,
917 "totalServerGroupsUsed": 0
918 },
919 "rate": []
920 }
921 }
922 response = Response(json.dumps(resp), status=200, mimetype="application/json")
923 response.headers['Access-Control-Allow-Origin'] = '*'
924 return response
925
926 except Exception as ex:
927 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
928 return ex.message, 500