Added '/limits' endpoint to Nova API
[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
524 class NovaListImages(Resource):
525 def __init__(self, api):
526 self.api = api
527
528 def get(self, id):
529 """
530 Creates a list of all usable images.
531
532 :param id: tenant id, used for the 'href' link
533 :type id: ``str``
534 :return: Returns a flask response with a list of available images.
535 :rtype: :class:`flask.response`
536 """
537 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
538 try:
539 resp = dict()
540 resp['images'] = list()
541 for image in self.api.compute.images.values():
542 f = dict()
543 f['id'] = image.id
544 f['name'] = str(image.name).replace(":latest", "")
545 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
546 self.api.port,
547 id,
548 image.id)}]
549 resp['images'].append(f)
550 response = Response(json.dumps(resp), status=200, mimetype="application/json")
551 response.headers['Access-Control-Allow-Origin'] = '*'
552 return response
553
554 except Exception as ex:
555 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
556 return ex.message, 500
557
558
559 class NovaListImagesDetails(Resource):
560 def __init__(self, api):
561 self.api = api
562
563 def get(self, id):
564 """
565 As List Images but with additional metadata.
566
567 :param id: tenant id, used for the 'href' link
568 :type id: ``str``
569 :return: Returns a flask response with a list of images and their metadata.
570 :rtype: :class:`flask.response`
571 """
572 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
573 try:
574 resp = dict()
575 resp['images'] = list()
576 for image in self.api.compute.images.values():
577 # use the class dict. it should work fine
578 # but use a copy so we don't modifiy the original
579 f = image.__dict__.copy()
580 # add additional expected stuff stay openstack compatible
581 f['name'] = str(image.name).replace(":latest", "")
582 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
583 self.api.port,
584 id,
585 image.id)}]
586 f['metadata'] = {
587 "architecture": "x86_64",
588 "auto_disk_config": "True",
589 "kernel_id": "nokernel",
590 "ramdisk_id": "nokernel"
591 }
592 resp['images'].append(f)
593
594 response = Response(json.dumps(resp), status=200, mimetype="application/json")
595 response.headers['Access-Control-Allow-Origin'] = '*'
596 return response
597
598 except Exception as ex:
599 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
600 return ex.message, 500
601
602
603 class NovaListImageById(Resource):
604 def __init__(self, api):
605 self.api = api
606
607 def get(self, id, imageid):
608 """
609 Gets an image by id from the emulator with openstack nova compliant return values.
610
611 :param id: tenantid, we ignore this most of the time
612 :type id: ``str``
613 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
614 :type imageid: ``str``
615 :return: Returns a flask response with the information about one image.
616 :rtype: :class:`flask.response`
617 """
618 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
619 try:
620 resp = dict()
621 i = resp['image'] = dict()
622 for image in self.api.compute.images.values():
623 if image.id == imageid or image.name == imageid:
624 i['id'] = image.id
625 i['name'] = image.name
626
627 return Response(json.dumps(resp), status=200, mimetype="application/json")
628
629 response = Response("Image with id or name %s does not exists." % imageid, status=404)
630 response.headers['Access-Control-Allow-Origin'] = '*'
631 return response
632
633 except Exception as ex:
634 LOG.exception(u"%s: Could not retrieve image with id %s." % (__name__, imageid))
635 return ex.message, 500
636
637
638 class NovaShowServerDetails(Resource):
639 def __init__(self, api):
640 self.api = api
641
642 def get(self, id, serverid):
643 """
644 Returns detailed information about the specified server.
645
646 :param id: tenant id, used for the 'href' link
647 :type id: ``str``
648 :param serverid: Specifies the requested server.
649 :type serverid: ``str``
650 :return: Returns a flask response with details about the server.
651 :rtype: :class:`flask.response`
652 """
653 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
654 try:
655 server = self.api.compute.find_server_by_name_or_id(serverid)
656 if server is None:
657 return Response("Server with id or name %s does not exists." % serverid, status=404)
658 s = server.create_server_dict()
659 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (self.api.ip,
660 self.api.port,
661 id,
662 server.id)}]
663
664 flavor = self.api.compute.flavors[server.flavor]
665 s['flavor'] = {
666 "id": flavor.id,
667 "links": [
668 {
669 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
670 self.api.port,
671 id,
672 flavor.id),
673 "rel": "bookmark"
674 }
675 ]
676 }
677 image = self.api.compute.images[server.image]
678 s['image'] = {
679 "id": image.id,
680 "links": [
681 {
682 "href": "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
683 self.api.port,
684 id,
685 image.id),
686 "rel": "bookmark"
687 }
688 ]
689 }
690
691 response = Response(json.dumps({'server': s}), status=200, mimetype="application/json")
692 response.headers['Access-Control-Allow-Origin'] = '*'
693 return response
694
695 except Exception as ex:
696 LOG.exception(u"%s: Could not retrieve the server details." % __name__)
697 return ex.message, 500
698
699 def delete(self, id, serverid):
700 """
701 Delete a server instance.
702
703 :param id: tenant id, we ignore this most of the time
704 :type id: ``str``
705 :param serverid: The UUID of the server
706 :type serverid: ``str``
707 :return: Returns 200 if everything is fine.
708 :rtype: :class:`flask.response`
709 """
710 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
711 try:
712 server = self.api.compute.find_server_by_name_or_id(serverid)
713 if server is None:
714 return Response('Could not find server.', status=404, mimetype="application/json")
715
716 self.api.compute.stop_compute(server)
717
718 response = Response('Server deleted.', status=204, mimetype="application/json")
719 response.headers['Access-Control-Allow-Origin'] = '*'
720 return response
721
722 except Exception as ex:
723 LOG.exception(u"%s: Could not create the server." % __name__)
724 return ex.message, 500
725
726
727 class NovaInterfaceToServer(Resource):
728 def __init__(self, api):
729 self.api = api
730
731 def post(self, id, serverid):
732 """
733 Add an interface to the specified server.
734
735 :param id: tenant id, we ignore this most of the time
736 :type id: ``str``
737 :param serverid: Specifies the server.
738 :type serverid: ``str``
739 :return: Returns a flask response with information about the attached interface.
740 :rtype: :class:`flask.response`
741 """
742 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
743 try:
744 server = self.api.compute.find_server_by_name_or_id(serverid)
745 if server is None:
746 return Response("Server with id or name %s does not exists." % serverid, status=404)
747
748 if server.emulator_compute is None:
749 LOG.error("The targeted container does not exist.")
750 return Response("The targeted container of %s does not exist." % serverid, status=404)
751 data = json.loads(request.data).get("interfaceAttachment")
752 resp = dict()
753 port = data.get("port_id", None)
754 net = data.get("net_id", None)
755 dc = self.api.compute.dc
756 network_dict = dict()
757 network = None
758
759 if net is not None and port is not None:
760 port = self.api.compute.find_port_by_name_or_id(port)
761 network = self.api.compute.find_network_by_name_or_id(net)
762 network_dict['id'] = port.intf_name
763 network_dict['ip'] = port.ip_address
764 network_dict[network_dict['id']] = network.name
765 elif net is not None:
766 network = self.api.compute.find_network_by_name_or_id(net)
767 if network is None:
768 return Response("Network with id or name %s does not exists." % net, status=404)
769 port = self.api.compute.create_port("port:cp%s:fl:%s" %
770 (len(self.api.compute.ports), str(uuid.uuid4())))
771
772 port.net_name = network.name
773 port.ip_address = network.get_new_ip_address(port.name)
774 network_dict['id'] = port.intf_name
775 network_dict['ip'] = port.ip_address
776 network_dict[network_dict['id']] = network.name
777 elif port is not None:
778 port = self.api.compute.find_port_by_name_or_id(port)
779 network_dict['id'] = port.intf_name
780 network_dict['ip'] = port.ip_address
781 network = self.api.compute.find_network_by_name_or_id(port.net_name)
782 network_dict[network_dict['id']] = network.name
783 else:
784 raise Exception("You can only attach interfaces by port or network at the moment")
785
786 if network == self.api.manage.floating_network:
787 dc.net.addLink(server.emulator_compute, self.api.manage.floating_switch,
788 params1=network_dict, cls=Link, intfName1=port.intf_name)
789 else:
790 dc.net.addLink(server.emulator_compute, dc.switch,
791 params1=network_dict, cls=Link, intfName1=port.intf_name)
792 resp["port_state"] = "ACTIVE"
793 resp["port_id"] = port.id
794 resp["net_id"] = self.api.compute.find_network_by_name_or_id(port.net_name).id
795 resp["mac_addr"] = port.mac_address
796 resp["fixed_ips"] = list()
797 fixed_ips = dict()
798 fixed_ips["ip_address"] = port.ip_address
799 fixed_ips["subnet_id"] = network.subnet_name
800 resp["fixed_ips"].append(fixed_ips)
801 response = Response(json.dumps({"interfaceAttachment": resp}), status=202, mimetype="application/json")
802 response.headers['Access-Control-Allow-Origin'] = '*'
803 return response
804
805 except Exception as ex:
806 LOG.exception(u"%s: Could not add interface to the server." % __name__)
807 return ex.message, 500
808
809
810 class NovaShowAndDeleteInterfaceAtServer(Resource):
811 def __init__(self, api):
812 self.api = api
813
814 def delete(self, id, serverid, port_id):
815 """
816 Deletes an existing interface.
817
818 :param id: tenant id, we ignore this most of the time
819 :type id: ``str``
820 :param serverid: Specifies the server, where the interface will be deleted.
821 :type serverid: ``str``
822 :param port_id: Specifies the port of the interface.
823 :type port_id: ``str``
824 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
825 error message.
826 :rtype: :class:`flask.response`
827 """
828 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
829 try:
830 server = self.api.compute.find_server_by_name_or_id(serverid)
831 if server is None:
832 return Response("Server with id or name %s does not exists." % serverid, status=404)
833 port = self.api.compute.find_port_by_name_or_id(port_id)
834 if port is None:
835 return Response("Port with id or name %s does not exists." % port_id, status=404)
836
837 for link in self.api.compute.dc.net.links:
838 if str(link.intf1) == port.intf_name and \
839 str(link.intf1.ip) == port.ip_address.split('/')[0]:
840 self.api.compute.dc.net.removeLink(link)
841 break
842
843 response = Response("", status=202, mimetype="application/json")
844 response.headers['Access-Control-Allow-Origin'] = '*'
845 return response
846
847 except Exception as ex:
848 LOG.exception(u"%s: Could not detach interface from the server." % __name__)
849 return ex.message, 500
850
851
852 class NovaLimits(Resource):
853 def __init__(self, api):
854 self.api = api
855
856 def get(self, id):
857 """
858 Returns the resource limits of the emulated cloud.
859 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
860
861 TODO: For now we only return fixed limits, not based on the real deployment.
862
863 :param id: tenant id, used for the 'href' link
864 :type id: ``str``
865 :return: Returns the resource limits.
866 :rtype: :class:`flask.response`
867 """
868 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
869 try:
870 resp = {
871 "limits": {
872 "absolute": {
873 "maxImageMeta": 12800,
874 "maxPersonality": 500,
875 "maxPersonalitySize": 1024000,
876 "maxSecurityGroupRules": 2000,
877 "maxSecurityGroups": 1000,
878 "maxServerMeta": 12800,
879 "maxTotalCores": 2000,
880 "maxTotalFloatingIps": 1000,
881 "maxTotalInstances": 1000,
882 "maxTotalKeypairs": 1000,
883 "maxTotalRAMSize": 5120000,
884 "maxServerGroups": 1000,
885 "maxServerGroupMembers": 1000,
886 "totalCoresUsed": 0,
887 "totalInstancesUsed": 0,
888 "totalRAMUsed": 0,
889 "totalSecurityGroupsUsed": 0,
890 "totalFloatingIpsUsed": 0,
891 "totalServerGroupsUsed": 0
892 },
893 "rate": []
894 }
895 }
896 response = Response(json.dumps(resp), status=200, mimetype="application/json")
897 response.headers['Access-Control-Allow-Origin'] = '*'
898 return response
899
900 except Exception as ex:
901 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
902 return ex.message, 500