1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # Neither the name of the SONATA-NFV, Paderborn University
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior written
21 # This work has been performed in the framework of the SONATA project,
22 # funded by the European Commission under Grant number 671517 through
23 # the Horizon 2020 and 5G-PPP programmes. The authors would like to
24 # acknowledge the contributions of their colleagues of the SONATA
25 # partner consortium (www.sonata-nfv.eu).
26 from flask_restful
import Resource
27 from flask
import Response
, request
28 from emuvim
.api
.openstack
.openstack_dummies
.base_openstack_dummy
import BaseOpenstackDummy
29 from emuvim
.api
.openstack
.helper
import get_host
33 from mininet
.link
import Link
36 LOG
= logging
.getLogger("api.openstack.nova")
39 class NovaDummyApi(BaseOpenstackDummy
):
40 def __init__(self
, in_ip
, in_port
, compute
):
41 super(NovaDummyApi
, self
).__init
__(in_ip
, in_port
)
42 self
.compute
= compute
43 self
.compute
.add_flavor('m1.tiny', 1, 512, "MB", 1, "GB")
44 self
.compute
.add_flavor('m1.nano', 1, 64, "MB", 0, "GB")
45 self
.compute
.add_flavor('m1.micro', 1, 128, "MB", 0, "GB")
46 self
.compute
.add_flavor('m1.small', 1, 1024, "MB", 2, "GB")
48 self
.api
.add_resource(NovaVersionsList
, "/",
49 resource_class_kwargs
={'api': self
})
50 self
.api
.add_resource(NovaVersionShow
, "/v2.1/<id>",
51 resource_class_kwargs
={'api': self
})
52 self
.api
.add_resource(NovaListServersApi
, "/v2.1/<id>/servers",
53 resource_class_kwargs
={'api': self
})
54 self
.api
.add_resource(NovaListServersAndPortsApi
, "/v2.1/<id>/servers/andPorts",
55 resource_class_kwargs
={'api': self
})
56 self
.api
.add_resource(NovaListServersDetailed
, "/v2.1/<id>/servers/detail",
57 resource_class_kwargs
={'api': self
})
58 self
.api
.add_resource(NovaShowServerDetails
, "/v2.1/<id>/servers/<serverid>",
59 resource_class_kwargs
={'api': self
})
60 self
.api
.add_resource(NovaInterfaceToServer
, "/v2.1/<id>/servers/<serverid>/os-interface",
61 resource_class_kwargs
={'api': self
})
62 self
.api
.add_resource(NovaShowAndDeleteInterfaceAtServer
, "/v2.1/<id>/servers/<serverid>/os-interface/<port_id>",
63 resource_class_kwargs
={'api': self
})
64 self
.api
.add_resource(NovaListFlavors
, "/v2.1/<id>/flavors", "/v2/<id>/flavors",
65 resource_class_kwargs
={'api': self
})
66 self
.api
.add_resource(NovaListFlavorsDetails
, "/v2.1/<id>/flavors/detail", "/v2/<id>/flavors/detail",
67 resource_class_kwargs
={'api': self
})
68 self
.api
.add_resource(NovaListFlavorById
, "/v2.1/<id>/flavors/<flavorid>", "/v2/<id>/flavors/<flavorid>",
69 resource_class_kwargs
={'api': self
})
70 self
.api
.add_resource(NovaListImages
, "/v2.1/<id>/images",
71 resource_class_kwargs
={'api': self
})
72 self
.api
.add_resource(NovaListImagesDetails
, "/v2.1/<id>/images/detail",
73 resource_class_kwargs
={'api': self
})
74 self
.api
.add_resource(NovaListImageById
, "/v2.1/<id>/images/<imageid>",
75 resource_class_kwargs
={'api': self
})
76 self
.api
.add_resource(NovaLimits
, "/v2.1/<id>/limits",
77 resource_class_kwargs
={'api': self
})
80 class NovaVersionsList(Resource
):
81 def __init__(self
, api
):
88 :return: Returns a json with API versions.
89 :rtype: :class:`flask.response`
91 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
100 "href": "http://%s:%d/v2.1/",
106 "min_version": "2.1",
107 "updated": "2013-07-23T11:33:21Z"
111 """ % (get_host(request
), self
.api
.port
)
113 response
= Response(resp
, status
=200, mimetype
="application/json")
114 response
.headers
['Access-Control-Allow-Origin'] = '*'
117 except Exception as ex
:
118 LOG
.exception(u
"%s: Could not show list of versions." % __name__
)
119 return ex
.message
, 500
122 class NovaVersionShow(Resource
):
123 def __init__(self
, api
):
132 :return: Returns a json with API details.
133 :rtype: :class:`flask.response`
135 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
144 "href": "http://%s:%d/v2.1/",
148 "href": "http://docs.openstack.org/",
149 "rel": "describedby",
155 "base": "application/json",
156 "type": "application/vnd.openstack.compute+json;version=2.1"
161 "min_version": "2.1",
162 "updated": "2013-07-23T11:33:21Z"
165 """ % (get_host(request
), self
.api
.port
)
167 response
= Response(resp
, status
=200, mimetype
="application/json")
168 response
.headers
['Access-Control-Allow-Origin'] = '*'
171 except Exception as ex
:
172 LOG
.exception(u
"%s: Could not show list of versions." % __name__
)
173 return ex
.message
, 500
176 class NovaListServersApi(Resource
):
177 def __init__(self
, api
):
182 Creates a list with all running servers and their detailed information.
184 :param id: Used to create a individual link to quarry further information.
186 :return: Returns a json response with a dictionary that contains the server information.
187 :rtype: :class:`flask.response`
189 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
193 resp
['servers'] = list()
194 for server
in self
.api
.compute
.computeUnits
.values():
195 s
= server
.create_server_dict(self
.api
.compute
)
196 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
201 resp
['servers'].append(s
)
203 response
= Response(json
.dumps(resp
), status
=200,
204 mimetype
="application/json")
205 response
.headers
['Access-Control-Allow-Origin'] = '*'
208 except Exception as ex
:
210 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
)
231 "Server with name %s already exists." % name
, status
=409)
232 # TODO: not finished!
233 server
= self
.api
.compute
.create_server(name
)
234 server
.full_name
= str(
235 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_name_or_id
= net
.get('port', "")
250 port
= self
.api
.compute
.find_port_by_name_or_id(port_name_or_id
)
252 server
.port_names
.append(port_name_or_id
)
255 "Currently only networking by port is supported.", status
=400)
257 self
.api
.compute
._start
_compute
(server
)
259 response
= NovaShowServerDetails(self
.api
).get(id, server
.id)
260 response
.headers
['Access-Control-Allow-Origin'] = '*'
263 except Exception as ex
:
264 LOG
.exception(u
"%s: Could not create the server." % __name__
)
265 return ex
.message
, 500
268 class NovaListServersAndPortsApi(Resource
):
269 def __init__(self
, api
):
274 Creates a list with all running servers and their detailed information. This function also presents all
275 port information of each server.
277 :param id: Used to create a individual link to quarry further information.
279 :return: Returns a json response with a dictionary that contains the server information.
280 :rtype: :class:`flask.response`
282 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
286 resp
['servers'] = list()
287 for server
in self
.api
.compute
.computeUnits
.values():
288 s
= server
.create_server_dict(self
.api
.compute
)
289 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
295 for port_name
in server
.port_names
:
296 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
300 tmp
= port
.create_port_dict(self
.api
.compute
)
301 tmp
['intf_name'] = port
.intf_name
302 s
['ports'].append(tmp
)
304 resp
['servers'].append(s
)
306 response
= Response(json
.dumps(resp
), status
=200,
307 mimetype
="application/json")
308 response
.headers
['Access-Control-Allow-Origin'] = '*'
311 except Exception as ex
:
313 u
"%s: Could not retrieve the list of servers." % __name__
)
314 return ex
.message
, 500
317 class NovaListServersDetailed(Resource
):
318 def __init__(self
, api
):
323 As List Servers, it lists all running servers and their details but furthermore it also states the
324 used flavor and the server image.
326 :param id: tenant id, used for the 'href' link.
328 :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
329 :rtype: :class:`flask.response`
331 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
334 resp
= {"servers": list()}
335 for server
in self
.api
.compute
.computeUnits
.values():
336 s
= server
.create_server_dict(self
.api
.compute
)
337 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
341 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
346 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
354 image
= self
.api
.compute
.images
[server
.image
]
359 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
368 resp
['servers'].append(s
)
370 response
= Response(json
.dumps(resp
), status
=200,
371 mimetype
="application/json")
372 response
.headers
['Access-Control-Allow-Origin'] = '*'
375 except Exception as ex
:
377 u
"%s: Could not retrieve the list of servers." % __name__
)
378 return ex
.message
, 500
381 class NovaListFlavors(Resource
):
382 def __init__(self
, api
):
387 Lists all available flavors.
389 :param id: tenant id, used for the 'href' link
391 :return: Returns a flask response with a list of all flavors.
392 :rtype: :class:`flask.response`
394 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
397 resp
['flavors'] = list()
398 for flavor
in self
.api
.compute
.flavors
.values():
399 f
= flavor
.__dict
__.copy()
401 f
['name'] = flavor
.name
402 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
406 resp
['flavors'].append(f
)
408 response
= Response(json
.dumps(resp
), status
=200,
409 mimetype
="application/json")
410 response
.headers
['Access-Control-Allow-Origin'] = '*'
413 except Exception as ex
:
415 u
"%s: Could not retrieve the list of servers." % __name__
)
416 return ex
.message
, 500
419 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
420 data
= json
.loads(request
.data
).get("flavor")
421 LOG
.warning("Create Flavor: %s" % str(data
))
422 # add to internal dict
423 f
= self
.api
.compute
.add_flavor(
426 data
.get("ram"), "MB",
427 data
.get("disk"), "GB")
428 # create response based on incoming data
430 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
434 resp
= {"flavor": data
}
435 return Response(json
.dumps(resp
), status
=200,
436 mimetype
="application/json")
439 class NovaListFlavorsDetails(Resource
):
440 def __init__(self
, api
):
445 Lists all flavors with additional information like ram and disk space.
447 :param id: tenant id, used for the 'href' link
449 :return: Returns a flask response with a list of all flavors with additional information.
450 :rtype: :class:`flask.response`
452 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
455 resp
['flavors'] = list()
456 for flavor
in self
.api
.compute
.flavors
.values():
457 # use the class dict. it should work fine
458 # but use a copy so we don't modifiy the original
459 f
= flavor
.__dict
__.copy()
460 # add additional expected stuff stay openstack compatible
461 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
465 f
['OS-FLV-DISABLED:disabled'] = False
466 f
['OS-FLV-EXT-DATA:ephemeral'] = 0
467 f
['os-flavor-access:is_public'] = True
468 f
['ram'] = flavor
.memory
469 f
['vcpus'] = flavor
.cpu
471 f
['disk'] = flavor
.storage
472 f
['rxtx_factor'] = 1.0
473 resp
['flavors'].append(f
)
475 response
= Response(json
.dumps(resp
), status
=200,
476 mimetype
="application/json")
477 response
.headers
['Access-Control-Allow-Origin'] = '*'
480 except Exception as ex
:
482 u
"%s: Could not retrieve the list of servers." % __name__
)
483 return ex
.message
, 500
486 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
487 data
= json
.loads(request
.data
).get("flavor")
488 LOG
.warning("Create Flavor: %s" % str(data
))
489 # add to internal dict
490 f
= self
.api
.compute
.add_flavor(
493 data
.get("ram"), "MB",
494 data
.get("disk"), "GB")
495 # create response based on incoming data
497 data
["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
501 resp
= {"flavor": data
}
502 return Response(json
.dumps(resp
), status
=200,
503 mimetype
="application/json")
506 class NovaListFlavorById(Resource
):
507 def __init__(self
, api
):
510 def get(self
, id, flavorid
):
512 Returns details about one flavor.
514 :param id: tenant id, used for the 'href' link
516 :param flavorid: Represents the flavor.
517 :type flavorid: ``str``
518 :return: Returns a flask response with detailed information about the flavor.
519 :rtype: :class:`flask.response`
521 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
524 resp
['flavor'] = dict()
525 flavor
= self
.api
.compute
.flavors
.get(flavorid
, None)
527 for f
in self
.api
.compute
.flavors
.values():
531 resp
['flavor']['id'] = flavor
.id
532 resp
['flavor']['name'] = flavor
.name
533 resp
['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
537 response
= Response(json
.dumps(resp
), status
=200,
538 mimetype
="application/json")
539 response
.headers
['Access-Control-Allow-Origin'] = '*'
542 except Exception as ex
:
543 LOG
.exception(u
"%s: Could not retrieve flavor with id %s" %
544 (__name__
, flavorid
))
545 return ex
.message
, 500
547 def delete(self
, id, flavorid
):
549 Removes the given flavor.
550 Does not really remove anything from the machine, just fakes an OK.
552 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
553 return Response("", status
=204, mimetype
="application/json")
556 class NovaListImages(Resource
):
557 def __init__(self
, api
):
562 Creates a list of all usable images.
564 :param id: tenant id, used for the 'href' link
566 :return: Returns a flask response with a list of available images.
567 :rtype: :class:`flask.response`
569 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
572 resp
['images'] = list()
573 for image
in self
.api
.compute
.images
.values():
576 f
['name'] = str(image
.name
).replace(":latest", "")
577 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
581 resp
['images'].append(f
)
582 response
= Response(json
.dumps(resp
), status
=200,
583 mimetype
="application/json")
584 response
.headers
['Access-Control-Allow-Origin'] = '*'
587 except Exception as ex
:
589 u
"%s: Could not retrieve the list of images." % __name__
)
590 return ex
.message
, 500
593 class NovaListImagesDetails(Resource
):
594 def __init__(self
, api
):
599 As List Images but with additional metadata.
601 :param id: tenant id, used for the 'href' link
603 :return: Returns a flask response with a list of images and their metadata.
604 :rtype: :class:`flask.response`
606 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
609 resp
['images'] = list()
610 for image
in self
.api
.compute
.images
.values():
611 # use the class dict. it should work fine
612 # but use a copy so we don't modifiy the original
613 f
= image
.__dict
__.copy()
614 # add additional expected stuff stay openstack compatible
615 f
['name'] = str(image
.name
).replace(":latest", "")
616 f
['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
621 "architecture": "x86_64",
622 "auto_disk_config": "True",
623 "kernel_id": "nokernel",
624 "ramdisk_id": "nokernel"
626 resp
['images'].append(f
)
628 response
= Response(json
.dumps(resp
), status
=200,
629 mimetype
="application/json")
630 response
.headers
['Access-Control-Allow-Origin'] = '*'
633 except Exception as ex
:
635 u
"%s: Could not retrieve the list of images." % __name__
)
636 return ex
.message
, 500
639 class NovaListImageById(Resource
):
640 def __init__(self
, api
):
643 def get(self
, id, imageid
):
645 Gets an image by id from the emulator with openstack nova compliant return values.
647 :param id: tenantid, we ignore this most of the time
649 :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
650 :type imageid: ``str``
651 :return: Returns a flask response with the information about one image.
652 :rtype: :class:`flask.response`
654 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
657 i
= resp
['image'] = dict()
658 for image
in self
.api
.compute
.images
.values():
659 if image
.id == imageid
or image
.name
== imageid
:
661 i
['name'] = image
.name
663 return Response(json
.dumps(resp
), status
=200,
664 mimetype
="application/json")
667 "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." %
674 return ex
.message
, 500
676 def delete(self
, id, imageid
):
678 Removes the given image.
679 Does not really remove anything from the machine, just fakes an OK.
681 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
682 return Response("", status
=204, mimetype
="application/json")
685 class NovaShowServerDetails(Resource
):
686 def __init__(self
, api
):
689 def get(self
, id, serverid
):
691 Returns detailed information about the specified server.
693 :param id: tenant id, used for the 'href' link
695 :param serverid: Specifies the requested server.
696 :type serverid: ``str``
697 :return: Returns a flask response with details about the server.
698 :rtype: :class:`flask.response`
700 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
702 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
705 "Server with id or name %s does not exists." % serverid
, status
=404)
706 s
= server
.create_server_dict()
707 s
['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request
),
712 flavor
= self
.api
.compute
.flavors
[server
.flavor
]
717 "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request
),
725 image
= self
.api
.compute
.images
[server
.image
]
730 "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request
),
739 response
= Response(json
.dumps(
740 {'server': s
}), status
=200, mimetype
="application/json")
741 response
.headers
['Access-Control-Allow-Origin'] = '*'
744 except Exception as ex
:
746 u
"%s: Could not retrieve the server details." % __name__
)
747 return ex
.message
, 500
749 def delete(self
, id, serverid
):
751 Delete a server instance.
753 :param id: tenant id, we ignore this most of the time
755 :param serverid: The UUID of the server
756 :type serverid: ``str``
757 :return: Returns 204 if everything is fine.
758 :rtype: :class:`flask.response`
760 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
762 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
764 return Response('Could not find server.',
765 status
=404, mimetype
="application/json")
767 self
.api
.compute
.stop_compute(server
)
769 response
= Response('', status
=204, mimetype
="application/json")
770 response
.headers
['Access-Control-Allow-Origin'] = '*'
773 except Exception as ex
:
774 LOG
.exception(u
"%s: Could not create the server." % __name__
)
775 return ex
.message
, 500
778 class NovaInterfaceToServer(Resource
):
779 def __init__(self
, api
):
782 def post(self
, id, serverid
):
784 Add an interface to the specified server.
786 :param id: tenant id, we ignore this most of the time
788 :param serverid: Specifies the server.
789 :type serverid: ``str``
790 :return: Returns a flask response with information about the attached interface.
791 :rtype: :class:`flask.response`
793 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
795 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
798 "Server with id or name %s does not exists." % serverid
, status
=404)
800 if server
.emulator_compute
is None:
801 LOG
.error("The targeted container does not exist.")
803 "The targeted container of %s does not exist." % serverid
, status
=404)
804 data
= json
.loads(request
.data
).get("interfaceAttachment")
806 port
= data
.get("port_id", None)
807 net
= data
.get("net_id", None)
808 dc
= self
.api
.compute
.dc
809 network_dict
= dict()
812 if net
is not None and port
is not None:
813 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
814 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
815 network_dict
['id'] = port
.intf_name
816 network_dict
['ip'] = port
.ip_address
817 network_dict
[network_dict
['id']] = network
.name
818 elif net
is not None:
819 network
= self
.api
.compute
.find_network_by_name_or_id(net
)
822 "Network with id or name %s does not exists." % net
, status
=404)
823 port
= self
.api
.compute
.create_port("port:cp%s:fl:%s" %
824 (len(self
.api
.compute
.ports
), str(uuid
.uuid4())))
826 port
.net_name
= network
.name
827 port
.ip_address
= network
.get_new_ip_address(port
.name
)
828 network_dict
['id'] = port
.intf_name
829 network_dict
['ip'] = port
.ip_address
830 network_dict
[network_dict
['id']] = network
.name
831 elif port
is not None:
832 port
= self
.api
.compute
.find_port_by_name_or_id(port
)
833 network_dict
['id'] = port
.intf_name
834 network_dict
['ip'] = port
.ip_address
835 network
= self
.api
.compute
.find_network_by_name_or_id(
837 network_dict
[network_dict
['id']] = network
.name
840 "You can only attach interfaces by port or network at the moment")
842 if network
== self
.api
.manage
.floating_network
:
843 dc
.net
.addLink(server
.emulator_compute
, self
.api
.manage
.floating_switch
,
844 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
846 dc
.net
.addLink(server
.emulator_compute
, dc
.switch
,
847 params1
=network_dict
, cls
=Link
, intfName1
=port
.intf_name
)
848 resp
["port_state"] = "ACTIVE"
849 resp
["port_id"] = port
.id
850 resp
["net_id"] = self
.api
.compute
.find_network_by_name_or_id(
852 resp
["mac_addr"] = port
.mac_address
853 resp
["fixed_ips"] = list()
855 fixed_ips
["ip_address"] = port
.ip_address
856 fixed_ips
["subnet_id"] = network
.subnet_name
857 resp
["fixed_ips"].append(fixed_ips
)
858 response
= Response(json
.dumps(
859 {"interfaceAttachment": resp
}), status
=202, mimetype
="application/json")
860 response
.headers
['Access-Control-Allow-Origin'] = '*'
863 except Exception as ex
:
865 u
"%s: Could not add interface to the server." % __name__
)
866 return ex
.message
, 500
869 class NovaShowAndDeleteInterfaceAtServer(Resource
):
870 def __init__(self
, api
):
873 def delete(self
, id, serverid
, port_id
):
875 Deletes an existing interface.
877 :param id: tenant id, we ignore this most of the time
879 :param serverid: Specifies the server, where the interface will be deleted.
880 :type serverid: ``str``
881 :param port_id: Specifies the port of the interface.
882 :type port_id: ``str``
883 :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
885 :rtype: :class:`flask.response`
887 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
889 server
= self
.api
.compute
.find_server_by_name_or_id(serverid
)
892 "Server with id or name %s does not exists." % serverid
, status
=404)
893 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
896 "Port with id or name %s does not exists." % port_id
, status
=404)
898 for link
in self
.api
.compute
.dc
.net
.links
:
899 if str(link
.intf1
) == port
.intf_name
and \
900 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
901 self
.api
.compute
.dc
.net
.removeLink(link
)
904 response
= Response("", status
=202, mimetype
="application/json")
905 response
.headers
['Access-Control-Allow-Origin'] = '*'
908 except Exception as ex
:
910 u
"%s: Could not detach interface from the server." % __name__
)
911 return ex
.message
, 500
914 class NovaLimits(Resource
):
915 def __init__(self
, api
):
920 Returns the resource limits of the emulated cloud.
921 https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits
923 TODO: For now we only return fixed limits, not based on the real deployment.
925 :param id: tenant id, used for the 'href' link
927 :return: Returns the resource limits.
928 :rtype: :class:`flask.response`
930 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
935 "maxImageMeta": 12800,
936 "maxPersonality": 500,
937 "maxPersonalitySize": 1024000,
938 "maxSecurityGroupRules": 2000,
939 "maxSecurityGroups": 1000,
940 "maxServerMeta": 12800,
941 "maxTotalCores": 2000,
942 "maxTotalFloatingIps": 1000,
943 "maxTotalInstances": 1000,
944 "maxTotalKeypairs": 1000,
945 "maxTotalRAMSize": 5120000,
946 "maxServerGroups": 1000,
947 "maxServerGroupMembers": 1000,
949 "totalInstancesUsed": 0,
951 "totalSecurityGroupsUsed": 0,
952 "totalFloatingIpsUsed": 0,
953 "totalServerGroupsUsed": 0
958 response
= Response(json
.dumps(resp
), status
=200,
959 mimetype
="application/json")
960 response
.headers
['Access-Control-Allow-Origin'] = '*'
963 except Exception as ex
:
965 u
"%s: Could not retrieve the list of images." % __name__
)
966 return ex
.message
, 500