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