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