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