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