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