385163cce7c943365b0c3794e2d3cba4a9a392da
[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('Network ' + str(network_id) + ' deleted.\n', 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 return Response('Only one subnet per network is supported\n', status=409, mimetype='application/json')
531
532 if "id" in subnet_dict["subnet"]:
533 net.subnet_id = subnet_dict["subnet"]["id"]
534 else:
535 net.subnet_id = str(uuid.uuid4())
536 import emuvim.api.openstack.ip_handler as IP
537 net.set_cidr(IP.get_new_cidr(net.subnet_id))
538
539 if "tenant_id" in subnet_dict["subnet"]:
540 pass
541 if "allocation_pools" in subnet_dict["subnet"]:
542 pass
543 if "gateway_ip" in subnet_dict["subnet"]:
544 net.gateway_ip = subnet_dict["subnet"]["gateway_ip"]
545 if "ip_version" in subnet_dict["subnet"]:
546 pass
547 if "enable_dhcp" in subnet_dict["subnet"]:
548 pass
549
550 return Response(json.dumps({'subnet': net.create_subnet_dict()}), status=201, mimetype='application/json')
551
552 except Exception as ex:
553 LOG.exception("Neutron: Create network excepiton.")
554 return Response(ex.message, status=500, mimetype='application/json')
555
556
557 class NeutronUpdateSubnet(Resource):
558 def __init__(self, api):
559 self.api = api
560
561 def put(self, subnet_id):
562 """
563 Updates the existing subnet with the given parameters.
564
565 :param subnet_id: The indicator string, which specifies the requested subnet.
566 :type subnet_id: ``str``
567 :return: * 404, if the network could not be found.
568 * 500, if any exception occurred while updating the network.
569 * 200, if everything worked out.
570 :rtype: :class:`flask.response`
571 """
572 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
573 try:
574 for net in self.api.compute.nets.values():
575 if net.subnet_id == subnet_id:
576 subnet_dict = json.loads(request.data)
577
578 if "name" in subnet_dict["subnet"]:
579 net.subnet_name = subnet_dict["subnet"]["name"]
580 if "network_id" in subnet_dict["subnet"]:
581 net.id = subnet_dict["subnet"]["network_id"]
582 if "tenant_id" in subnet_dict["subnet"]:
583 pass
584 if "allocation_pools" in subnet_dict["subnet"]:
585 pass
586 if "gateway_ip" in subnet_dict["subnet"]:
587 net.gateway_ip = subnet_dict["subnet"]["gateway_ip"]
588 if "ip_version" in subnet_dict["subnet"]:
589 pass
590 if "cidr" in subnet_dict["subnet"]:
591 net.set_cidr(subnet_dict["subnet"]["cidr"])
592 if "id" in subnet_dict["subnet"]:
593 net.subnet_id = subnet_dict["subnet"]["id"]
594 if "enable_dhcp" in subnet_dict["subnet"]:
595 pass
596
597 net.subnet_update_time = str(datetime.now())
598 tmp_dict = {'subnet': net.create_subnet_dict()}
599 return Response(json.dumps(tmp_dict), status=200, mimetype='application/json')
600
601 return Response('Network not found.\n', status=404, mimetype='application/json')
602
603 except Exception as ex:
604 LOG.exception("Neutron: Show networks exception.")
605 return Response(ex.message, status=500, mimetype='application/json')
606
607
608 class NeutronDeleteSubnet(Resource):
609 def __init__(self, api):
610 self.api = api
611
612 def delete(self, subnet_id):
613 """
614 Deletes the specified subnet.
615
616 :param subnet_id: The indicator string, which specifies the requested subnet.
617 :type subnet_id: ``str``
618 :return: * 404, if the subnet could not be removed.
619 * 500, if any exception occurred while deletion.
620 * 204, if everything worked out.
621 :rtype: :class:`flask.response`
622 """
623 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
624 try:
625 for net in self.api.compute.nets.values():
626 if net.subnet_id == subnet_id:
627 for server in self.api.compute.computeUnits.values():
628 for port_name in server.port_names:
629 port = self.api.compute.find_port_by_name_or_id(port_name)
630 if port.net_name == net.name:
631 port.ip_address = None
632 self.api.compute.dc.net.removeLink(
633 link=None,
634 node1=self.api.compute.dc.containers[server.name],
635 node2=self.api.compute.dc.switch)
636 port.net_name = None
637
638 net.delete_subnet()
639
640 return Response('Subnet ' + str(subnet_id) + ' deleted.\n',
641 status=204, mimetype='application/json')
642
643 return Response('Could not find subnet.', status=404, mimetype='application/json')
644 except Exception as ex:
645 LOG.exception("Neutron: Delete subnet exception.")
646 return Response(ex.message, status=500, mimetype='application/json')
647
648
649 class NeutronListPorts(Resource):
650 def __init__(self, api):
651 self.api = api
652
653 def get(self):
654 """
655 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
656 port with the name, or the ports specified via id.
657
658 :return: Returns a json response, starting with 'ports' as root node.
659 :rtype: :class:`flask.response`
660 """
661 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
662 try:
663 if request.args.get('name'):
664 show_port = NeutronShowPort(self.api)
665 return show_port.get_port(request.args.get('name'), True)
666 id_list = request.args.getlist('id')
667 if len(id_list) == 1:
668 show_port = NeutronShowPort(self.api)
669 return show_port.get_port(request.args.get('id'), True)
670
671 port_list = list()
672 port_dict = dict()
673
674 if len(id_list) == 0:
675 for port in self.api.compute.ports.values():
676 tmp_port_dict = port.create_port_dict(self.api.compute)
677 port_list.append(tmp_port_dict)
678 else:
679 for port in self.api.compute.ports.values():
680 if port.id in id_list:
681 tmp_port_dict = port.create_port_dict(self.api.compute)
682 port_list.append(tmp_port_dict)
683
684 port_dict["ports"] = port_list
685
686 return Response(json.dumps(port_dict), status=200, mimetype='application/json')
687
688 except Exception as ex:
689 LOG.exception("Neutron: List ports exception.")
690 return Response(ex.message, status=500, mimetype='application/json')
691
692
693 class NeutronShowPort(Resource):
694 def __init__(self, api):
695 self.api = api
696
697 def get(self, port_id):
698 """
699 Returns the port, specified via 'port_id'.
700
701 :param port_id: The unique ID string of the network.
702 :type port_id: ``str``
703 :return: Returns a json response, starting with 'port' as root node and one network description.
704 :rtype: :class:`flask.response`
705 """
706 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
707 return self.get_port(port_id, False)
708
709 def get_port(self, port_name_or_id, as_list):
710 """
711 Returns one network description of the port, specified via 'port_name_or_id'.
712
713 :param port_name_or_id: The indicator string, which specifies the requested port.
714 :type port_name_or_id: ``str``
715 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
716 :type as_list: ``bool``
717 :return: Returns a json response, with one port description.
718 :rtype: :class:`flask.response`
719 """
720 try:
721 port = self.api.compute.find_port_by_name_or_id(port_name_or_id)
722 if port is None:
723 return Response('Port not found. (' + port_name_or_id + ')\n', status=404, mimetype='application/json')
724 tmp_port_dict = port.create_port_dict(self.api.compute)
725 tmp_dict = dict()
726 if as_list:
727 tmp_dict["ports"] = [tmp_port_dict]
728 else:
729 tmp_dict["port"] = tmp_port_dict
730 return Response(json.dumps(tmp_dict), status=200, mimetype='application/json')
731 except Exception as ex:
732 LOG.exception("Neutron: Show port exception.")
733 return Response(ex.message, status=500, mimetype='application/json')
734
735
736 class NeutronCreatePort(Resource):
737 def __init__(self, api):
738 self.api = api
739
740 def post(self):
741 """
742 Creates a port with the name, specified within the request under ['port']['name'].
743
744 :return: * 404, if the network could not be found.
745 * 500, if any exception occurred while creation and
746 * 201, if everything worked out.
747 :rtype: :class:`flask.response`
748 """
749 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
750 try:
751 port_dict = json.loads(request.data)
752 net_id = port_dict['port']['network_id']
753
754 if net_id not in self.api.compute.nets:
755 return Response('Could not find network.\n', status=404, mimetype='application/json')
756
757 net = self.api.compute.nets[net_id]
758 if 'name' in port_dict['port']:
759 name = port_dict['port']['name']
760 else:
761 num_ports = len(self.api.compute.ports)
762 name = "port:cp%s:man:%s" % (num_ports, str(uuid.uuid4()))
763
764 if self.api.compute.find_port_by_name_or_id(name):
765 return Response("Port with name %s already exists.\n" % name, status=500, mimetype='application/json')
766
767 port = self.api.compute.create_port(name)
768
769 port.net_name = net.name
770 port.ip_address = net.get_new_ip_address(name)
771
772 if "admin_state_up" in port_dict["port"]:
773 pass
774 if "device_id" in port_dict["port"]:
775 pass
776 if "device_owner" in port_dict["port"]:
777 pass
778 if "fixed_ips" in port_dict["port"]:
779 pass
780 if "mac_address" in port_dict["port"]:
781 port.mac_address = port_dict["port"]["mac_address"]
782 if "status" in port_dict["port"]:
783 pass
784 if "tenant_id" in port_dict["port"]:
785 pass
786
787 # add the port to a stack if the specified network is a stack network
788 for stack in self.api.compute.stacks.values():
789 for net in stack.nets.values():
790 if net.id == net_id:
791 stack.ports[name] = port
792
793 return Response(json.dumps({'port': port.create_port_dict(self.api.compute)}), status=201,
794 mimetype='application/json')
795 except Exception as ex:
796 LOG.exception("Neutron: Show port exception.")
797 return Response(ex.message, status=500, mimetype='application/json')
798
799
800 class NeutronUpdatePort(Resource):
801 def __init__(self, api):
802 self.api = api
803
804 def put(self, port_id):
805 """
806 Updates the existing port with the given parameters.
807
808 :param network_id: The indicator string, which specifies the requested port.
809 :type network_id: ``str``
810 :return: * 404, if the network could not be found.
811 * 500, if any exception occurred while updating the network.
812 * 200, if everything worked out.
813 :rtype: :class:`flask.response`
814 """
815 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
816 try:
817 port_dict = json.loads(request.data)
818 port = self.api.compute.find_port_by_name_or_id(port_id)
819 if port is None:
820 return Response("Port with id %s does not exists.\n" % port_id, status=404, mimetype='application/json')
821 old_port = copy.copy(port)
822
823 stack = None
824 for s in self.api.compute.stacks.values():
825 for port in s.ports.values():
826 if port.id == port_id:
827 stack = s
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 "id" in port_dict["port"]:
837 port.id = port_dict["port"]["id"]
838 if "mac_address" in port_dict["port"]:
839 port.mac_address = port_dict["port"]["mac_address"]
840 if "name" in port_dict["port"] and port_dict["port"]["name"] != port.name:
841 port.set_name(port_dict["port"]["name"])
842 if stack is not None:
843 if port.net_name in stack.nets:
844 stack.nets[port.net_name].update_port_name_for_ip_address(port.ip_address, port.name)
845 stack.ports[port.name] = stack.ports[old_port.name]
846 del stack.ports[old_port.name]
847 if "network_id" in port_dict["port"]:
848 pass
849 if "status" in port_dict["port"]:
850 pass
851 if "tenant_id" in port_dict["port"]:
852 pass
853
854 return Response(json.dumps({'port': port.create_port_dict(self.api.compute)}), status=200,
855 mimetype='application/json')
856 except Exception as ex:
857 LOG.exception("Neutron: Update port exception.")
858 return Response(ex.message, status=500, mimetype='application/json')
859
860
861 class NeutronDeletePort(Resource):
862 def __init__(self, api):
863 self.api = api
864
865 def delete(self, port_id):
866 """
867 Deletes the specified port.
868
869 :param port_id: The indicator string, which specifies the requested port.
870 :type port_id: ``str``
871 :return: * 404, if the port could not be found.
872 * 500, if any exception occurred while deletion.
873 * 204, if everything worked out.
874 :rtype: :class:`flask.response`
875 """
876 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
877 try:
878 port = self.api.compute.find_port_by_name_or_id(port_id)
879 if port is None:
880 return Response("Port with id %s does not exists.\n" % port_id, status=404)
881 stack = None
882 for s in self.api.compute.stacks.values():
883 for p in s.ports.values():
884 if p.id == port_id:
885 stack = s
886 if stack is not None:
887 if port.net_name in stack.nets:
888 stack.nets[port.net_name].withdraw_ip_address(port.ip_address)
889 for server in stack.servers.values():
890 try:
891 server.port_names.remove(port.name)
892 except ValueError:
893 pass
894
895 # delete the port
896 self.api.compute.delete_port(port.id)
897
898 return Response('Port ' + port_id + ' deleted.\n', status=204, mimetype='application/json')
899
900 except Exception as ex:
901 LOG.exception("Neutron: Delete port exception.")
902 return Response(ex.message, status=500, mimetype='application/json')
903
904
905 class NeutronAddFloatingIp(Resource):
906 def __init__(self, api):
907 self.api = api
908
909 def get(self):
910 """
911 Added a quick and dirty fake for the OSM integration. Returns a list of
912 floating IPs. Has nothing to do with the setup inside the emulator.
913 But its enough to make the OSM driver happy.
914 @PG Sandman: Feel free to improve this and let it do something meaningful.
915 """
916 resp = dict()
917 resp["floatingips"] = list()
918 # create a list of floting IP definitions and return it
919 for i in range(100, 110):
920 ip = dict()
921 ip["router_id"] = "router_id"
922 ip["description"] = "hardcoded in api"
923 ip["created_at"] = "router_id"
924 ip["updated_at"] = "router_id"
925 ip["revision_number"] = 1
926 ip["tenant_id"] = "tenant_id"
927 ip["project_id"] = "project_id"
928 ip["floating_network_id"] = str(i)
929 ip["status"] = "ACTIVE"
930 ip["id"] = str(i)
931 ip["port_id"] = "port_id"
932 ip["floating_ip_address"] = "172.0.0.%d" % i
933 ip["fixed_ip_address"] = "10.0.0.%d" % i
934 resp["floatingips"].append(ip)
935 return Response(json.dumps(resp), status=200, mimetype='application/json')
936
937 def post(self):
938 """
939 Adds a floating IP to neutron.
940
941 :return: Returns a floating network description.
942 :rtype: :class:`flask.response`
943 """
944 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
945 try:
946 # Fiddle with floating_network !
947 req = json.loads(request.data)
948
949 network_id = req["floatingip"]["floating_network_id"]
950 net = self.api.compute.find_network_by_name_or_id(network_id)
951 if net != self.api.manage.floating_network:
952 return Response("You have to specify the existing floating network\n",
953 status=400, mimetype='application/json')
954
955 port_id = req["floatingip"].get("port_id", None)
956 port = self.api.compute.find_port_by_name_or_id(port_id)
957 if port is not None:
958 if port.net_name != self.api.manage.floating_network.name:
959 return Response("You have to specify a port in the floating network\n",
960 status=400, mimetype='application/json')
961
962 if port.floating_ip is not None:
963 return Response("We allow only one floating ip per port\n", status=400, mimetype='application/json')
964 else:
965 num_ports = len(self.api.compute.ports)
966 name = "port:cp%s:fl:%s" % (num_ports, str(uuid.uuid4()))
967 port = self.api.compute.create_port(name)
968 port.net_name = net.name
969 port.ip_address = net.get_new_ip_address(name)
970
971 port.floating_ip = port.ip_address
972
973 response = dict()
974 resp = response["floatingip"] = dict()
975
976 resp["floating_network_id"] = net.id
977 resp["status"] = "ACTIVE"
978 resp["id"] = net.id
979 resp["port_id"] = port.id
980 resp["floating_ip_address"] = port.floating_ip
981 resp["fixed_ip_address"] = port.floating_ip
982
983 return Response(json.dumps(response), status=200, mimetype='application/json')
984 except Exception as ex:
985 LOG.exception("Neutron: Create FloatingIP exception %s.", ex)
986 return Response(ex.message, status=500, mimetype='application/json')