Return actually reachable IP as part of the Floating IP API
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / neutron_dummy_api.py
1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
2 # ALL RIGHTS RESERVED.
3 #
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
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15 #
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
19 # permission.
20 #
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 request, Response
28 from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
29 from datetime import datetime
30 import neutron_sfc_dummy_api as SFC
31 import logging
32 import json
33 import uuid
34 import copy
35
36 LOG = logging.getLogger("api.openstack.neutron")
37
38
39 class NeutronDummyApi(BaseOpenstackDummy):
40 def __init__(self, ip, port, compute):
41 super(NeutronDummyApi, self).__init__(ip, port)
42 self.compute = compute
43
44 # create default networks (OSM usually assumes to have these
45 # pre-configured)
46 self.compute.create_network("mgmt")
47 self.compute.create_network("mgmtnet")
48
49 self.api.add_resource(NeutronListAPIVersions, "/")
50 self.api.add_resource(NeutronShowAPIv2Details, "/v2.0")
51 self.api.add_resource(NeutronListNetworks, "/v2.0/networks.json", "/v2.0/networks",
52 resource_class_kwargs={'api': self})
53 self.api.add_resource(NeutronShowNetwork, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
54 resource_class_kwargs={'api': self})
55 self.api.add_resource(NeutronCreateNetwork, "/v2.0/networks.json", "/v2.0/networks",
56 resource_class_kwargs={'api': self})
57 self.api.add_resource(NeutronUpdateNetwork, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
58 resource_class_kwargs={'api': self})
59 self.api.add_resource(NeutronDeleteNetwork, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
60 resource_class_kwargs={'api': self})
61 self.api.add_resource(NeutronListSubnets, "/v2.0/subnets.json", "/v2.0/subnets",
62 resource_class_kwargs={'api': self})
63 self.api.add_resource(NeutronShowSubnet, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
64 resource_class_kwargs={'api': self})
65 self.api.add_resource(NeutronCreateSubnet, "/v2.0/subnets.json", "/v2.0/subnets",
66 resource_class_kwargs={'api': self})
67 self.api.add_resource(NeutronUpdateSubnet, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
68 resource_class_kwargs={'api': self})
69 self.api.add_resource(NeutronDeleteSubnet, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
70 resource_class_kwargs={'api': self})
71 self.api.add_resource(NeutronListPorts, "/v2.0/ports.json", "/v2.0/ports",
72 resource_class_kwargs={'api': self})
73 self.api.add_resource(NeutronShowPort, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
74 resource_class_kwargs={'api': self})
75 self.api.add_resource(NeutronCreatePort, "/v2.0/ports.json", "/v2.0/ports",
76 resource_class_kwargs={'api': self})
77 self.api.add_resource(NeutronUpdatePort, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
78 resource_class_kwargs={'api': self})
79 self.api.add_resource(NeutronDeletePort, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
80 resource_class_kwargs={'api': self})
81 self.api.add_resource(NeutronAddFloatingIp, "/v2.0/floatingips.json", "/v2.0/floatingips",
82 resource_class_kwargs={'api': self})
83
84 # Service Function Chaining (SFC) API
85 self.api.add_resource(SFC.PortPairsCreate, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
86 resource_class_kwargs={'api': self})
87 self.api.add_resource(SFC.PortPairsUpdate, "/v2.0/sfc/port_pairs/<pair_id>.json",
88 "/v2.0/sfc/port_pairs/<pair_id>",
89 resource_class_kwargs={'api': self})
90 self.api.add_resource(SFC.PortPairsDelete, "/v2.0/sfc/port_pairs/<pair_id>.json",
91 "/v2.0/sfc/port_pairs/<pair_id>",
92 resource_class_kwargs={'api': self})
93 self.api.add_resource(SFC.PortPairsList, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
94 resource_class_kwargs={'api': self})
95 self.api.add_resource(SFC.PortPairsShow, "/v2.0/sfc/port_pairs/<pair_id>.json",
96 "/v2.0/sfc/port_pairs/<pair_id>",
97 resource_class_kwargs={'api': self})
98
99 self.api.add_resource(SFC.PortPairGroupCreate, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
100 resource_class_kwargs={'api': self})
101 self.api.add_resource(SFC.PortPairGroupUpdate, "/v2.0/sfc/port_pair_groups/<group_id>.json",
102 "/v2.0/sfc/port_pair_groups/<group_id>",
103 resource_class_kwargs={'api': self})
104 self.api.add_resource(SFC.PortPairGroupDelete, "/v2.0/sfc/port_pair_groups/<group_id>.json",
105 "/v2.0/sfc/port_pair_groups/<group_id>",
106 resource_class_kwargs={'api': self})
107 self.api.add_resource(SFC.PortPairGroupList, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
108 resource_class_kwargs={'api': self})
109 self.api.add_resource(SFC.PortPairGroupShow, "/v2.0/sfc/port_pair_groups/<group_id>.json",
110 "/v2.0/sfc/port_pair_groups/<group_id>",
111 resource_class_kwargs={'api': self})
112
113 self.api.add_resource(SFC.FlowClassifierCreate, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
114 resource_class_kwargs={'api': self})
115 self.api.add_resource(SFC.FlowClassifierUpdate, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
116 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
117 resource_class_kwargs={'api': self})
118 self.api.add_resource(SFC.FlowClassifierDelete, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
119 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
120 resource_class_kwargs={'api': self})
121 self.api.add_resource(SFC.FlowClassifierList, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
122 resource_class_kwargs={'api': self})
123 self.api.add_resource(SFC.FlowClassifierShow, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
124 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
125 resource_class_kwargs={'api': self})
126
127 self.api.add_resource(SFC.PortChainCreate, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
128 resource_class_kwargs={'api': self})
129 self.api.add_resource(SFC.PortChainUpdate, "/v2.0/sfc/port_chains/<chain_id>.json",
130 "/v2.0/sfc/port_chains/<chain_id>",
131 resource_class_kwargs={'api': self})
132 self.api.add_resource(SFC.PortChainDelete, "/v2.0/sfc/port_chains/<chain_id>.json",
133 "/v2.0/sfc/port_chains/<chain_id>",
134 resource_class_kwargs={'api': self})
135 self.api.add_resource(SFC.PortChainList, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
136 resource_class_kwargs={'api': self})
137 self.api.add_resource(SFC.PortChainShow, "/v2.0/sfc/port_chains/<chain_id>.json",
138 "/v2.0/sfc/port_chains/<chain_id>",
139 resource_class_kwargs={'api': self})
140
141
142 class NeutronListAPIVersions(Resource):
143 def get(self):
144 """
145 Lists API versions.
146
147 :return: Returns a json with API versions.
148 :rtype: :class:`flask.response`
149 """
150 LOG.debug("API CALL: Neutron - List API Versions")
151 resp = dict()
152 resp['versions'] = dict()
153
154 versions = [{
155 "status": "CURRENT",
156 "id": "v2.0",
157 "links": [
158 {
159 "href": request.url_root + '/v2.0',
160 "rel": "self"
161 }
162 ]
163 }]
164 resp['versions'] = versions
165
166 return Response(json.dumps(resp), status=200,
167 mimetype='application/json')
168
169
170 class NeutronShowAPIv2Details(Resource):
171 def get(self):
172 """
173 Returns API details.
174
175 :return: Returns a json with API details.
176 :rtype: :class:`flask.response`
177 """
178 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
179 resp = dict()
180
181 resp['resources'] = dict()
182 resp['resources'] = [{
183 "links": [
184 {
185 "href": request.url_root + 'v2.0/subnets',
186 "rel": "self"
187 }
188 ],
189 "name": "subnet",
190 "collection": "subnets"
191 },
192 {
193 "links": [
194 {
195 "href": request.url_root + 'v2.0/networks',
196 "rel": "self"
197 }
198 ],
199 "name": "network",
200 "collection": "networks"
201 },
202 {
203 "links": [
204 {
205 "href": request.url_root + 'v2.0/ports',
206 "rel": "self"
207 }
208 ],
209 "name": "ports",
210 "collection": "ports"
211 }
212 ]
213
214 return Response(json.dumps(resp), status=200,
215 mimetype='application/json')
216
217
218 class NeutronListNetworks(Resource):
219 def __init__(self, api):
220 self.api = api
221
222 def get(self):
223 """
224 Lists all networks, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
225 network with the name, or the networks specified via id.
226
227 :return: Returns a json response, starting with 'networks' as root node.
228 :rtype: :class:`flask.response`
229 """
230 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
231 # LOG.debug("ARGS: {}".format(request.args))
232 try:
233 if request.args.get('name'):
234 tmp_network = NeutronShowNetwork(self.api)
235 response = tmp_network.get_network(
236 request.args.get('name'), True)
237 LOG.debug("{} RESPONSE (1): {}".format(
238 self.__class__.__name__, response))
239 return response
240 id_list = request.args.getlist('id')
241 if len(id_list) == 1:
242 tmp_network = NeutronShowNetwork(self.api)
243 response = tmp_network.get_network(
244 request.args.get('id'), True)
245 LOG.debug("{} RESPONSE (2): {}".format(
246 self.__class__.__name__, response))
247 return response
248
249 network_list = list()
250 network_dict = dict()
251
252 if len(id_list) == 0:
253 for net in self.api.compute.nets.values():
254 tmp_network_dict = net.create_network_dict()
255 if tmp_network_dict not in network_list:
256 network_list.append(tmp_network_dict)
257 else:
258 for net in self.api.compute.nets.values():
259 if net.id in id_list:
260 tmp_network_dict = net.create_network_dict()
261 if tmp_network_dict not in network_list:
262 network_list.append(tmp_network_dict)
263
264 network_dict["networks"] = network_list
265 LOG.debug("{} RESPONSE (3): {}".format(
266 self.__class__.__name__, network_dict))
267 return Response(json.dumps(network_dict),
268 status=200, mimetype='application/json')
269
270 except Exception as ex:
271 LOG.exception("Neutron: List networks exception.")
272 return Response(ex.message, status=500,
273 mimetype='application/json')
274
275
276 class NeutronShowNetwork(Resource):
277 def __init__(self, api):
278 self.api = api
279
280 def get(self, network_id):
281 """
282 Returns the network, specified via 'network_id'.
283
284 :param network_id: The unique ID string of the network.
285 :type network_id: ``str``
286 :return: Returns a json response, starting with 'network' as root node and one network description.
287 :rtype: :class:`flask.response`
288 """
289 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
290 return self.get_network(network_id, False)
291
292 def get_network(self, network_name_or_id, as_list):
293 """
294 Returns one network description of the network, specified via 'network_name_or_id'.
295
296 :param network_name_or_id: The indicator string, which specifies the requested network.
297 :type network_name_or_id: ``str``
298 :param as_list: Determines if the network description should start with the root node 'network' or 'networks'.
299 :type as_list: ``bool``
300 :return: Returns a json response, with one network description.
301 :rtype: :class:`flask.response`
302 """
303 try:
304 net = self.api.compute.find_network_by_name_or_id(
305 network_name_or_id)
306 if net is None:
307 return Response(u'Network not found.\n',
308 status=404, mimetype='application/json')
309
310 tmp_network_dict = net.create_network_dict()
311 tmp_dict = dict()
312 if as_list:
313 tmp_dict["networks"] = [tmp_network_dict]
314 else:
315 tmp_dict["network"] = tmp_network_dict
316
317 return Response(json.dumps(tmp_dict), status=200,
318 mimetype='application/json')
319
320 except Exception as ex:
321 logging.exception("Neutron: Show network exception.")
322 return Response(ex.message, status=500,
323 mimetype='application/json')
324
325
326 class NeutronCreateNetwork(Resource):
327 def __init__(self, api):
328 self.api = api
329
330 def post(self):
331 """
332 Creates a network with the name, specified within the request under ['network']['name'].
333
334 :return: * 400, if the network already exists.
335 * 500, if any exception occurred while creation.
336 * 201, if everything worked out.
337 :rtype: :class:`flask.response`
338 """
339 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
340 try:
341 network_dict = json.loads(request.data)
342 name = network_dict['network']['name']
343 net = self.api.compute.find_network_by_name_or_id(name)
344 if net is not None:
345 return Response('Network already exists.\n',
346 status=400, mimetype='application/json')
347
348 net = self.api.compute.create_network(name)
349 return Response(json.dumps(
350 {"network": net.create_network_dict()}), status=201, mimetype='application/json')
351 except Exception as ex:
352 LOG.exception("Neutron: Create network excepiton.")
353 return Response(ex.message, status=500,
354 mimetype='application/json')
355
356
357 class NeutronUpdateNetwork(Resource):
358 def __init__(self, api):
359 self.api = api
360
361 def put(self, network_id): # TODO currently only the name will be changed
362 """
363 Updates the existing network with the given parameters.
364
365 :param network_id: The indicator string, which specifies the requested network.
366 :type network_id: ``str``
367 :return: * 404, if the network could not be found.
368 * 500, if any exception occurred while updating the network.
369 * 200, if everything worked out.
370 :rtype: :class:`flask.response`
371 """
372 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
373 try:
374 if network_id in self.api.compute.nets:
375 net = self.api.compute.nets[network_id]
376 network_dict = json.loads(request.data)
377
378 if "status" in network_dict["network"]:
379 net.status = network_dict["network"]["status"]
380 if "subnets" in network_dict["network"]:
381 pass # tmp_network_dict["subnets"] = None
382 if "name" in network_dict["network"] and net.name != network_dict["network"]["name"]:
383 net.name = network_dict["network"]["name"]
384 if "admin_state_up" in network_dict["network"]:
385 pass # tmp_network_dict["admin_state_up"] = True
386 if "tenant_id" in network_dict["network"]:
387 # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
388 pass
389 if "shared" in network_dict["network"]:
390 pass # tmp_network_dict["shared"] = False
391
392 return Response(json.dumps(network_dict),
393 status=200, mimetype='application/json')
394
395 return Response('Network not found.\n', status=404,
396 mimetype='application/json')
397
398 except Exception as ex:
399 LOG.exception("Neutron: Show networks exception.")
400 return Response(ex.message, status=500,
401 mimetype='application/json')
402
403
404 class NeutronDeleteNetwork(Resource):
405 def __init__(self, api):
406 self.api = api
407
408 def delete(self, network_id):
409 """
410 Deletes the specified network and all its subnets.
411
412 :param network_id: The indicator string, which specifies the requested network.
413 :type network_id: ``str``
414 :return: * 404, if the network or the subnet could not be removed.
415 * 500, if any exception occurred while deletion.
416 * 204, if everything worked out.
417 :rtype: :class:`flask.response`
418 """
419 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
420 try:
421 if network_id not in self.api.compute.nets:
422 return Response('Could not find network. (' + network_id + ')\n',
423 status=404, mimetype='application/json')
424
425 net = self.api.compute.nets[network_id]
426 delete_subnet = NeutronDeleteSubnet(self.api)
427 resp = delete_subnet.delete(net.subnet_id)
428
429 if '204' not in resp.status and '404' not in resp.status:
430 return resp
431
432 self.api.compute.delete_network(network_id)
433
434 return Response('', status=204, mimetype='application/json')
435 except Exception as ex:
436 LOG.exception("Neutron: Delete network exception.")
437 return Response(ex.message, status=500,
438 mimetype='application/json')
439
440
441 class NeutronListSubnets(Resource):
442 def __init__(self, api):
443 self.api = api
444
445 def get(self):
446 """
447 Lists all subnets, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
448 subnet with the name, or the subnets specified via id.
449
450 :return: Returns a json response, starting with 'subnets' as root node.
451 :rtype: :class:`flask.response`
452 """
453 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
454 try:
455 if request.args.get('name'):
456 show_subnet = NeutronShowSubnet(self.api)
457 return show_subnet.get_subnet(request.args.get('name'), True)
458 id_list = request.args.getlist('id')
459 if len(id_list) == 1:
460 show_subnet = NeutronShowSubnet(self.api)
461 return show_subnet.get_subnet(id_list[0], True)
462
463 subnet_list = list()
464 subnet_dict = dict()
465
466 if len(id_list) == 0:
467 for net in self.api.compute.nets.values():
468 if net.subnet_id is not None:
469 tmp_subnet_dict = net.create_subnet_dict()
470 subnet_list.append(tmp_subnet_dict)
471 else:
472 for net in self.api.compute.nets.values():
473 if net.subnet_id in id_list:
474 tmp_subnet_dict = net.create_subnet_dict()
475 subnet_list.append(tmp_subnet_dict)
476
477 subnet_dict["subnets"] = subnet_list
478
479 return Response(json.dumps(subnet_dict), status=200,
480 mimetype='application/json')
481
482 except Exception as ex:
483 LOG.exception("Neutron: List subnets exception.")
484 return Response(ex.message, status=500,
485 mimetype='application/json')
486
487
488 class NeutronShowSubnet(Resource):
489 def __init__(self, api):
490 self.api = api
491
492 def get(self, subnet_id):
493 """
494 Returns the subnet, specified via 'subnet_id'.
495
496 :param subnet_id: The unique ID string of the subnet.
497 :type subnet_id: ``str``
498 :return: Returns a json response, starting with 'subnet' as root node and one subnet description.
499 :rtype: :class:`flask.response`
500 """
501 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
502 return self.get_subnet(subnet_id, False)
503
504 def get_subnet(self, subnet_name_or_id, as_list):
505 """
506 Returns one subnet description of the subnet, specified via 'subnet_name_or_id'.
507
508 :param subnet_name_or_id: The indicator string, which specifies the requested subnet.
509 :type subnet_name_or_id: ``str``
510 :param as_list: Determines if the subnet description should start with the root node 'subnet' or 'subnets'.
511 :type as_list: ``bool``
512 :return: Returns a json response, with one subnet description.
513 :rtype: :class:`flask.response`
514 """
515 try:
516 for net in self.api.compute.nets.values():
517 if net.subnet_id == subnet_name_or_id or net.subnet_name == subnet_name_or_id:
518 tmp_subnet_dict = net.create_subnet_dict()
519 tmp_dict = dict()
520 if as_list:
521 tmp_dict["subnets"] = [tmp_subnet_dict]
522 else:
523 tmp_dict["subnet"] = tmp_subnet_dict
524 return Response(json.dumps(tmp_dict),
525 status=200, mimetype='application/json')
526
527 return Response('Subnet not found. (' + subnet_name_or_id +
528 ')\n', status=404, mimetype='application/json')
529
530 except Exception as ex:
531 LOG.exception("Neutron: Show subnet exception.")
532 return Response(ex.message, status=500,
533 mimetype='application/json')
534
535
536 class NeutronCreateSubnet(Resource):
537 def __init__(self, api):
538 self.api = api
539
540 def post(self):
541 """
542 Creates a subnet with the name, specified within the request under ['subnet']['name'].
543
544 :return: * 400, if the 'CIDR' format is wrong or it does not exist.
545 * 404, if the network was not found.
546 * 409, if the corresponding network already has one subnet.
547 * 500, if any exception occurred while creation and
548 * 201, if everything worked out.
549 :rtype: :class:`flask.response`
550 """
551 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
552 try:
553 subnet_dict = json.loads(request.data)
554 net = self.api.compute.find_network_by_name_or_id(
555 subnet_dict['subnet']['network_id'])
556
557 if net is None:
558 return Response('Could not find network.\n',
559 status=404, mimetype='application/json')
560
561 net.subnet_name = subnet_dict["subnet"].get(
562 'name', str(net.name) + '-sub')
563 if net.subnet_id is not None:
564 LOG.error(
565 "Only one subnet per network is supported: {}".format(net.subnet_id))
566 return Response('Only one subnet per network is supported\n',
567 status=409, mimetype='application/json')
568
569 if "id" in subnet_dict["subnet"]:
570 net.subnet_id = subnet_dict["subnet"]["id"]
571 else:
572 net.subnet_id = str(uuid.uuid4())
573 import emuvim.api.openstack.ip_handler as IP
574 net.set_cidr(IP.get_new_cidr(net.subnet_id))
575
576 if "tenant_id" in subnet_dict["subnet"]:
577 pass
578 if "allocation_pools" in subnet_dict["subnet"]:
579 pass
580 if "gateway_ip" in subnet_dict["subnet"]:
581 net.gateway_ip = subnet_dict["subnet"]["gateway_ip"]
582 if "ip_version" in subnet_dict["subnet"]:
583 pass
584 if "enable_dhcp" in subnet_dict["subnet"]:
585 pass
586
587 return Response(json.dumps(
588 {'subnet': net.create_subnet_dict()}), status=201, mimetype='application/json')
589
590 except Exception as ex:
591 LOG.exception("Neutron: Create network excepiton.")
592 return Response(ex.message, status=500,
593 mimetype='application/json')
594
595
596 class NeutronUpdateSubnet(Resource):
597 def __init__(self, api):
598 self.api = api
599
600 def put(self, subnet_id):
601 """
602 Updates the existing subnet with the given parameters.
603
604 :param subnet_id: The indicator string, which specifies the requested subnet.
605 :type subnet_id: ``str``
606 :return: * 404, if the network could not be found.
607 * 500, if any exception occurred while updating the network.
608 * 200, if everything worked out.
609 :rtype: :class:`flask.response`
610 """
611 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
612 try:
613 for net in self.api.compute.nets.values():
614 if net.subnet_id == subnet_id:
615 subnet_dict = json.loads(request.data)
616
617 if "name" in subnet_dict["subnet"]:
618 net.subnet_name = subnet_dict["subnet"]["name"]
619 if "network_id" in subnet_dict["subnet"]:
620 net.id = subnet_dict["subnet"]["network_id"]
621 if "tenant_id" in subnet_dict["subnet"]:
622 pass
623 if "allocation_pools" in subnet_dict["subnet"]:
624 pass
625 if "gateway_ip" in subnet_dict["subnet"]:
626 net.gateway_ip = subnet_dict["subnet"]["gateway_ip"]
627 if "ip_version" in subnet_dict["subnet"]:
628 pass
629 if "cidr" in subnet_dict["subnet"]:
630 net.set_cidr(subnet_dict["subnet"]["cidr"])
631 if "id" in subnet_dict["subnet"]:
632 net.subnet_id = subnet_dict["subnet"]["id"]
633 if "enable_dhcp" in subnet_dict["subnet"]:
634 pass
635
636 net.subnet_update_time = str(datetime.now())
637 tmp_dict = {'subnet': net.create_subnet_dict()}
638 return Response(json.dumps(tmp_dict),
639 status=200, mimetype='application/json')
640
641 return Response('Network not found.\n', status=404,
642 mimetype='application/json')
643
644 except Exception as ex:
645 LOG.exception("Neutron: Show networks exception.")
646 return Response(ex.message, status=500,
647 mimetype='application/json')
648
649
650 class NeutronDeleteSubnet(Resource):
651 def __init__(self, api):
652 self.api = api
653
654 def delete(self, subnet_id):
655 """
656 Deletes the specified subnet.
657
658 :param subnet_id: The indicator string, which specifies the requested subnet.
659 :type subnet_id: ``str``
660 :return: * 404, if the subnet could not be removed.
661 * 500, if any exception occurred while deletion.
662 * 204, if everything worked out.
663 :rtype: :class:`flask.response`
664 """
665 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
666 try:
667 for net in self.api.compute.nets.values():
668 if net.subnet_id == subnet_id:
669 for server in self.api.compute.computeUnits.values():
670 for port_name in server.port_names:
671 port = self.api.compute.find_port_by_name_or_id(
672 port_name)
673 if port is None:
674 LOG.warning(
675 "Port search for {} returned None.".format(port_name))
676 continue
677 if port.net_name == net.name:
678 port.ip_address = None
679 self.api.compute.dc.net.removeLink(
680 link=None,
681 node1=self.api.compute.dc.containers[server.name],
682 node2=self.api.compute.dc.switch)
683 port.net_name = None
684
685 net.delete_subnet()
686
687 return Response(
688 '', status=204, mimetype='application/json')
689
690 return Response('Could not find subnet.',
691 status=404, mimetype='application/json')
692 except Exception as ex:
693 LOG.exception("Neutron: Delete subnet exception.")
694 return Response(ex.message, status=500,
695 mimetype='application/json')
696
697
698 class NeutronListPorts(Resource):
699 def __init__(self, api):
700 self.api = api
701
702 def get(self):
703 """
704 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
705 port with the name, or the ports specified via id.
706
707 :return: Returns a json response, starting with 'ports' as root node.
708 :rtype: :class:`flask.response`
709 """
710 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
711 try:
712 if request.args.get('name'):
713 show_port = NeutronShowPort(self.api)
714 return show_port.get_port(request.args.get('name'), True)
715 id_list = request.args.getlist('id')
716 if len(id_list) == 1:
717 show_port = NeutronShowPort(self.api)
718 return show_port.get_port(request.args.get('id'), True)
719
720 port_list = list()
721 port_dict = dict()
722
723 if len(id_list) == 0:
724 for port in self.api.compute.ports.values():
725 tmp_port_dict = port.create_port_dict(self.api.compute)
726 port_list.append(tmp_port_dict)
727 else:
728 for port in self.api.compute.ports.values():
729 if port.id in id_list:
730 tmp_port_dict = port.create_port_dict(self.api.compute)
731 port_list.append(tmp_port_dict)
732
733 port_dict["ports"] = port_list
734
735 return Response(json.dumps(port_dict), status=200,
736 mimetype='application/json')
737
738 except Exception as ex:
739 LOG.exception("Neutron: List ports exception.")
740 return Response(ex.message, status=500,
741 mimetype='application/json')
742
743
744 class NeutronShowPort(Resource):
745 def __init__(self, api):
746 self.api = api
747
748 def get(self, port_id):
749 """
750 Returns the port, specified via 'port_id'.
751
752 :param port_id: The unique ID string of the network.
753 :type port_id: ``str``
754 :return: Returns a json response, starting with 'port' as root node and one network description.
755 :rtype: :class:`flask.response`
756 """
757 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
758 return self.get_port(port_id, False)
759
760 def get_port(self, port_name_or_id, as_list):
761 """
762 Returns one network description of the port, specified via 'port_name_or_id'.
763
764 :param port_name_or_id: The indicator string, which specifies the requested port.
765 :type port_name_or_id: ``str``
766 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
767 :type as_list: ``bool``
768 :return: Returns a json response, with one port description.
769 :rtype: :class:`flask.response`
770 """
771 try:
772 port = self.api.compute.find_port_by_name_or_id(port_name_or_id)
773 if port is None:
774 return Response('Port not found. (' + port_name_or_id + ')\n',
775 status=404, mimetype='application/json')
776 tmp_port_dict = port.create_port_dict(self.api.compute)
777 tmp_dict = dict()
778 if as_list:
779 tmp_dict["ports"] = [tmp_port_dict]
780 else:
781 tmp_dict["port"] = tmp_port_dict
782 return Response(json.dumps(tmp_dict), status=200,
783 mimetype='application/json')
784 except Exception as ex:
785 LOG.exception("Neutron: Show port exception.")
786 return Response(ex.message, status=500,
787 mimetype='application/json')
788
789
790 class NeutronCreatePort(Resource):
791 def __init__(self, api):
792 self.api = api
793
794 def post(self):
795 """
796 Creates a port with the name, specified within the request under ['port']['name'].
797
798 :return: * 404, if the network could not be found.
799 * 500, if any exception occurred while creation and
800 * 201, if everything worked out.
801 :rtype: :class:`flask.response`
802 """
803 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
804 try:
805 port_dict = json.loads(request.data)
806 net_id = port_dict['port']['network_id']
807
808 if net_id not in self.api.compute.nets:
809 return Response('Could not find network.\n',
810 status=404, mimetype='application/json')
811
812 net = self.api.compute.nets[net_id]
813 if 'name' in port_dict['port']:
814 name = port_dict['port']['name']
815 else:
816 num_ports = len(self.api.compute.ports)
817 name = "port:cp%s:man:%s" % (num_ports, str(uuid.uuid4()))
818
819 if self.api.compute.find_port_by_name_or_id(name):
820 return Response("Port with name %s already exists.\n" %
821 name, status=500, mimetype='application/json')
822
823 port = self.api.compute.create_port(name)
824
825 port.net_name = net.name
826 port.ip_address = net.get_new_ip_address(name)
827
828 if "admin_state_up" in port_dict["port"]:
829 pass
830 if "device_id" in port_dict["port"]:
831 pass
832 if "device_owner" in port_dict["port"]:
833 pass
834 if "fixed_ips" in port_dict["port"]:
835 pass
836 if "mac_address" in port_dict["port"]:
837 port.mac_address = port_dict["port"]["mac_address"]
838 if "status" in port_dict["port"]:
839 pass
840 if "tenant_id" in port_dict["port"]:
841 pass
842
843 # add the port to a stack if the specified network is a stack
844 # network
845 for stack in self.api.compute.stacks.values():
846 for net in stack.nets.values():
847 if net.id == net_id:
848 stack.ports[name] = port
849
850 return Response(json.dumps({'port': port.create_port_dict(self.api.compute)}), status=201,
851 mimetype='application/json')
852 except Exception as ex:
853 LOG.exception("Neutron: Show port exception.")
854 return Response(ex.message, status=500,
855 mimetype='application/json')
856
857
858 class NeutronUpdatePort(Resource):
859 def __init__(self, api):
860 self.api = api
861
862 def put(self, port_id):
863 """
864 Updates the existing port with the given parameters.
865
866 :param network_id: The indicator string, which specifies the requested port.
867 :type network_id: ``str``
868 :return: * 404, if the network could not be found.
869 * 500, if any exception occurred while updating the network.
870 * 200, if everything worked out.
871 :rtype: :class:`flask.response`
872 """
873 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
874 try:
875 port_dict = json.loads(request.data)
876 port = self.api.compute.find_port_by_name_or_id(port_id)
877 if port is None:
878 return Response("Port with id %s does not exists.\n" %
879 port_id, status=404, mimetype='application/json')
880 old_port = copy.copy(port)
881
882 stack = None
883 for s in self.api.compute.stacks.values():
884 for port in s.ports.values():
885 if port.id == port_id:
886 stack = s
887 if "admin_state_up" in port_dict["port"]:
888 pass
889 if "device_id" in port_dict["port"]:
890 pass
891 if "device_owner" in port_dict["port"]:
892 pass
893 if "fixed_ips" in port_dict["port"]:
894 pass
895 if "id" in port_dict["port"]:
896 port.id = port_dict["port"]["id"]
897 if "mac_address" in port_dict["port"]:
898 port.mac_address = port_dict["port"]["mac_address"]
899 if "name" in port_dict["port"] and port_dict["port"]["name"] != port.name:
900 port.set_name(port_dict["port"]["name"])
901 if stack is not None:
902 if port.net_name in stack.nets:
903 stack.nets[port.net_name].update_port_name_for_ip_address(
904 port.ip_address, port.name)
905 stack.ports[port.name] = stack.ports[old_port.name]
906 del stack.ports[old_port.name]
907 if "network_id" in port_dict["port"]:
908 pass
909 if "status" in port_dict["port"]:
910 pass
911 if "tenant_id" in port_dict["port"]:
912 pass
913
914 return Response(json.dumps({'port': port.create_port_dict(self.api.compute)}), status=200,
915 mimetype='application/json')
916 except Exception as ex:
917 LOG.exception("Neutron: Update port exception.")
918 return Response(ex.message, status=500,
919 mimetype='application/json')
920
921
922 class NeutronDeletePort(Resource):
923 def __init__(self, api):
924 self.api = api
925
926 def delete(self, port_id):
927 """
928 Deletes the specified port.
929
930 :param port_id: The indicator string, which specifies the requested port.
931 :type port_id: ``str``
932 :return: * 404, if the port could not be found.
933 * 500, if any exception occurred while deletion.
934 * 204, if everything worked out.
935 :rtype: :class:`flask.response`
936 """
937 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
938 try:
939 port = self.api.compute.find_port_by_name_or_id(port_id)
940 if port is None:
941 return Response("Port with id %s does not exists.\n" %
942 port_id, status=404)
943 stack = None
944 for s in self.api.compute.stacks.values():
945 for p in s.ports.values():
946 if p.id == port_id:
947 stack = s
948 if stack is not None:
949 if port.net_name in stack.nets:
950 stack.nets[port.net_name].withdraw_ip_address(
951 port.ip_address)
952 for server in stack.servers.values():
953 try:
954 server.port_names.remove(port.name)
955 except ValueError:
956 pass
957
958 # delete the port
959 self.api.compute.delete_port(port.id)
960
961 return Response('', status=204, mimetype='application/json')
962
963 except Exception as ex:
964 LOG.exception("Neutron: Delete port exception.")
965 return Response(ex.message, status=500,
966 mimetype='application/json')
967
968
969 class NeutronAddFloatingIp(Resource):
970 def __init__(self, api):
971 self.api = api
972
973 def get(self):
974 """
975 Returns a Floating IP for a port.
976
977 Currently ports are not mapped to individual IPs, but the
978 (potentially shared) Docker external IP is returned.
979 """
980 port_id = request.args.get("port_id")
981 if not port_id:
982 message = "Neutron: List API for FloatingIPs is not implemented"
983 LOG.exception(message)
984 return Response(message, status=500,
985 mimetype='application/json')
986 port = self.api.compute.find_port_by_name_or_id(port_id)
987 ip = port.assigned_container.dcinfo["NetworkSettings"]["IPAddress"]
988 resp = dict()
989 resp["floatingips"] = [
990 {'floating_ip_address': ip}
991 ]
992 return Response(json.dumps(resp), status=200,
993 mimetype='application/json')
994
995 def post(self):
996 """
997 Adds a floating IP to neutron.
998
999 :return: Returns a floating network description.
1000 :rtype: :class:`flask.response`
1001 """
1002 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
1003 try:
1004 # Fiddle with floating_network !
1005 req = json.loads(request.data)
1006
1007 network_id = req["floatingip"]["floating_network_id"]
1008 net = self.api.compute.find_network_by_name_or_id(network_id)
1009 if net != self.api.manage.floating_network:
1010 return Response("You have to specify the existing floating network\n",
1011 status=400, mimetype='application/json')
1012
1013 port_id = req["floatingip"].get("port_id", None)
1014 port = self.api.compute.find_port_by_name_or_id(port_id)
1015 if port is not None:
1016 if port.net_name != self.api.manage.floating_network.name:
1017 return Response("You have to specify a port in the floating network\n",
1018 status=400, mimetype='application/json')
1019
1020 if port.floating_ip is not None:
1021 return Response("We allow only one floating ip per port\n",
1022 status=400, mimetype='application/json')
1023 else:
1024 num_ports = len(self.api.compute.ports)
1025 name = "port:cp%s:fl:%s" % (num_ports, str(uuid.uuid4()))
1026 port = self.api.compute.create_port(name)
1027 port.net_name = net.name
1028 port.ip_address = net.get_new_ip_address(name)
1029
1030 port.floating_ip = port.ip_address
1031
1032 response = dict()
1033 resp = response["floatingip"] = dict()
1034
1035 resp["floating_network_id"] = net.id
1036 resp["status"] = "ACTIVE"
1037 resp["id"] = net.id
1038 resp["port_id"] = port.id
1039 resp["floating_ip_address"] = port.floating_ip
1040 resp["fixed_ip_address"] = port.floating_ip
1041
1042 return Response(json.dumps(response), status=200,
1043 mimetype='application/json')
1044 except Exception as ex:
1045 LOG.exception("Neutron: Create FloatingIP exception %s.", ex)
1046 return Response(ex.message, status=500,
1047 mimetype='application/json')