2 Copyright (c) 2017 SONATA-NFV and Paderborn University
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
17 Neither the name of the SONATA-NFV, Paderborn University
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
22 This work has been performed in the framework of the SONATA project,
23 funded by the European Commission under Grant number 671517 through
24 the Horizon 2020 and 5G-PPP programmes. The authors would like to
25 acknowledge the contributions of their colleagues of the SONATA
26 partner consortium (www.sonata-nfv.eu).
28 from flask_restful
import Resource
29 from flask
import Response
, request
30 from emuvim
.api
.openstack
.openstack_dummies
.base_openstack_dummy
import BaseOpenstackDummy
31 from emuvim
.api
.openstack
.helper
import get_host
35 from mininet
.link
import Link
38 LOG
= logging
.getLogger("api.openstack.nova")
41 class NovaDummyApi(BaseOpenstackDummy
):
42 def __init__(self
, in_ip
, in_port
, compute
):
43 super(NovaDummyApi
, self
).__init
__(in_ip
, in_port
)
44 self
.compute
= compute
46 self
.api
.add_resource(NovaVersionsList
, "/",
47 resource_class_kwargs
={'api': self
})
48 self
.api
.add_resource(Shutdown
, "/shutdown")
49 self
.api
.add_resource(NovaVersionShow
, "/v2.1/<id>",
50 resource_class_kwargs
={'api': self
})
51 self
.api
.add_resource(NovaListServersApi
, "/v2.1/<id>/servers",
52 resource_class_kwargs
={'api': self
})
53 self
.api
.add_resource(NovaListServersAndPortsApi
, "/v2.1/<id>/servers/andPorts",
54 resource_class_kwargs
={'api': self
})
55 self
.api
.add_resource(NovaListServersDetailed
, "/v2.1/<id>/servers/detail",
56 resource_class_kwargs
={'api': self
})
57 self
.api
.add_resource(NovaShowServerDetails
, "/v2.1/<id>/servers/<serverid>",
58 resource_class_kwargs
={'api': self
})
59 self
.api
.add_resource(NovaInterfaceToServer
, "/v2.1/<id>/servers/<serverid>/os-interface",
60 resource_class_kwargs
={'api': self
})
61 self
.api
.add_resource(NovaShowAndDeleteInterfaceAtServer
, "/v2.1/<id>/servers/<serverid>/os-interface/<port_id>",
62 resource_class_kwargs
={'api': self
})
63 self
.api
.add_resource(NovaListFlavors
, "/v2.1/<id>/flavors", "/v2/<id>/flavors",
64 resource_class_kwargs
={'api': self
})
65 self
.api
.add_resource(NovaListFlavorsDetails
, "/v2.1/<id>/flavors/detail", "/v2/<id>/flavors/detail",
66 resource_class_kwargs
={'api': self
})
67 self
.api
.add_resource(NovaListFlavorById
, "/v2.1/<id>/flavors/<flavorid>", "/v2/<id>/flavors/<flavorid>",
68 resource_class_kwargs
={'api': self
})
69 self
.api
.add_resource(NovaListImages
, "/v2.1/<id>/images",
70 resource_class_kwargs
={'api': self
})
71 self
.api
.add_resource(NovaListImagesDetails
, "/v2.1/<id>/images/detail",
72 resource_class_kwargs
={'api': self
})
73 self
.api
.add_resource(NovaListImageById
, "/v2.1/<id>/images/<imageid>",
74 resource_class_kwargs
={'api': self
})
75 self
.api
.add_resource(NovaLimits
, "/v2.1/<id>/limits",
76 resource_class_kwargs
={'api': self
})
78 def _start_flask(self
):
79 LOG
.info("Starting %s endpoint @ http://%s:%d" % ("NovaDummyApi", self
.ip
, self
.port
))
80 # add some flavors for good measure
81 self
.compute
.add_flavor('m1.tiny', 1, 512, "MB", 1, "GB")
82 self
.compute
.add_flavor('m1.nano', 1, 64, "MB", 0, "GB")
83 self
.compute
.add_flavor('m1.micro', 1, 128, "MB", 0, "GB")
84 self
.compute
.add_flavor('m1.small', 1, 1024, "MB", 2, "GB")
85 if self
.app
is not None:
86 self
.app
.before_request(self
.dump_playbook
)
87 self
.app
.run(self
.ip
, self
.port
, debug
=True, use_reloader
=False)
90 class Shutdown(Resource
):
92 A get request to /shutdown will shut down this endpoint.
96 LOG
.debug(("%s is beeing shut doen") % (__name__
))
97 func
= request
.environ
.get('werkzeug.server.shutdown')
99 raise RuntimeError('Not running with the Werkzeug Server')
103 class NovaVersionsList(Resource
):
104 def __init__(self
, api
):
111 :return: Returns a json with API versions.
112 :rtype: :class:`flask.response`
114 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
123 "href": "http://%s:%d/v2.1/",
129 "min_version": "2.1",
130 "updated": "2013-07-23T11:33:21Z"
134 """ % (get_host(request
), self
.api
.port
)
136 response
= Response(resp
, status
=200, mimetype
="application/json")
137 response
.headers
['Access-Control-Allow-Origin'] = '*'
140 except Exception as ex
:
141 LOG
.exception(u
"%s: Could not show list of versions." % __name__
)
142 return ex
.message
, 500
145 class NovaVersionShow(Resource
):
146 def __init__(self
, api
):
155 :return: Returns a json with API details.
156 :rtype: :class:`flask.response`
158 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
167 "href": "http://%s:%d/v2.1/",
171 "href": "http://docs.openstack.org/",
172 "rel": "describedby",
178 "base": "application/json",
179 "type": "application/vnd.openstack.compute+json;version=2.1"
184 "min_version": "2.1",
185 "updated": "2013-07-23T11:33:21Z"
188 """ % (get_host(request
), self
.api
.port
)
190 response
= Response(resp
, status
=200, mimetype
="application/json")
191 response
.headers
['Access-Control-Allow-Origin'] = '*'
194 except Exception as ex
:
195 LOG
.exception(u
"%s: Could not show list of versions." % __name__
)
196 return ex
.message
, 500
199 class NovaListServersApi(Resource
):
200 def __init__(self
, api
):
205 Creates a list with all running servers and their detailed information.
207 :param id: Used to create a individual link to quarry further information.
209 :return: Returns a json response with a dictionary that contains the server information.
210 :rtype: :class:`flask.response`
212 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
216 resp
['servers'] = list()
217 for server
in self
.api
.compute
.computeUnits
.values():
218 s
= server
.create_server_dict(self
.api
.compute
)
219 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
224 resp
['servers'].append(s
)
226 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
227 response
.headers
['Access-Control-Allow-Origin'] = '*'
230 except Exception as ex
:
231 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
232 return ex
.message
, 500
236 Creates a server instance.
238 :param id: tenant id, we ignore this most of the time
240 :return: Returns a flask response, with detailed information about the just created server.
241 :rtype: :class:`flask.response`
243 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
245 server_dict
= json
.loads(request
.data
)['server']
246 networks
= server_dict
.get('networks', None)
247 name
= str(self
.api
.compute
.dc
.label
) + "_man_" + server_dict
["name"][0:12]
249 if self
.api
.compute
.find_server_by_name_or_id(name
) is not None:
250 return Response("Server with name %s already exists." % name
, status
=409)
251 # TODO: not finished!
254 server
= self
.api
.compute
.create_server(name
)
255 server
.full_name
= str(self
.api
.compute
.dc
.label
) + "_man_" + server_dict
["name"]
256 server
.template_name
= server_dict
["name"]
257 if "metadata" in server_dict
:
258 server
.properties
= server_dict
["metadata"]
260 for flavor
in self
.api
.compute
.flavors
.values():
261 if flavor
.id == server_dict
.get('flavorRef', ''):
262 server
.flavor
= flavor
.name
263 for image
in self
.api
.compute
.images
.values():
264 if image
.id in server_dict
['imageRef']:
265 server
.image
= image
.name
267 if networks
is not None:
269 port
= self
.api
.compute
.find_port_by_name_or_id(net
.get('port', ""))
271 server
.port_names
.append(port
.name
)
273 return Response("Currently only networking by port is supported.", status
=400)
275 self
.api
.compute
._start
_compute
(server
)
277 response
= NovaShowServerDetails(self
.api
).get(id, server
.id)
278 response
.headers
['Access-Control-Allow-Origin'] = '*'
281 except Exception as ex
:
282 LOG
.exception(u
"%s: Could not create the server." % __name__
)
283 return ex
.message
, 500
286 class NovaListServersAndPortsApi(Resource
):
287 def __init__(self
, api
):
292 Creates a list with all running servers and their detailed information. This function also presents all
293 port information of each server.
295 :param id: Used to create a individual link to quarry further information.
297 :return: Returns a json response with a dictionary that contains the server information.
298 :rtype: :class:`flask.response`
300 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
304 resp
['servers'] = list()
305 for server
in self
.api
.compute
.computeUnits
.values():
306 s
= server
.create_server_dict(self
.api
.compute
)
307 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
313 for port_name
in server
.port_names
:
314 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
318 tmp
= port
.create_port_dict(self
.api
.compute
)
319 tmp
['intf_name'] = port
.intf_name
320 s
['ports'].append(tmp
)
322 resp
['servers'].append(s
)
324 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
325 response
.headers
['Access-Control-Allow-Origin'] = '*'
328 except Exception as ex
:
329 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
330 return ex
.message
, 500
333 class NovaListServersDetailed(Resource
):
334 def __init__(self
, api
):
339 As List Servers, it lists all running servers and their details but furthermore it also states the
340 used flavor and the server image.
342 :param id: tenant id, used for the 'href' link.
344 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
345 :rtype: :class:`flask.response`
347 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
350 resp
= {"servers": list()}
351 for server
in self
.api
.compute
.computeUnits
.values():
352 s
= server
.create_server_dict(self
.api
.compute
)
353 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
357 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
362 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
370 image
= self
.api
.compute
.images
[server
.image
]
375 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
384 resp
['servers'].append(s
)
386 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
387 response
.headers
['Access-Control-Allow-Origin'] = '*'
390 except Exception as ex
:
391 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
392 return ex
.message
, 500
395 class NovaListFlavors(Resource
):
396 def __init__(self
, api
):
401 Lists all available flavors.
403 :param id: tenant id, used for the 'href' link
405 :return: Returns a flask response with a list of all flavors.
406 :rtype: :class:`flask.response`
408 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
411 resp
['flavors'] = list()
412 for flavor
in self
.api
.compute
.flavors
.values():
413 f
= flavor
.__dict
__.copy()
415 f
['name'] = flavor
.name
416 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
420 resp
['flavors'].append(f
)
422 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
423 response
.headers
['Access-Control-Allow-Origin'] = '*'
426 except Exception as ex
:
427 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
428 return ex
.message
, 500
431 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
432 data
= json
.loads(request
.data
).get("flavor")
433 LOG
.warning("Create Flavor: %s" % str(data
))
434 # add to internal dict
435 f
= self
.api
.compute
.add_flavor(
438 data
.get("ram"), "MB",
439 data
.get("disk"), "GB")
440 # create response based on incoming data
442 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
446 resp
= {"flavor": data
}
447 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
450 class NovaListFlavorsDetails(Resource
):
451 def __init__(self
, api
):
456 Lists all flavors with additional information like ram and disk space.
458 :param id: tenant id, used for the 'href' link
460 :return: Returns a flask response with a list of all flavors with additional information.
461 :rtype: :class:`flask.response`
463 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
466 resp
['flavors'] = list()
467 for flavor
in self
.api
.compute
.flavors
.values():
468 # use the class dict. it should work fine
469 # but use a copy so we don't modifiy the original
470 f
= flavor
.__dict
__.copy()
471 # add additional expected stuff stay openstack compatible
472 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
476 f
['OS-FLV-DISABLED:disabled'] = False
477 f
['OS-FLV-EXT-DATA:ephemeral'] = 0
478 f
['os-flavor-access:is_public'] = True
479 f
['ram'] = flavor
.memory
480 f
['vcpus'] = flavor
.cpu
482 f
['disk'] = flavor
.storage
483 f
['rxtx_factor'] = 1.0
484 resp
['flavors'].append(f
)
486 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
487 response
.headers
['Access-Control-Allow-Origin'] = '*'
490 except Exception as ex
:
491 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
492 return ex
.message
, 500
495 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
496 data
= json
.loads(request
.data
).get("flavor")
497 LOG
.warning("Create Flavor: %s" % str(data
))
498 # add to internal dict
499 f
= self
.api
.compute
.add_flavor(
502 data
.get("ram"), "MB",
503 data
.get("disk"), "GB")
504 # create response based on incoming data
506 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
510 resp
= {"flavor": data
}
511 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
514 class NovaListFlavorById(Resource
):
515 def __init__(self
, api
):
518 def get(self
, id, flavorid
):
520 Returns details about one flavor.
522 :param id: tenant id, used for the 'href' link
524 :param flavorid: Represents the flavor.
525 :type flavorid: ``str``
526 :return: Returns a flask response with detailed information about the flavor.
527 :rtype: :class:`flask.response`
529 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
532 resp
['flavor'] = dict()
533 flavor
= self
.api
.compute
.flavors
.get(flavorid
, None)
535 for f
in self
.api
.compute
.flavors
.values():
539 resp
['flavor']['id'] = flavor
.id
540 resp
['flavor']['name'] = flavor
.name
541 resp
['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
545 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
546 response
.headers
['Access-Control-Allow-Origin'] = '*'
549 except Exception as ex
:
550 LOG
.exception(u
"%s: Could not retrieve flavor with id %s" % (__name__
, flavorid
))
551 return ex
.message
, 500
553 def delete(self
, id, flavorid
):
555 Removes the given flavor.
556 Does not really remove anything from the machine, just fakes an OK.
558 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
559 return Response("{}", status
=204, mimetype
="application/json")
562 class NovaListImages(Resource
):
563 def __init__(self
, api
):
568 Creates a list of all usable images.
570 :param id: tenant id, used for the 'href' link
572 :return: Returns a flask response with a list of available images.
573 :rtype: :class:`flask.response`
575 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
578 resp
['images'] = list()
579 for image
in self
.api
.compute
.images
.values():
582 f
['name'] = str(image
.name
).replace(":latest", "")
583 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
587 resp
['images'].append(f
)
588 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
589 response
.headers
['Access-Control-Allow-Origin'] = '*'
592 except Exception as ex
:
593 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
594 return ex
.message
, 500
597 class NovaListImagesDetails(Resource
):
598 def __init__(self
, api
):
603 As List Images but with additional metadata.
605 :param id: tenant id, used for the 'href' link
607 :return: Returns a flask response with a list of images and their metadata.
608 :rtype: :class:`flask.response`
610 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
613 resp
['images'] = list()
614 for image
in self
.api
.compute
.images
.values():
615 # use the class dict. it should work fine
616 # but use a copy so we don't modifiy the original
617 f
= image
.__dict
__.copy()
618 # add additional expected stuff stay openstack compatible
619 f
['name'] = str(image
.name
).replace(":latest", "")
620 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
625 "architecture": "x86_64",
626 "auto_disk_config": "True",
627 "kernel_id": "nokernel",
628 "ramdisk_id": "nokernel"
630 resp
['images'].append(f
)
632 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
633 response
.headers
['Access-Control-Allow-Origin'] = '*'
636 except Exception as ex
:
637 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
638 return ex
.message
, 500
641 class NovaListImageById(Resource
):
642 def __init__(self
, api
):
645 def get(self
, id, imageid
):
647 Gets an image by id from the emulator with openstack nova compliant return values.
649 :param id: tenantid, we ignore this most of the time
651 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
652 :type imageid: ``str``
653 :return: Returns a flask response with the information about one image.
654 :rtype: :class:`flask.response`
656 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
659 i
= resp
['image'] = dict()
660 for image
in self
.api
.compute
.images
.values():
661 if image
.id == imageid
or image
.name
== imageid
:
663 i
['name'] = image
.name
665 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
667 response
= Response("Image with id or name %s does not exists." % imageid
, status
=404)
668 response
.headers
['Access-Control-Allow-Origin'] = '*'
671 except Exception as ex
:
672 LOG
.exception(u
"%s: Could not retrieve image with id %s." % (__name__
, imageid
))
673 return ex
.message
, 500
675 def delete(self
, id, imageid
):
677 Removes the given image.
678 Does not really remove anything from the machine, just fakes an OK.
680 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
681 return Response("{}", status
=204, mimetype
="application/json")
684 class NovaShowServerDetails(Resource
):
685 def __init__(self
, api
):
688 def get(self
, id, serverid
):
690 Returns detailed information about the specified server.
692 :param id: tenant id, used for the 'href' link
694 :param serverid: Specifies the requested server.
695 :type serverid: ``str``
696 :return: Returns a flask response with details about the server.
697 :rtype: :class:`flask.response`
699 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
701 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
703 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
704 s
= server
.create_server_dict()
705 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
710 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
715 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
723 image
= self
.api
.compute
.images
[server
.image
]
728 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
737 response
= Response(json
.dumps({'server': s
}), status
=200, mimetype
="application/json")
738 response
.headers
['Access-Control-Allow-Origin'] = '*'
741 except Exception as ex
:
742 LOG
.exception(u
"%s: Could not retrieve the server details." % __name__
)
743 return ex
.message
, 500
745 def delete(self
, id, serverid
):
747 Delete a server instance.
749 :param id: tenant id, we ignore this most of the time
751 :param serverid: The UUID of the server
752 :type serverid: ``str``
753 :return: Returns 200 if everything is fine.
754 :rtype: :class:`flask.response`
756 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
758 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
760 return Response('Could not find server.', status
=404, mimetype
="application/json")
762 self
.api
.compute
.stop_compute(server
)
764 response
= Response('Server deleted.', status
=204, mimetype
="application/json")
765 response
.headers
['Access-Control-Allow-Origin'] = '*'
768 except Exception as ex
:
769 LOG
.exception(u
"%s: Could not create the server." % __name__
)
770 return ex
.message
, 500
773 class NovaInterfaceToServer(Resource
):
774 def __init__(self
, api
):
777 def post(self
, id, serverid
):
779 Add an interface to the specified server.
781 :param id: tenant id, we ignore this most of the time
783 :param serverid: Specifies the server.
784 :type serverid: ``str``
785 :return: Returns a flask response with information about the attached interface.
786 :rtype: :class:`flask.response`
788 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
790 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
792 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
794 if server
.emulator_compute
is None:
795 LOG
.error("The targeted container does not exist.")
796 return Response("The targeted container of %s does not exist." % serverid
, status
=404)
797 data
= json
.loads(request
.data
).get("interfaceAttachment")
799 port
= data
.get("port_id", None)
800 net
= data
.get("net_id", None)
801 dc
= self
.api
.compute
.dc
802 network_dict
= dict()
805 if net
is not None and port
is not None:
806 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
807 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
808 network_dict
['id'] = port
.intf_name
809 network_dict
['ip'] = port
.ip_address
810 network_dict
[network_dict
['id']] = network
.name
811 elif net
is not None:
812 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
814 return Response("Network with id or name %s does not exists." % net
, status
=404)
815 port
= self
.api
.compute
.create_port("port:cp%s:fl:%s" %
816 (len(self
.api
.compute
.ports
), str(uuid
.uuid4())))
818 port
.net_name
= network
.name
819 port
.ip_address
= network
.get_new_ip_address(port
.name
)
820 network_dict
['id'] = port
.intf_name
821 network_dict
['ip'] = port
.ip_address
822 network_dict
[network_dict
['id']] = network
.name
823 elif port
is not None:
824 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
825 network_dict
['id'] = port
.intf_name
826 network_dict
['ip'] = port
.ip_address
827 network
= self
.api
.compute
.find_network_by_name_or_id(port
.net_name
)
828 network_dict
[network_dict
['id']] = network
.name
830 raise Exception("You can only attach interfaces by port or network at the moment")
832 if network
== self
.api
.manage
.floating_network
:
833 dc
.net
.addLink(server
.emulator_compute
, self
.api
.manage
.floating_switch
,
834 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
836 dc
.net
.addLink(server
.emulator_compute
, dc
.switch
,
837 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
838 resp
["port_state"] = "ACTIVE"
839 resp
["port_id"] = port
.id
840 resp
["net_id"] = self
.api
.compute
.find_network_by_name_or_id(port
.net_name
).id
841 resp
["mac_addr"] = port
.mac_address
842 resp
["fixed_ips"] = list()
844 fixed_ips
["ip_address"] = port
.ip_address
845 fixed_ips
["subnet_id"] = network
.subnet_name
846 resp
["fixed_ips"].append(fixed_ips
)
847 response
= Response(json
.dumps({"interfaceAttachment": resp
}), status
=202, mimetype
="application/json")
848 response
.headers
['Access-Control-Allow-Origin'] = '*'
851 except Exception as ex
:
852 LOG
.exception(u
"%s: Could not add interface to the server." % __name__
)
853 return ex
.message
, 500
856 class NovaShowAndDeleteInterfaceAtServer(Resource
):
857 def __init__(self
, api
):
860 def delete(self
, id, serverid
, port_id
):
862 Deletes an existing interface.
864 :param id: tenant id, we ignore this most of the time
866 :param serverid: Specifies the server, where the interface will be deleted.
867 :type serverid: ``str``
868 :param port_id: Specifies the port of the interface.
869 :type port_id: ``str``
870 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
872 :rtype: :class:`flask.response`
874 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
876 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
878 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
879 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
881 return Response("Port with id or name %s does not exists." % port_id
, status
=404)
883 for link
in self
.api
.compute
.dc
.net
.links
:
884 if str(link
.intf1
) == port
.intf_name
and \
885 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
886 self
.api
.compute
.dc
.net
.removeLink(link
)
889 response
= Response("", status
=202, mimetype
="application/json")
890 response
.headers
['Access-Control-Allow-Origin'] = '*'
893 except Exception as ex
:
894 LOG
.exception(u
"%s: Could not detach interface from the server." % __name__
)
895 return ex
.message
, 500
898 class NovaLimits(Resource
):
899 def __init__(self
, api
):
904 Returns the resource limits of the emulated cloud.
905 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
907 TODO: For now we only return fixed limits, not based on the real deployment.
909 :param id: tenant id, used for the 'href' link
911 :return: Returns the resource limits.
912 :rtype: :class:`flask.response`
914 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
919 "maxImageMeta": 12800,
920 "maxPersonality": 500,
921 "maxPersonalitySize": 1024000,
922 "maxSecurityGroupRules": 2000,
923 "maxSecurityGroups": 1000,
924 "maxServerMeta": 12800,
925 "maxTotalCores": 2000,
926 "maxTotalFloatingIps": 1000,
927 "maxTotalInstances": 1000,
928 "maxTotalKeypairs": 1000,
929 "maxTotalRAMSize": 5120000,
930 "maxServerGroups": 1000,
931 "maxServerGroupMembers": 1000,
933 "totalInstancesUsed": 0,
935 "totalSecurityGroupsUsed": 0,
936 "totalFloatingIpsUsed": 0,
937 "totalServerGroupsUsed": 0
942 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
943 response
.headers
['Access-Control-Allow-Origin'] = '*'
946 except Exception as ex
:
947 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
948 return ex
.message
, 500