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
45 self
.compute
.add_flavor('m1.tiny', 1, 512, "MB", 1, "GB")
46 self
.compute
.add_flavor('m1.nano', 1, 64, "MB", 0, "GB")
47 self
.compute
.add_flavor('m1.micro', 1, 128, "MB", 0, "GB")
48 self
.compute
.add_flavor('m1.small', 1, 1024, "MB", 2, "GB")
50 self
.api
.add_resource(NovaVersionsList
, "/",
51 resource_class_kwargs
={'api': self
})
52 self
.api
.add_resource(NovaVersionShow
, "/v2.1/<id>",
53 resource_class_kwargs
={'api': self
})
54 self
.api
.add_resource(NovaListServersApi
, "/v2.1/<id>/servers",
55 resource_class_kwargs
={'api': self
})
56 self
.api
.add_resource(NovaListServersAndPortsApi
, "/v2.1/<id>/servers/andPorts",
57 resource_class_kwargs
={'api': self
})
58 self
.api
.add_resource(NovaListServersDetailed
, "/v2.1/<id>/servers/detail",
59 resource_class_kwargs
={'api': self
})
60 self
.api
.add_resource(NovaShowServerDetails
, "/v2.1/<id>/servers/<serverid>",
61 resource_class_kwargs
={'api': self
})
62 self
.api
.add_resource(NovaInterfaceToServer
, "/v2.1/<id>/servers/<serverid>/os-interface",
63 resource_class_kwargs
={'api': self
})
64 self
.api
.add_resource(NovaShowAndDeleteInterfaceAtServer
, "/v2.1/<id>/servers/<serverid>/os-interface/<port_id>",
65 resource_class_kwargs
={'api': self
})
66 self
.api
.add_resource(NovaListFlavors
, "/v2.1/<id>/flavors", "/v2/<id>/flavors",
67 resource_class_kwargs
={'api': self
})
68 self
.api
.add_resource(NovaListFlavorsDetails
, "/v2.1/<id>/flavors/detail", "/v2/<id>/flavors/detail",
69 resource_class_kwargs
={'api': self
})
70 self
.api
.add_resource(NovaListFlavorById
, "/v2.1/<id>/flavors/<flavorid>", "/v2/<id>/flavors/<flavorid>",
71 resource_class_kwargs
={'api': self
})
72 self
.api
.add_resource(NovaListImages
, "/v2.1/<id>/images",
73 resource_class_kwargs
={'api': self
})
74 self
.api
.add_resource(NovaListImagesDetails
, "/v2.1/<id>/images/detail",
75 resource_class_kwargs
={'api': self
})
76 self
.api
.add_resource(NovaListImageById
, "/v2.1/<id>/images/<imageid>",
77 resource_class_kwargs
={'api': self
})
78 self
.api
.add_resource(NovaLimits
, "/v2.1/<id>/limits",
79 resource_class_kwargs
={'api': self
})
82 class NovaVersionsList(Resource
):
83 def __init__(self
, api
):
90 :return: Returns a json with API versions.
91 :rtype: :class:`flask.response`
93 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
102 "href": "http://%s:%d/v2.1/",
108 "min_version": "2.1",
109 "updated": "2013-07-23T11:33:21Z"
113 """ % (get_host(request
), self
.api
.port
)
115 response
= Response(resp
, status
=200, mimetype
="application/json")
116 response
.headers
['Access-Control-Allow-Origin'] = '*'
119 except Exception as ex
:
120 LOG
.exception(u
"%s: Could not show list of versions." % __name__
)
121 return ex
.message
, 500
124 class NovaVersionShow(Resource
):
125 def __init__(self
, api
):
134 :return: Returns a json with API details.
135 :rtype: :class:`flask.response`
137 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
146 "href": "http://%s:%d/v2.1/",
150 "href": "http://docs.openstack.org/",
151 "rel": "describedby",
157 "base": "application/json",
158 "type": "application/vnd.openstack.compute+json;version=2.1"
163 "min_version": "2.1",
164 "updated": "2013-07-23T11:33:21Z"
167 """ % (get_host(request
), self
.api
.port
)
169 response
= Response(resp
, status
=200, mimetype
="application/json")
170 response
.headers
['Access-Control-Allow-Origin'] = '*'
173 except Exception as ex
:
174 LOG
.exception(u
"%s: Could not show list of versions." % __name__
)
175 return ex
.message
, 500
178 class NovaListServersApi(Resource
):
179 def __init__(self
, api
):
184 Creates a list with all running servers and their detailed information.
186 :param id: Used to create a individual link to quarry further information.
188 :return: Returns a json response with a dictionary that contains the server information.
189 :rtype: :class:`flask.response`
191 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
195 resp
['servers'] = list()
196 for server
in self
.api
.compute
.computeUnits
.values():
197 s
= server
.create_server_dict(self
.api
.compute
)
198 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
203 resp
['servers'].append(s
)
205 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
206 response
.headers
['Access-Control-Allow-Origin'] = '*'
209 except Exception as ex
:
210 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
211 return ex
.message
, 500
215 Creates a server instance.
217 :param id: tenant id, we ignore this most of the time
219 :return: Returns a flask response, with detailed information about the just created server.
220 :rtype: :class:`flask.response`
222 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
224 server_dict
= json
.loads(request
.data
)['server']
225 networks
= server_dict
.get('networks', None)
226 name
= str(self
.api
.compute
.dc
.label
) + "_" + server_dict
["name"]
228 if self
.api
.compute
.find_server_by_name_or_id(name
) is not None:
229 LOG
.error("Server with name %s already exists. 409" % name
)
230 return Response("Server with name %s already exists." % name
, status
=409)
231 # TODO: not finished!
234 server
= self
.api
.compute
.create_server(name
)
235 server
.full_name
= str(self
.api
.compute
.dc
.label
) + "_" + server_dict
["name"]
236 server
.template_name
= server_dict
["name"]
237 if "metadata" in server_dict
:
238 server
.properties
= server_dict
["metadata"]
240 for flavor
in self
.api
.compute
.flavors
.values():
241 if flavor
.id == server_dict
.get('flavorRef', ''):
242 server
.flavor
= flavor
.name
243 for image
in self
.api
.compute
.images
.values():
244 if image
.id in server_dict
['imageRef']:
245 server
.image
= image
.name
247 if networks
is not None:
249 port
= self
.api
.compute
.find_port_by_name_or_id(net
.get('port', ""))
251 server
.port_names
.append(port
.name
)
253 return Response("Currently only networking by port is supported.", status
=400)
255 self
.api
.compute
._start
_compute
(server
)
257 response
= NovaShowServerDetails(self
.api
).get(id, server
.id)
258 response
.headers
['Access-Control-Allow-Origin'] = '*'
261 except Exception as ex
:
262 LOG
.exception(u
"%s: Could not create the server." % __name__
)
263 return ex
.message
, 500
266 class NovaListServersAndPortsApi(Resource
):
267 def __init__(self
, api
):
272 Creates a list with all running servers and their detailed information. This function also presents all
273 port information of each server.
275 :param id: Used to create a individual link to quarry further information.
277 :return: Returns a json response with a dictionary that contains the server information.
278 :rtype: :class:`flask.response`
280 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
284 resp
['servers'] = list()
285 for server
in self
.api
.compute
.computeUnits
.values():
286 s
= server
.create_server_dict(self
.api
.compute
)
287 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
293 for port_name
in server
.port_names
:
294 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
298 tmp
= port
.create_port_dict(self
.api
.compute
)
299 tmp
['intf_name'] = port
.intf_name
300 s
['ports'].append(tmp
)
302 resp
['servers'].append(s
)
304 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
305 response
.headers
['Access-Control-Allow-Origin'] = '*'
308 except Exception as ex
:
309 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
310 return ex
.message
, 500
313 class NovaListServersDetailed(Resource
):
314 def __init__(self
, api
):
319 As List Servers, it lists all running servers and their details but furthermore it also states the
320 used flavor and the server image.
322 :param id: tenant id, used for the 'href' link.
324 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
325 :rtype: :class:`flask.response`
327 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
330 resp
= {"servers": list()}
331 for server
in self
.api
.compute
.computeUnits
.values():
332 s
= server
.create_server_dict(self
.api
.compute
)
333 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
337 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
342 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
350 image
= self
.api
.compute
.images
[server
.image
]
355 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
364 resp
['servers'].append(s
)
366 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
367 response
.headers
['Access-Control-Allow-Origin'] = '*'
370 except Exception as ex
:
371 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
372 return ex
.message
, 500
375 class NovaListFlavors(Resource
):
376 def __init__(self
, api
):
381 Lists all available flavors.
383 :param id: tenant id, used for the 'href' link
385 :return: Returns a flask response with a list of all flavors.
386 :rtype: :class:`flask.response`
388 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
391 resp
['flavors'] = list()
392 for flavor
in self
.api
.compute
.flavors
.values():
393 f
= flavor
.__dict
__.copy()
395 f
['name'] = flavor
.name
396 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
400 resp
['flavors'].append(f
)
402 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
403 response
.headers
['Access-Control-Allow-Origin'] = '*'
406 except Exception as ex
:
407 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
408 return ex
.message
, 500
411 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
412 data
= json
.loads(request
.data
).get("flavor")
413 LOG
.warning("Create Flavor: %s" % str(data
))
414 # add to internal dict
415 f
= self
.api
.compute
.add_flavor(
418 data
.get("ram"), "MB",
419 data
.get("disk"), "GB")
420 # create response based on incoming data
422 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
426 resp
= {"flavor": data
}
427 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
430 class NovaListFlavorsDetails(Resource
):
431 def __init__(self
, api
):
436 Lists all flavors with additional information like ram and disk space.
438 :param id: tenant id, used for the 'href' link
440 :return: Returns a flask response with a list of all flavors with additional information.
441 :rtype: :class:`flask.response`
443 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
446 resp
['flavors'] = list()
447 for flavor
in self
.api
.compute
.flavors
.values():
448 # use the class dict. it should work fine
449 # but use a copy so we don't modifiy the original
450 f
= flavor
.__dict
__.copy()
451 # add additional expected stuff stay openstack compatible
452 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
456 f
['OS-FLV-DISABLED:disabled'] = False
457 f
['OS-FLV-EXT-DATA:ephemeral'] = 0
458 f
['os-flavor-access:is_public'] = True
459 f
['ram'] = flavor
.memory
460 f
['vcpus'] = flavor
.cpu
462 f
['disk'] = flavor
.storage
463 f
['rxtx_factor'] = 1.0
464 resp
['flavors'].append(f
)
466 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
467 response
.headers
['Access-Control-Allow-Origin'] = '*'
470 except Exception as ex
:
471 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
472 return ex
.message
, 500
475 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
476 data
= json
.loads(request
.data
).get("flavor")
477 LOG
.warning("Create Flavor: %s" % str(data
))
478 # add to internal dict
479 f
= self
.api
.compute
.add_flavor(
482 data
.get("ram"), "MB",
483 data
.get("disk"), "GB")
484 # create response based on incoming data
486 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
490 resp
= {"flavor": data
}
491 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
494 class NovaListFlavorById(Resource
):
495 def __init__(self
, api
):
498 def get(self
, id, flavorid
):
500 Returns details about one flavor.
502 :param id: tenant id, used for the 'href' link
504 :param flavorid: Represents the flavor.
505 :type flavorid: ``str``
506 :return: Returns a flask response with detailed information about the flavor.
507 :rtype: :class:`flask.response`
509 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
512 resp
['flavor'] = dict()
513 flavor
= self
.api
.compute
.flavors
.get(flavorid
, None)
515 for f
in self
.api
.compute
.flavors
.values():
519 resp
['flavor']['id'] = flavor
.id
520 resp
['flavor']['name'] = flavor
.name
521 resp
['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
525 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
526 response
.headers
['Access-Control-Allow-Origin'] = '*'
529 except Exception as ex
:
530 LOG
.exception(u
"%s: Could not retrieve flavor with id %s" % (__name__
, flavorid
))
531 return ex
.message
, 500
533 def delete(self
, id, flavorid
):
535 Removes the given flavor.
536 Does not really remove anything from the machine, just fakes an OK.
538 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
539 return Response("", status
=204, mimetype
="application/json")
542 class NovaListImages(Resource
):
543 def __init__(self
, api
):
548 Creates a list of all usable images.
550 :param id: tenant id, used for the 'href' link
552 :return: Returns a flask response with a list of available images.
553 :rtype: :class:`flask.response`
555 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
558 resp
['images'] = list()
559 for image
in self
.api
.compute
.images
.values():
562 f
['name'] = str(image
.name
).replace(":latest", "")
563 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
567 resp
['images'].append(f
)
568 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
569 response
.headers
['Access-Control-Allow-Origin'] = '*'
572 except Exception as ex
:
573 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
574 return ex
.message
, 500
577 class NovaListImagesDetails(Resource
):
578 def __init__(self
, api
):
583 As List Images but with additional metadata.
585 :param id: tenant id, used for the 'href' link
587 :return: Returns a flask response with a list of images and their metadata.
588 :rtype: :class:`flask.response`
590 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
593 resp
['images'] = list()
594 for image
in self
.api
.compute
.images
.values():
595 # use the class dict. it should work fine
596 # but use a copy so we don't modifiy the original
597 f
= image
.__dict
__.copy()
598 # add additional expected stuff stay openstack compatible
599 f
['name'] = str(image
.name
).replace(":latest", "")
600 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
605 "architecture": "x86_64",
606 "auto_disk_config": "True",
607 "kernel_id": "nokernel",
608 "ramdisk_id": "nokernel"
610 resp
['images'].append(f
)
612 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
613 response
.headers
['Access-Control-Allow-Origin'] = '*'
616 except Exception as ex
:
617 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
618 return ex
.message
, 500
621 class NovaListImageById(Resource
):
622 def __init__(self
, api
):
625 def get(self
, id, imageid
):
627 Gets an image by id from the emulator with openstack nova compliant return values.
629 :param id: tenantid, we ignore this most of the time
631 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
632 :type imageid: ``str``
633 :return: Returns a flask response with the information about one image.
634 :rtype: :class:`flask.response`
636 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
639 i
= resp
['image'] = dict()
640 for image
in self
.api
.compute
.images
.values():
641 if image
.id == imageid
or image
.name
== imageid
:
643 i
['name'] = image
.name
645 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
647 response
= Response("Image with id or name %s does not exists." % imageid
, status
=404)
648 response
.headers
['Access-Control-Allow-Origin'] = '*'
651 except Exception as ex
:
652 LOG
.exception(u
"%s: Could not retrieve image with id %s." % (__name__
, imageid
))
653 return ex
.message
, 500
655 def delete(self
, id, imageid
):
657 Removes the given image.
658 Does not really remove anything from the machine, just fakes an OK.
660 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
661 return Response("", status
=204, mimetype
="application/json")
664 class NovaShowServerDetails(Resource
):
665 def __init__(self
, api
):
668 def get(self
, id, serverid
):
670 Returns detailed information about the specified server.
672 :param id: tenant id, used for the 'href' link
674 :param serverid: Specifies the requested server.
675 :type serverid: ``str``
676 :return: Returns a flask response with details about the server.
677 :rtype: :class:`flask.response`
679 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
681 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
683 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
684 s
= server
.create_server_dict()
685 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
690 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
695 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
703 image
= self
.api
.compute
.images
[server
.image
]
708 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
717 response
= Response(json
.dumps({'server': s
}), status
=200, mimetype
="application/json")
718 response
.headers
['Access-Control-Allow-Origin'] = '*'
721 except Exception as ex
:
722 LOG
.exception(u
"%s: Could not retrieve the server details." % __name__
)
723 return ex
.message
, 500
725 def delete(self
, id, serverid
):
727 Delete a server instance.
729 :param id: tenant id, we ignore this most of the time
731 :param serverid: The UUID of the server
732 :type serverid: ``str``
733 :return: Returns 204 if everything is fine.
734 :rtype: :class:`flask.response`
736 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
738 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
740 return Response('Could not find server.', status
=404, mimetype
="application/json")
742 self
.api
.compute
.stop_compute(server
)
744 response
= Response('', status
=204, mimetype
="application/json")
745 response
.headers
['Access-Control-Allow-Origin'] = '*'
748 except Exception as ex
:
749 LOG
.exception(u
"%s: Could not create the server." % __name__
)
750 return ex
.message
, 500
753 class NovaInterfaceToServer(Resource
):
754 def __init__(self
, api
):
757 def post(self
, id, serverid
):
759 Add an interface to the specified server.
761 :param id: tenant id, we ignore this most of the time
763 :param serverid: Specifies the server.
764 :type serverid: ``str``
765 :return: Returns a flask response with information about the attached interface.
766 :rtype: :class:`flask.response`
768 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
770 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
772 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
774 if server
.emulator_compute
is None:
775 LOG
.error("The targeted container does not exist.")
776 return Response("The targeted container of %s does not exist." % serverid
, status
=404)
777 data
= json
.loads(request
.data
).get("interfaceAttachment")
779 port
= data
.get("port_id", None)
780 net
= data
.get("net_id", None)
781 dc
= self
.api
.compute
.dc
782 network_dict
= dict()
785 if net
is not None and port
is not None:
786 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
787 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
788 network_dict
['id'] = port
.intf_name
789 network_dict
['ip'] = port
.ip_address
790 network_dict
[network_dict
['id']] = network
.name
791 elif net
is not None:
792 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
794 return Response("Network with id or name %s does not exists." % net
, status
=404)
795 port
= self
.api
.compute
.create_port("port:cp%s:fl:%s" %
796 (len(self
.api
.compute
.ports
), str(uuid
.uuid4())))
798 port
.net_name
= network
.name
799 port
.ip_address
= network
.get_new_ip_address(port
.name
)
800 network_dict
['id'] = port
.intf_name
801 network_dict
['ip'] = port
.ip_address
802 network_dict
[network_dict
['id']] = network
.name
803 elif port
is not None:
804 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
805 network_dict
['id'] = port
.intf_name
806 network_dict
['ip'] = port
.ip_address
807 network
= self
.api
.compute
.find_network_by_name_or_id(port
.net_name
)
808 network_dict
[network_dict
['id']] = network
.name
810 raise Exception("You can only attach interfaces by port or network at the moment")
812 if network
== self
.api
.manage
.floating_network
:
813 dc
.net
.addLink(server
.emulator_compute
, self
.api
.manage
.floating_switch
,
814 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
816 dc
.net
.addLink(server
.emulator_compute
, dc
.switch
,
817 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
818 resp
["port_state"] = "ACTIVE"
819 resp
["port_id"] = port
.id
820 resp
["net_id"] = self
.api
.compute
.find_network_by_name_or_id(port
.net_name
).id
821 resp
["mac_addr"] = port
.mac_address
822 resp
["fixed_ips"] = list()
824 fixed_ips
["ip_address"] = port
.ip_address
825 fixed_ips
["subnet_id"] = network
.subnet_name
826 resp
["fixed_ips"].append(fixed_ips
)
827 response
= Response(json
.dumps({"interfaceAttachment": resp
}), status
=202, mimetype
="application/json")
828 response
.headers
['Access-Control-Allow-Origin'] = '*'
831 except Exception as ex
:
832 LOG
.exception(u
"%s: Could not add interface to the server." % __name__
)
833 return ex
.message
, 500
836 class NovaShowAndDeleteInterfaceAtServer(Resource
):
837 def __init__(self
, api
):
840 def delete(self
, id, serverid
, port_id
):
842 Deletes an existing interface.
844 :param id: tenant id, we ignore this most of the time
846 :param serverid: Specifies the server, where the interface will be deleted.
847 :type serverid: ``str``
848 :param port_id: Specifies the port of the interface.
849 :type port_id: ``str``
850 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
852 :rtype: :class:`flask.response`
854 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
856 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
858 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
859 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
861 return Response("Port with id or name %s does not exists." % port_id
, status
=404)
863 for link
in self
.api
.compute
.dc
.net
.links
:
864 if str(link
.intf1
) == port
.intf_name
and \
865 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
866 self
.api
.compute
.dc
.net
.removeLink(link
)
869 response
= Response("", status
=202, mimetype
="application/json")
870 response
.headers
['Access-Control-Allow-Origin'] = '*'
873 except Exception as ex
:
874 LOG
.exception(u
"%s: Could not detach interface from the server." % __name__
)
875 return ex
.message
, 500
878 class NovaLimits(Resource
):
879 def __init__(self
, api
):
884 Returns the resource limits of the emulated cloud.
885 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
887 TODO: For now we only return fixed limits, not based on the real deployment.
889 :param id: tenant id, used for the 'href' link
891 :return: Returns the resource limits.
892 :rtype: :class:`flask.response`
894 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
899 "maxImageMeta": 12800,
900 "maxPersonality": 500,
901 "maxPersonalitySize": 1024000,
902 "maxSecurityGroupRules": 2000,
903 "maxSecurityGroups": 1000,
904 "maxServerMeta": 12800,
905 "maxTotalCores": 2000,
906 "maxTotalFloatingIps": 1000,
907 "maxTotalInstances": 1000,
908 "maxTotalKeypairs": 1000,
909 "maxTotalRAMSize": 5120000,
910 "maxServerGroups": 1000,
911 "maxServerGroupMembers": 1000,
913 "totalInstancesUsed": 0,
915 "totalSecurityGroupsUsed": 0,
916 "totalFloatingIpsUsed": 0,
917 "totalServerGroupsUsed": 0
922 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
923 response
.headers
['Access-Control-Allow-Origin'] = '*'
926 except Exception as ex
:
927 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
928 return ex
.message
, 500