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 return Response("Server with name %s already exists." % name
, status
=409)
230 # TODO: not finished!
233 server
= self
.api
.compute
.create_server(name
)
234 server
.full_name
= str(self
.api
.compute
.dc
.label
) + "_" + server_dict
["name"]
235 server
.template_name
= server_dict
["name"]
236 if "metadata" in server_dict
:
237 server
.properties
= server_dict
["metadata"]
239 for flavor
in self
.api
.compute
.flavors
.values():
240 if flavor
.id == server_dict
.get('flavorRef', ''):
241 server
.flavor
= flavor
.name
242 for image
in self
.api
.compute
.images
.values():
243 if image
.id in server_dict
['imageRef']:
244 server
.image
= image
.name
246 if networks
is not None:
248 port
= self
.api
.compute
.find_port_by_name_or_id(net
.get('port', ""))
250 server
.port_names
.append(port
.name
)
252 return Response("Currently only networking by port is supported.", status
=400)
254 self
.api
.compute
._start
_compute
(server
)
256 response
= NovaShowServerDetails(self
.api
).get(id, server
.id)
257 response
.headers
['Access-Control-Allow-Origin'] = '*'
260 except Exception as ex
:
261 LOG
.exception(u
"%s: Could not create the server." % __name__
)
262 return ex
.message
, 500
265 class NovaListServersAndPortsApi(Resource
):
266 def __init__(self
, api
):
271 Creates a list with all running servers and their detailed information. This function also presents all
272 port information of each server.
274 :param id: Used to create a individual link to quarry further information.
276 :return: Returns a json response with a dictionary that contains the server information.
277 :rtype: :class:`flask.response`
279 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
283 resp
['servers'] = list()
284 for server
in self
.api
.compute
.computeUnits
.values():
285 s
= server
.create_server_dict(self
.api
.compute
)
286 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
292 for port_name
in server
.port_names
:
293 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
297 tmp
= port
.create_port_dict(self
.api
.compute
)
298 tmp
['intf_name'] = port
.intf_name
299 s
['ports'].append(tmp
)
301 resp
['servers'].append(s
)
303 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
304 response
.headers
['Access-Control-Allow-Origin'] = '*'
307 except Exception as ex
:
308 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
309 return ex
.message
, 500
312 class NovaListServersDetailed(Resource
):
313 def __init__(self
, api
):
318 As List Servers, it lists all running servers and their details but furthermore it also states the
319 used flavor and the server image.
321 :param id: tenant id, used for the 'href' link.
323 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
324 :rtype: :class:`flask.response`
326 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
329 resp
= {"servers": list()}
330 for server
in self
.api
.compute
.computeUnits
.values():
331 s
= server
.create_server_dict(self
.api
.compute
)
332 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
336 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
341 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
349 image
= self
.api
.compute
.images
[server
.image
]
354 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
363 resp
['servers'].append(s
)
365 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
366 response
.headers
['Access-Control-Allow-Origin'] = '*'
369 except Exception as ex
:
370 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
371 return ex
.message
, 500
374 class NovaListFlavors(Resource
):
375 def __init__(self
, api
):
380 Lists all available flavors.
382 :param id: tenant id, used for the 'href' link
384 :return: Returns a flask response with a list of all flavors.
385 :rtype: :class:`flask.response`
387 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
390 resp
['flavors'] = list()
391 for flavor
in self
.api
.compute
.flavors
.values():
392 f
= flavor
.__dict
__.copy()
394 f
['name'] = flavor
.name
395 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
399 resp
['flavors'].append(f
)
401 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
402 response
.headers
['Access-Control-Allow-Origin'] = '*'
405 except Exception as ex
:
406 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
407 return ex
.message
, 500
410 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
411 data
= json
.loads(request
.data
).get("flavor")
412 LOG
.warning("Create Flavor: %s" % str(data
))
413 # add to internal dict
414 f
= self
.api
.compute
.add_flavor(
417 data
.get("ram"), "MB",
418 data
.get("disk"), "GB")
419 # create response based on incoming data
421 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
425 resp
= {"flavor": data
}
426 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
429 class NovaListFlavorsDetails(Resource
):
430 def __init__(self
, api
):
435 Lists all flavors with additional information like ram and disk space.
437 :param id: tenant id, used for the 'href' link
439 :return: Returns a flask response with a list of all flavors with additional information.
440 :rtype: :class:`flask.response`
442 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
445 resp
['flavors'] = list()
446 for flavor
in self
.api
.compute
.flavors
.values():
447 # use the class dict. it should work fine
448 # but use a copy so we don't modifiy the original
449 f
= flavor
.__dict
__.copy()
450 # add additional expected stuff stay openstack compatible
451 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
455 f
['OS-FLV-DISABLED:disabled'] = False
456 f
['OS-FLV-EXT-DATA:ephemeral'] = 0
457 f
['os-flavor-access:is_public'] = True
458 f
['ram'] = flavor
.memory
459 f
['vcpus'] = flavor
.cpu
461 f
['disk'] = flavor
.storage
462 f
['rxtx_factor'] = 1.0
463 resp
['flavors'].append(f
)
465 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
466 response
.headers
['Access-Control-Allow-Origin'] = '*'
469 except Exception as ex
:
470 LOG
.exception(u
"%s: Could not retrieve the list of servers." % __name__
)
471 return ex
.message
, 500
474 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
475 data
= json
.loads(request
.data
).get("flavor")
476 LOG
.warning("Create Flavor: %s" % str(data
))
477 # add to internal dict
478 f
= self
.api
.compute
.add_flavor(
481 data
.get("ram"), "MB",
482 data
.get("disk"), "GB")
483 # create response based on incoming data
485 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
489 resp
= {"flavor": data
}
490 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
493 class NovaListFlavorById(Resource
):
494 def __init__(self
, api
):
497 def get(self
, id, flavorid
):
499 Returns details about one flavor.
501 :param id: tenant id, used for the 'href' link
503 :param flavorid: Represents the flavor.
504 :type flavorid: ``str``
505 :return: Returns a flask response with detailed information about the flavor.
506 :rtype: :class:`flask.response`
508 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
511 resp
['flavor'] = dict()
512 flavor
= self
.api
.compute
.flavors
.get(flavorid
, None)
514 for f
in self
.api
.compute
.flavors
.values():
518 resp
['flavor']['id'] = flavor
.id
519 resp
['flavor']['name'] = flavor
.name
520 resp
['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
524 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
525 response
.headers
['Access-Control-Allow-Origin'] = '*'
528 except Exception as ex
:
529 LOG
.exception(u
"%s: Could not retrieve flavor with id %s" % (__name__
, flavorid
))
530 return ex
.message
, 500
532 def delete(self
, id, flavorid
):
534 Removes the given flavor.
535 Does not really remove anything from the machine, just fakes an OK.
537 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
538 return Response("{}", status
=204, mimetype
="application/json")
541 class NovaListImages(Resource
):
542 def __init__(self
, api
):
547 Creates a list of all usable images.
549 :param id: tenant id, used for the 'href' link
551 :return: Returns a flask response with a list of available images.
552 :rtype: :class:`flask.response`
554 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
557 resp
['images'] = list()
558 for image
in self
.api
.compute
.images
.values():
561 f
['name'] = str(image
.name
).replace(":latest", "")
562 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
566 resp
['images'].append(f
)
567 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
568 response
.headers
['Access-Control-Allow-Origin'] = '*'
571 except Exception as ex
:
572 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
573 return ex
.message
, 500
576 class NovaListImagesDetails(Resource
):
577 def __init__(self
, api
):
582 As List Images but with additional metadata.
584 :param id: tenant id, used for the 'href' link
586 :return: Returns a flask response with a list of images and their metadata.
587 :rtype: :class:`flask.response`
589 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
592 resp
['images'] = list()
593 for image
in self
.api
.compute
.images
.values():
594 # use the class dict. it should work fine
595 # but use a copy so we don't modifiy the original
596 f
= image
.__dict
__.copy()
597 # add additional expected stuff stay openstack compatible
598 f
['name'] = str(image
.name
).replace(":latest", "")
599 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
604 "architecture": "x86_64",
605 "auto_disk_config": "True",
606 "kernel_id": "nokernel",
607 "ramdisk_id": "nokernel"
609 resp
['images'].append(f
)
611 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
612 response
.headers
['Access-Control-Allow-Origin'] = '*'
615 except Exception as ex
:
616 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
617 return ex
.message
, 500
620 class NovaListImageById(Resource
):
621 def __init__(self
, api
):
624 def get(self
, id, imageid
):
626 Gets an image by id from the emulator with openstack nova compliant return values.
628 :param id: tenantid, we ignore this most of the time
630 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
631 :type imageid: ``str``
632 :return: Returns a flask response with the information about one image.
633 :rtype: :class:`flask.response`
635 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
638 i
= resp
['image'] = dict()
639 for image
in self
.api
.compute
.images
.values():
640 if image
.id == imageid
or image
.name
== imageid
:
642 i
['name'] = image
.name
644 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
646 response
= Response("Image with id or name %s does not exists." % imageid
, status
=404)
647 response
.headers
['Access-Control-Allow-Origin'] = '*'
650 except Exception as ex
:
651 LOG
.exception(u
"%s: Could not retrieve image with id %s." % (__name__
, imageid
))
652 return ex
.message
, 500
654 def delete(self
, id, imageid
):
656 Removes the given image.
657 Does not really remove anything from the machine, just fakes an OK.
659 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
660 return Response("{}", status
=204, mimetype
="application/json")
663 class NovaShowServerDetails(Resource
):
664 def __init__(self
, api
):
667 def get(self
, id, serverid
):
669 Returns detailed information about the specified server.
671 :param id: tenant id, used for the 'href' link
673 :param serverid: Specifies the requested server.
674 :type serverid: ``str``
675 :return: Returns a flask response with details about the server.
676 :rtype: :class:`flask.response`
678 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
680 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
682 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
683 s
= server
.create_server_dict()
684 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
689 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
694 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
702 image
= self
.api
.compute
.images
[server
.image
]
707 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
716 response
= Response(json
.dumps({'server': s
}), status
=200, mimetype
="application/json")
717 response
.headers
['Access-Control-Allow-Origin'] = '*'
720 except Exception as ex
:
721 LOG
.exception(u
"%s: Could not retrieve the server details." % __name__
)
722 return ex
.message
, 500
724 def delete(self
, id, serverid
):
726 Delete a server instance.
728 :param id: tenant id, we ignore this most of the time
730 :param serverid: The UUID of the server
731 :type serverid: ``str``
732 :return: Returns 200 if everything is fine.
733 :rtype: :class:`flask.response`
735 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
737 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
739 return Response('Could not find server.', status
=404, mimetype
="application/json")
741 self
.api
.compute
.stop_compute(server
)
743 response
= Response('Server deleted.', status
=204, mimetype
="application/json")
744 response
.headers
['Access-Control-Allow-Origin'] = '*'
747 except Exception as ex
:
748 LOG
.exception(u
"%s: Could not create the server." % __name__
)
749 return ex
.message
, 500
752 class NovaInterfaceToServer(Resource
):
753 def __init__(self
, api
):
756 def post(self
, id, serverid
):
758 Add an interface to the specified server.
760 :param id: tenant id, we ignore this most of the time
762 :param serverid: Specifies the server.
763 :type serverid: ``str``
764 :return: Returns a flask response with information about the attached interface.
765 :rtype: :class:`flask.response`
767 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
769 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
771 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
773 if server
.emulator_compute
is None:
774 LOG
.error("The targeted container does not exist.")
775 return Response("The targeted container of %s does not exist." % serverid
, status
=404)
776 data
= json
.loads(request
.data
).get("interfaceAttachment")
778 port
= data
.get("port_id", None)
779 net
= data
.get("net_id", None)
780 dc
= self
.api
.compute
.dc
781 network_dict
= dict()
784 if net
is not None and port
is not None:
785 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
786 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
787 network_dict
['id'] = port
.intf_name
788 network_dict
['ip'] = port
.ip_address
789 network_dict
[network_dict
['id']] = network
.name
790 elif net
is not None:
791 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
793 return Response("Network with id or name %s does not exists." % net
, status
=404)
794 port
= self
.api
.compute
.create_port("port:cp%s:fl:%s" %
795 (len(self
.api
.compute
.ports
), str(uuid
.uuid4())))
797 port
.net_name
= network
.name
798 port
.ip_address
= network
.get_new_ip_address(port
.name
)
799 network_dict
['id'] = port
.intf_name
800 network_dict
['ip'] = port
.ip_address
801 network_dict
[network_dict
['id']] = network
.name
802 elif port
is not None:
803 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
804 network_dict
['id'] = port
.intf_name
805 network_dict
['ip'] = port
.ip_address
806 network
= self
.api
.compute
.find_network_by_name_or_id(port
.net_name
)
807 network_dict
[network_dict
['id']] = network
.name
809 raise Exception("You can only attach interfaces by port or network at the moment")
811 if network
== self
.api
.manage
.floating_network
:
812 dc
.net
.addLink(server
.emulator_compute
, self
.api
.manage
.floating_switch
,
813 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
815 dc
.net
.addLink(server
.emulator_compute
, dc
.switch
,
816 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
817 resp
["port_state"] = "ACTIVE"
818 resp
["port_id"] = port
.id
819 resp
["net_id"] = self
.api
.compute
.find_network_by_name_or_id(port
.net_name
).id
820 resp
["mac_addr"] = port
.mac_address
821 resp
["fixed_ips"] = list()
823 fixed_ips
["ip_address"] = port
.ip_address
824 fixed_ips
["subnet_id"] = network
.subnet_name
825 resp
["fixed_ips"].append(fixed_ips
)
826 response
= Response(json
.dumps({"interfaceAttachment": resp
}), status
=202, mimetype
="application/json")
827 response
.headers
['Access-Control-Allow-Origin'] = '*'
830 except Exception as ex
:
831 LOG
.exception(u
"%s: Could not add interface to the server." % __name__
)
832 return ex
.message
, 500
835 class NovaShowAndDeleteInterfaceAtServer(Resource
):
836 def __init__(self
, api
):
839 def delete(self
, id, serverid
, port_id
):
841 Deletes an existing interface.
843 :param id: tenant id, we ignore this most of the time
845 :param serverid: Specifies the server, where the interface will be deleted.
846 :type serverid: ``str``
847 :param port_id: Specifies the port of the interface.
848 :type port_id: ``str``
849 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
851 :rtype: :class:`flask.response`
853 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
855 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
857 return Response("Server with id or name %s does not exists." % serverid
, status
=404)
858 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
860 return Response("Port with id or name %s does not exists." % port_id
, status
=404)
862 for link
in self
.api
.compute
.dc
.net
.links
:
863 if str(link
.intf1
) == port
.intf_name
and \
864 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
865 self
.api
.compute
.dc
.net
.removeLink(link
)
868 response
= Response("", status
=202, mimetype
="application/json")
869 response
.headers
['Access-Control-Allow-Origin'] = '*'
872 except Exception as ex
:
873 LOG
.exception(u
"%s: Could not detach interface from the server." % __name__
)
874 return ex
.message
, 500
877 class NovaLimits(Resource
):
878 def __init__(self
, api
):
883 Returns the resource limits of the emulated cloud.
884 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
886 TODO: For now we only return fixed limits, not based on the real deployment.
888 :param id: tenant id, used for the 'href' link
890 :return: Returns the resource limits.
891 :rtype: :class:`flask.response`
893 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
898 "maxImageMeta": 12800,
899 "maxPersonality": 500,
900 "maxPersonalitySize": 1024000,
901 "maxSecurityGroupRules": 2000,
902 "maxSecurityGroups": 1000,
903 "maxServerMeta": 12800,
904 "maxTotalCores": 2000,
905 "maxTotalFloatingIps": 1000,
906 "maxTotalInstances": 1000,
907 "maxTotalKeypairs": 1000,
908 "maxTotalRAMSize": 5120000,
909 "maxServerGroups": 1000,
910 "maxServerGroupMembers": 1000,
912 "totalInstancesUsed": 0,
914 "totalSecurityGroupsUsed": 0,
915 "totalFloatingIpsUsed": 0,
916 "totalServerGroupsUsed": 0
921 response
= Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
922 response
.headers
['Access-Control-Allow-Origin'] = '*'
925 except Exception as ex
:
926 LOG
.exception(u
"%s: Could not retrieve the list of images." % __name__
)
927 return ex
.message
, 500