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