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