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