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