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