Merge pull request #241 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 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 if "metadata" in server_dict:
230 server.properties = server_dict["metadata"]
231
232 for flavor in self.api.compute.flavors.values():
233 if flavor.id == server_dict.get('flavorRef', ''):
234 server.flavor = flavor.name
235 for image in self.api.compute.images.values():
236 if image.id in server_dict['imageRef']:
237 server.image = image.name
238
239 if networks is not None:
240 for net in networks:
241 port = self.api.compute.find_port_by_name_or_id(net.get('port', ""))
242 if port is not None:
243 server.port_names.append(port.name)
244 else:
245 return Response("Currently only networking by port is supported.", status=400)
246
247 self.api.compute._start_compute(server)
248
249 response = NovaShowServerDetails(self.api).get(id, server.id)
250 response.headers['Access-Control-Allow-Origin'] = '*'
251 return response
252
253 except Exception as ex:
254 LOG.exception(u"%s: Could not create the server." % __name__)
255 return ex.message, 500
256
257
258 class NovaListServersAndPortsApi(Resource):
259 def __init__(self, api):
260 self.api = api
261
262 def get(self, id):
263 """
264 Creates a list with all running servers and their detailed information. This function also presents all
265 port information of each server.
266
267 :param id: Used to create a individual link to quarry further information.
268 :type id: ``str``
269 :return: Returns a json response with a dictionary that contains the server information.
270 :rtype: :class:`flask.response`
271 """
272 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
273
274 try:
275 resp = dict()
276 resp['servers'] = list()
277 for server in self.api.compute.computeUnits.values():
278 s = server.create_server_dict(self.api.compute)
279 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (self.api.ip,
280 self.api.port,
281 id,
282 server.id)}]
283
284 s['ports'] = list()
285 for port_name in server.port_names:
286 port = self.api.compute.find_port_by_name_or_id(port_name)
287 if port is None:
288 continue
289
290 tmp = port.create_port_dict(self.api.compute)
291 tmp['intf_name'] = port.intf_name
292 s['ports'].append(tmp)
293
294 resp['servers'].append(s)
295
296 response = Response(json.dumps(resp), status=200, mimetype="application/json")
297 response.headers['Access-Control-Allow-Origin'] = '*'
298 return response
299
300 except Exception as ex:
301 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
302 return ex.message, 500
303
304
305 class NovaListServersDetailed(Resource):
306 def __init__(self, api):
307 self.api = api
308
309 def get(self, id):
310 """
311 As List Servers, it lists all running servers and their details but furthermore it also states the
312 used flavor and the server image.
313
314 :param id: tenant id, used for the 'href' link.
315 :type id: ``str``
316 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
317 :rtype: :class:`flask.response`
318 """
319 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
320
321 try:
322 resp = {"servers": list()}
323 for server in self.api.compute.computeUnits.values():
324 s = server.create_server_dict(self.api.compute)
325 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (self.api.ip,
326 self.api.port,
327 id,
328 server.id)}]
329 flavor = self.api.compute.flavors[server.flavor]
330 s['flavor'] = {
331 "id": flavor.id,
332 "links": [
333 {
334 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
335 self.api.port,
336 id,
337 flavor.id),
338 "rel": "bookmark"
339 }
340 ]
341 }
342 image = self.api.compute.images[server.image]
343 s['image'] = {
344 "id": image.id,
345 "links": [
346 {
347 "href": "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
348 self.api.port,
349 id,
350 image.id),
351 "rel": "bookmark"
352 }
353 ]
354 }
355
356 resp['servers'].append(s)
357
358 response = Response(json.dumps(resp), status=200, mimetype="application/json")
359 response.headers['Access-Control-Allow-Origin'] = '*'
360 return response
361
362 except Exception as ex:
363 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
364 return ex.message, 500
365
366
367 class NovaListFlavors(Resource):
368 def __init__(self, api):
369 self.api = api
370
371 def get(self, id):
372 """
373 Lists all available flavors.
374
375 :param id: tenant id, used for the 'href' link
376 :type id: ``str``
377 :return: Returns a flask response with a list of all flavors.
378 :rtype: :class:`flask.response`
379 """
380 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
381 try:
382 resp = dict()
383 resp['flavors'] = list()
384 for flavor in self.api.compute.flavors.values():
385 f = flavor.__dict__.copy()
386 f['id'] = flavor.id
387 f['name'] = flavor.name
388 f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
389 self.api.port,
390 id,
391 flavor.id)}]
392 resp['flavors'].append(f)
393
394 response = Response(json.dumps(resp), status=200, mimetype="application/json")
395 response.headers['Access-Control-Allow-Origin'] = '*'
396 return response
397
398 except Exception as ex:
399 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
400 return ex.message, 500
401
402 def post(self, id):
403 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
404 data = json.loads(request.data).get("flavor")
405 LOG.warning("Create Flavor: %s" % str(data))
406 # add to internal dict
407 f = self.api.compute.add_flavor(
408 data.get("name"),
409 data.get("vcpus"),
410 data.get("ram"), "MB",
411 data.get("disk"), "GB")
412 # create response based on incoming data
413 data["id"] = f.id
414 data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
415 self.api.port,
416 id,
417 f.id)}]
418 resp = {"flavor": data}
419 return Response(json.dumps(resp), status=200, mimetype="application/json")
420
421
422 class NovaListFlavorsDetails(Resource):
423 def __init__(self, api):
424 self.api = api
425
426 def get(self, id):
427 """
428 Lists all flavors with additional information like ram and disk space.
429
430 :param id: tenant id, used for the 'href' link
431 :type id: ``str``
432 :return: Returns a flask response with a list of all flavors with additional information.
433 :rtype: :class:`flask.response`
434 """
435 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
436 try:
437 resp = dict()
438 resp['flavors'] = list()
439 for flavor in self.api.compute.flavors.values():
440 # use the class dict. it should work fine
441 # but use a copy so we don't modifiy the original
442 f = flavor.__dict__.copy()
443 # add additional expected stuff stay openstack compatible
444 f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
445 self.api.port,
446 id,
447 flavor.id)}]
448 f['OS-FLV-DISABLED:disabled'] = False
449 f['OS-FLV-EXT-DATA:ephemeral'] = 0
450 f['os-flavor-access:is_public'] = True
451 f['ram'] = flavor.memory
452 f['vcpus'] = flavor.cpu
453 f['swap'] = 0
454 f['disk'] = flavor.storage
455 f['rxtx_factor'] = 1.0
456 resp['flavors'].append(f)
457
458 response = Response(json.dumps(resp), status=200, mimetype="application/json")
459 response.headers['Access-Control-Allow-Origin'] = '*'
460 return response
461
462 except Exception as ex:
463 LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
464 return ex.message, 500
465
466 def post(self, id):
467 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
468 data = json.loads(request.data).get("flavor")
469 LOG.warning("Create Flavor: %s" % str(data))
470 # add to internal dict
471 f = self.api.compute.add_flavor(
472 data.get("name"),
473 data.get("vcpus"),
474 data.get("ram"), "MB",
475 data.get("disk"), "GB")
476 # create response based on incoming data
477 data["id"] = f.id
478 data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
479 self.api.port,
480 id,
481 f.id)}]
482 resp = {"flavor": data}
483 return Response(json.dumps(resp), status=200, mimetype="application/json")
484
485
486 class NovaListFlavorById(Resource):
487 def __init__(self, api):
488 self.api = api
489
490 def get(self, id, flavorid):
491 """
492 Returns details about one flavor.
493
494 :param id: tenant id, used for the 'href' link
495 :type id: ``str``
496 :param flavorid: Represents the flavor.
497 :type flavorid: ``str``
498 :return: Returns a flask response with detailed information about the flavor.
499 :rtype: :class:`flask.response`
500 """
501 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
502 try:
503 resp = dict()
504 resp['flavor'] = dict()
505 flavor = self.api.compute.flavors.get(flavorid, None)
506 if flavor is None:
507 for f in self.api.compute.flavors.values():
508 if f.id == flavorid:
509 flavor = f
510 break
511 resp['flavor']['id'] = flavor.id
512 resp['flavor']['name'] = flavor.name
513 resp['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
514 self.api.port,
515 id,
516 flavor.id)}]
517 response = Response(json.dumps(resp), status=200, mimetype="application/json")
518 response.headers['Access-Control-Allow-Origin'] = '*'
519 return response
520
521 except Exception as ex:
522 LOG.exception(u"%s: Could not retrieve flavor with id %s" % (__name__, flavorid))
523 return ex.message, 500
524
525 def delete(self, id, flavorid):
526 """
527 Removes the given flavor.
528 Does not really remove anything from the machine, just fakes an OK.
529 """
530 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
531 return Response("{}", status=204, mimetype="application/json")
532
533
534 class NovaListImages(Resource):
535 def __init__(self, api):
536 self.api = api
537
538 def get(self, id):
539 """
540 Creates a list of all usable images.
541
542 :param id: tenant id, used for the 'href' link
543 :type id: ``str``
544 :return: Returns a flask response with a list of available images.
545 :rtype: :class:`flask.response`
546 """
547 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
548 try:
549 resp = dict()
550 resp['images'] = list()
551 for image in self.api.compute.images.values():
552 f = dict()
553 f['id'] = image.id
554 f['name'] = str(image.name).replace(":latest", "")
555 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
556 self.api.port,
557 id,
558 image.id)}]
559 resp['images'].append(f)
560 response = Response(json.dumps(resp), status=200, mimetype="application/json")
561 response.headers['Access-Control-Allow-Origin'] = '*'
562 return response
563
564 except Exception as ex:
565 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
566 return ex.message, 500
567
568
569 class NovaListImagesDetails(Resource):
570 def __init__(self, api):
571 self.api = api
572
573 def get(self, id):
574 """
575 As List Images but with additional metadata.
576
577 :param id: tenant id, used for the 'href' link
578 :type id: ``str``
579 :return: Returns a flask response with a list of images and their metadata.
580 :rtype: :class:`flask.response`
581 """
582 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
583 try:
584 resp = dict()
585 resp['images'] = list()
586 for image in self.api.compute.images.values():
587 # use the class dict. it should work fine
588 # but use a copy so we don't modifiy the original
589 f = image.__dict__.copy()
590 # add additional expected stuff stay openstack compatible
591 f['name'] = str(image.name).replace(":latest", "")
592 f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
593 self.api.port,
594 id,
595 image.id)}]
596 f['metadata'] = {
597 "architecture": "x86_64",
598 "auto_disk_config": "True",
599 "kernel_id": "nokernel",
600 "ramdisk_id": "nokernel"
601 }
602 resp['images'].append(f)
603
604 response = Response(json.dumps(resp), status=200, mimetype="application/json")
605 response.headers['Access-Control-Allow-Origin'] = '*'
606 return response
607
608 except Exception as ex:
609 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
610 return ex.message, 500
611
612
613 class NovaListImageById(Resource):
614 def __init__(self, api):
615 self.api = api
616
617 def get(self, id, imageid):
618 """
619 Gets an image by id from the emulator with openstack nova compliant return values.
620
621 :param id: tenantid, we ignore this most of the time
622 :type id: ``str``
623 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
624 :type imageid: ``str``
625 :return: Returns a flask response with the information about one image.
626 :rtype: :class:`flask.response`
627 """
628 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
629 try:
630 resp = dict()
631 i = resp['image'] = dict()
632 for image in self.api.compute.images.values():
633 if image.id == imageid or image.name == imageid:
634 i['id'] = image.id
635 i['name'] = image.name
636
637 return Response(json.dumps(resp), status=200, mimetype="application/json")
638
639 response = Response("Image with id or name %s does not exists." % imageid, status=404)
640 response.headers['Access-Control-Allow-Origin'] = '*'
641 return response
642
643 except Exception as ex:
644 LOG.exception(u"%s: Could not retrieve image with id %s." % (__name__, imageid))
645 return ex.message, 500
646
647 def delete(self, id, imageid):
648 """
649 Removes the given image.
650 Does not really remove anything from the machine, just fakes an OK.
651 """
652 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
653 return Response("{}", status=204, mimetype="application/json")
654
655
656 class NovaShowServerDetails(Resource):
657 def __init__(self, api):
658 self.api = api
659
660 def get(self, id, serverid):
661 """
662 Returns detailed information about the specified server.
663
664 :param id: tenant id, used for the 'href' link
665 :type id: ``str``
666 :param serverid: Specifies the requested server.
667 :type serverid: ``str``
668 :return: Returns a flask response with details about the server.
669 :rtype: :class:`flask.response`
670 """
671 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
672 try:
673 server = self.api.compute.find_server_by_name_or_id(serverid)
674 if server is None:
675 return Response("Server with id or name %s does not exists." % serverid, status=404)
676 s = server.create_server_dict()
677 s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (self.api.ip,
678 self.api.port,
679 id,
680 server.id)}]
681
682 flavor = self.api.compute.flavors[server.flavor]
683 s['flavor'] = {
684 "id": flavor.id,
685 "links": [
686 {
687 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (self.api.ip,
688 self.api.port,
689 id,
690 flavor.id),
691 "rel": "bookmark"
692 }
693 ]
694 }
695 image = self.api.compute.images[server.image]
696 s['image'] = {
697 "id": image.id,
698 "links": [
699 {
700 "href": "http://%s:%d/v2.1/%s/images/%s" % (self.api.ip,
701 self.api.port,
702 id,
703 image.id),
704 "rel": "bookmark"
705 }
706 ]
707 }
708
709 response = Response(json.dumps({'server': s}), status=200, mimetype="application/json")
710 response.headers['Access-Control-Allow-Origin'] = '*'
711 return response
712
713 except Exception as ex:
714 LOG.exception(u"%s: Could not retrieve the server details." % __name__)
715 return ex.message, 500
716
717 def delete(self, id, serverid):
718 """
719 Delete a server instance.
720
721 :param id: tenant id, we ignore this most of the time
722 :type id: ``str``
723 :param serverid: The UUID of the server
724 :type serverid: ``str``
725 :return: Returns 200 if everything is fine.
726 :rtype: :class:`flask.response`
727 """
728 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
729 try:
730 server = self.api.compute.find_server_by_name_or_id(serverid)
731 if server is None:
732 return Response('Could not find server.', status=404, mimetype="application/json")
733
734 self.api.compute.stop_compute(server)
735
736 response = Response('Server deleted.', status=204, mimetype="application/json")
737 response.headers['Access-Control-Allow-Origin'] = '*'
738 return response
739
740 except Exception as ex:
741 LOG.exception(u"%s: Could not create the server." % __name__)
742 return ex.message, 500
743
744
745 class NovaInterfaceToServer(Resource):
746 def __init__(self, api):
747 self.api = api
748
749 def post(self, id, serverid):
750 """
751 Add an interface to the specified server.
752
753 :param id: tenant id, we ignore this most of the time
754 :type id: ``str``
755 :param serverid: Specifies the server.
756 :type serverid: ``str``
757 :return: Returns a flask response with information about the attached interface.
758 :rtype: :class:`flask.response`
759 """
760 LOG.debug("API CALL: %s GET" % 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("Server with id or name %s does not exists." % serverid, status=404)
765
766 if server.emulator_compute is None:
767 LOG.error("The targeted container does not exist.")
768 return Response("The targeted container of %s does not exist." % serverid, status=404)
769 data = json.loads(request.data).get("interfaceAttachment")
770 resp = dict()
771 port = data.get("port_id", None)
772 net = data.get("net_id", None)
773 dc = self.api.compute.dc
774 network_dict = dict()
775 network = None
776
777 if net is not None and port is not None:
778 port = self.api.compute.find_port_by_name_or_id(port)
779 network = self.api.compute.find_network_by_name_or_id(net)
780 network_dict['id'] = port.intf_name
781 network_dict['ip'] = port.ip_address
782 network_dict[network_dict['id']] = network.name
783 elif net is not None:
784 network = self.api.compute.find_network_by_name_or_id(net)
785 if network is None:
786 return Response("Network with id or name %s does not exists." % net, status=404)
787 port = self.api.compute.create_port("port:cp%s:fl:%s" %
788 (len(self.api.compute.ports), str(uuid.uuid4())))
789
790 port.net_name = network.name
791 port.ip_address = network.get_new_ip_address(port.name)
792 network_dict['id'] = port.intf_name
793 network_dict['ip'] = port.ip_address
794 network_dict[network_dict['id']] = network.name
795 elif port is not None:
796 port = self.api.compute.find_port_by_name_or_id(port)
797 network_dict['id'] = port.intf_name
798 network_dict['ip'] = port.ip_address
799 network = self.api.compute.find_network_by_name_or_id(port.net_name)
800 network_dict[network_dict['id']] = network.name
801 else:
802 raise Exception("You can only attach interfaces by port or network at the moment")
803
804 if network == self.api.manage.floating_network:
805 dc.net.addLink(server.emulator_compute, self.api.manage.floating_switch,
806 params1=network_dict, cls=Link, intfName1=port.intf_name)
807 else:
808 dc.net.addLink(server.emulator_compute, dc.switch,
809 params1=network_dict, cls=Link, intfName1=port.intf_name)
810 resp["port_state"] = "ACTIVE"
811 resp["port_id"] = port.id
812 resp["net_id"] = self.api.compute.find_network_by_name_or_id(port.net_name).id
813 resp["mac_addr"] = port.mac_address
814 resp["fixed_ips"] = list()
815 fixed_ips = dict()
816 fixed_ips["ip_address"] = port.ip_address
817 fixed_ips["subnet_id"] = network.subnet_name
818 resp["fixed_ips"].append(fixed_ips)
819 response = Response(json.dumps({"interfaceAttachment": resp}), status=202, mimetype="application/json")
820 response.headers['Access-Control-Allow-Origin'] = '*'
821 return response
822
823 except Exception as ex:
824 LOG.exception(u"%s: Could not add interface to the server." % __name__)
825 return ex.message, 500
826
827
828 class NovaShowAndDeleteInterfaceAtServer(Resource):
829 def __init__(self, api):
830 self.api = api
831
832 def delete(self, id, serverid, port_id):
833 """
834 Deletes an existing interface.
835
836 :param id: tenant id, we ignore this most of the time
837 :type id: ``str``
838 :param serverid: Specifies the server, where the interface will be deleted.
839 :type serverid: ``str``
840 :param port_id: Specifies the port of the interface.
841 :type port_id: ``str``
842 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
843 error message.
844 :rtype: :class:`flask.response`
845 """
846 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
847 try:
848 server = self.api.compute.find_server_by_name_or_id(serverid)
849 if server is None:
850 return Response("Server with id or name %s does not exists." % serverid, status=404)
851 port = self.api.compute.find_port_by_name_or_id(port_id)
852 if port is None:
853 return Response("Port with id or name %s does not exists." % port_id, status=404)
854
855 for link in self.api.compute.dc.net.links:
856 if str(link.intf1) == port.intf_name and \
857 str(link.intf1.ip) == port.ip_address.split('/')[0]:
858 self.api.compute.dc.net.removeLink(link)
859 break
860
861 response = Response("", status=202, mimetype="application/json")
862 response.headers['Access-Control-Allow-Origin'] = '*'
863 return response
864
865 except Exception as ex:
866 LOG.exception(u"%s: Could not detach interface from the server." % __name__)
867 return ex.message, 500
868
869
870 class NovaLimits(Resource):
871 def __init__(self, api):
872 self.api = api
873
874 def get(self, id):
875 """
876 Returns the resource limits of the emulated cloud.
877 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
878
879 TODO: For now we only return fixed limits, not based on the real deployment.
880
881 :param id: tenant id, used for the 'href' link
882 :type id: ``str``
883 :return: Returns the resource limits.
884 :rtype: :class:`flask.response`
885 """
886 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
887 try:
888 resp = {
889 "limits": {
890 "absolute": {
891 "maxImageMeta": 12800,
892 "maxPersonality": 500,
893 "maxPersonalitySize": 1024000,
894 "maxSecurityGroupRules": 2000,
895 "maxSecurityGroups": 1000,
896 "maxServerMeta": 12800,
897 "maxTotalCores": 2000,
898 "maxTotalFloatingIps": 1000,
899 "maxTotalInstances": 1000,
900 "maxTotalKeypairs": 1000,
901 "maxTotalRAMSize": 5120000,
902 "maxServerGroups": 1000,
903 "maxServerGroupMembers": 1000,
904 "totalCoresUsed": 0,
905 "totalInstancesUsed": 0,
906 "totalRAMUsed": 0,
907 "totalSecurityGroupsUsed": 0,
908 "totalFloatingIpsUsed": 0,
909 "totalServerGroupsUsed": 0
910 },
911 "rate": []
912 }
913 }
914 response = Response(json.dumps(resp), status=200, mimetype="application/json")
915 response.headers['Access-Control-Allow-Origin'] = '*'
916 return response
917
918 except Exception as ex:
919 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
920 return ex.message, 500