2 Copyright (c) 2017 SONATA-NFV and Paderborn University
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
9 http://www.apache.org/licenses/LICENSE-2.0
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.
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
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).
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
39 LOG
= logging
.getLogger("api.openstack.neutron")
42 class NeutronDummyApi(BaseOpenstackDummy
):
43 def __init__(self
, ip
, port
, compute
):
44 super(NeutronDummyApi
, self
).__init
__(ip
, port
)
45 self
.compute
= compute
47 self
.api
.add_resource(NeutronListAPIVersions
, "/")
48 self
.api
.add_resource(NeutronShowAPIv2Details
, "/v2.0")
49 self
.api
.add_resource(NeutronListNetworks
, "/v2.0/networks.json", "/v2.0/networks",
50 resource_class_kwargs
={'api': self
})
51 self
.api
.add_resource(NeutronShowNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
52 resource_class_kwargs
={'api': self
})
53 self
.api
.add_resource(NeutronCreateNetwork
, "/v2.0/networks.json", "/v2.0/networks",
54 resource_class_kwargs
={'api': self
})
55 self
.api
.add_resource(NeutronUpdateNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
56 resource_class_kwargs
={'api': self
})
57 self
.api
.add_resource(NeutronDeleteNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
58 resource_class_kwargs
={'api': self
})
59 self
.api
.add_resource(NeutronListSubnets
, "/v2.0/subnets.json", "/v2.0/subnets",
60 resource_class_kwargs
={'api': self
})
61 self
.api
.add_resource(NeutronShowSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
62 resource_class_kwargs
={'api': self
})
63 self
.api
.add_resource(NeutronCreateSubnet
, "/v2.0/subnets.json", "/v2.0/subnets",
64 resource_class_kwargs
={'api': self
})
65 self
.api
.add_resource(NeutronUpdateSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
66 resource_class_kwargs
={'api': self
})
67 self
.api
.add_resource(NeutronDeleteSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
68 resource_class_kwargs
={'api': self
})
69 self
.api
.add_resource(NeutronListPorts
, "/v2.0/ports.json", "/v2.0/ports",
70 resource_class_kwargs
={'api': self
})
71 self
.api
.add_resource(NeutronShowPort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
72 resource_class_kwargs
={'api': self
})
73 self
.api
.add_resource(NeutronCreatePort
, "/v2.0/ports.json", "/v2.0/ports",
74 resource_class_kwargs
={'api': self
})
75 self
.api
.add_resource(NeutronUpdatePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
76 resource_class_kwargs
={'api': self
})
77 self
.api
.add_resource(NeutronDeletePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
78 resource_class_kwargs
={'api': self
})
79 self
.api
.add_resource(NeutronAddFloatingIp
, "/v2.0/floatingips.json", "/v2.0/floatingips",
80 resource_class_kwargs
={'api': self
})
82 # Service Function Chaining (SFC) API
83 self
.api
.add_resource(SFC
.PortPairsCreate
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
84 resource_class_kwargs
={'api': self
})
85 self
.api
.add_resource(SFC
.PortPairsUpdate
, "/v2.0/sfc/port_pairs/<pair_id>.json",
86 "/v2.0/sfc/port_pairs/<pair_id>",
87 resource_class_kwargs
={'api': self
})
88 self
.api
.add_resource(SFC
.PortPairsDelete
, "/v2.0/sfc/port_pairs/<pair_id>.json",
89 "/v2.0/sfc/port_pairs/<pair_id>",
90 resource_class_kwargs
={'api': self
})
91 self
.api
.add_resource(SFC
.PortPairsList
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
92 resource_class_kwargs
={'api': self
})
93 self
.api
.add_resource(SFC
.PortPairsShow
, "/v2.0/sfc/port_pairs/<pair_id>.json",
94 "/v2.0/sfc/port_pairs/<pair_id>",
95 resource_class_kwargs
={'api': self
})
97 self
.api
.add_resource(SFC
.PortPairGroupCreate
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
98 resource_class_kwargs
={'api': self
})
99 self
.api
.add_resource(SFC
.PortPairGroupUpdate
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
100 "/v2.0/sfc/port_pair_groups/<group_id>",
101 resource_class_kwargs
={'api': self
})
102 self
.api
.add_resource(SFC
.PortPairGroupDelete
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
103 "/v2.0/sfc/port_pair_groups/<group_id>",
104 resource_class_kwargs
={'api': self
})
105 self
.api
.add_resource(SFC
.PortPairGroupList
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
106 resource_class_kwargs
={'api': self
})
107 self
.api
.add_resource(SFC
.PortPairGroupShow
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
108 "/v2.0/sfc/port_pair_groups/<group_id>",
109 resource_class_kwargs
={'api': self
})
111 self
.api
.add_resource(SFC
.FlowClassifierCreate
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
112 resource_class_kwargs
={'api': self
})
113 self
.api
.add_resource(SFC
.FlowClassifierUpdate
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
114 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
115 resource_class_kwargs
={'api': self
})
116 self
.api
.add_resource(SFC
.FlowClassifierDelete
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
117 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
118 resource_class_kwargs
={'api': self
})
119 self
.api
.add_resource(SFC
.FlowClassifierList
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
120 resource_class_kwargs
={'api': self
})
121 self
.api
.add_resource(SFC
.FlowClassifierShow
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
122 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
123 resource_class_kwargs
={'api': self
})
125 self
.api
.add_resource(SFC
.PortChainCreate
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
126 resource_class_kwargs
={'api': self
})
127 self
.api
.add_resource(SFC
.PortChainUpdate
, "/v2.0/sfc/port_chains/<chain_id>.json",
128 "/v2.0/sfc/port_chains/<chain_id>",
129 resource_class_kwargs
={'api': self
})
130 self
.api
.add_resource(SFC
.PortChainDelete
, "/v2.0/sfc/port_chains/<chain_id>.json",
131 "/v2.0/sfc/port_chains/<chain_id>",
132 resource_class_kwargs
={'api': self
})
133 self
.api
.add_resource(SFC
.PortChainList
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
134 resource_class_kwargs
={'api': self
})
135 self
.api
.add_resource(SFC
.PortChainShow
, "/v2.0/sfc/port_chains/<chain_id>.json",
136 "/v2.0/sfc/port_chains/<chain_id>",
137 resource_class_kwargs
={'api': self
})
140 class NeutronListAPIVersions(Resource
):
145 :return: Returns a json with API versions.
146 :rtype: :class:`flask.response`
148 LOG
.debug("API CALL: Neutron - List API Versions")
150 resp
['versions'] = dict()
157 "href": request
.url_root
+ '/v2.0',
162 resp
['versions'] = versions
164 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
167 class NeutronShowAPIv2Details(Resource
):
172 :return: Returns a json with API details.
173 :rtype: :class:`flask.response`
175 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
178 resp
['resources'] = dict()
179 resp
['resources'] = [{
182 "href": request
.url_root
+ 'v2.0/subnets',
187 "collection": "subnets"
192 "href": request
.url_root
+ 'v2.0/networks',
197 "collection": "networks"
202 "href": request
.url_root
+ 'v2.0/ports',
207 "collection": "ports"
211 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
214 class NeutronListNetworks(Resource
):
215 def __init__(self
, api
):
220 Lists all networks, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
221 network with the name, or the networks specified via id.
223 :return: Returns a json response, starting with 'networks' as root node.
224 :rtype: :class:`flask.response`
226 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
228 if request
.args
.get('name'):
229 tmp_network
= NeutronShowNetwork(self
.api
)
230 return tmp_network
.get_network(request
.args
.get('name'), True)
231 id_list
= request
.args
.getlist('id')
232 if len(id_list
) == 1:
233 tmp_network
= NeutronShowNetwork(self
.api
)
234 return tmp_network
.get_network(request
.args
.get('id'), True)
236 network_list
= list()
237 network_dict
= dict()
239 if len(id_list
) == 0:
240 for net
in self
.api
.compute
.nets
.values():
241 tmp_network_dict
= net
.create_network_dict()
242 if tmp_network_dict
not in network_list
:
243 network_list
.append(tmp_network_dict
)
245 for net
in self
.api
.compute
.nets
.values():
246 if net
.id in id_list
:
247 tmp_network_dict
= net
.create_network_dict()
248 if tmp_network_dict
not in network_list
:
249 network_list
.append(tmp_network_dict
)
251 network_dict
["networks"] = network_list
253 return Response(json
.dumps(network_dict
), status
=200, mimetype
='application/json')
255 except Exception as ex
:
256 LOG
.exception("Neutron: List networks exception.")
257 return Response(ex
.message
, status
=500, mimetype
='application/json')
260 class NeutronShowNetwork(Resource
):
261 def __init__(self
, api
):
264 def get(self
, network_id
):
266 Returns the network, specified via 'network_id'.
268 :param network_id: The unique ID string of the network.
269 :type network_id: ``str``
270 :return: Returns a json response, starting with 'network' as root node and one network description.
271 :rtype: :class:`flask.response`
273 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
274 return self
.get_network(network_id
, False)
276 def get_network(self
, network_name_or_id
, as_list
):
278 Returns one network description of the network, specified via 'network_name_or_id'.
280 :param network_name_or_id: The indicator string, which specifies the requested network.
281 :type network_name_or_id: ``str``
282 :param as_list: Determines if the network description should start with the root node 'network' or 'networks'.
283 :type as_list: ``bool``
284 :return: Returns a json response, with one network description.
285 :rtype: :class:`flask.response`
288 net
= self
.api
.compute
.find_network_by_name_or_id(network_name_or_id
)
290 return Response(u
'Network not found.\n', status
=404, mimetype
='application/json')
292 tmp_network_dict
= net
.create_network_dict()
295 tmp_dict
["networks"] = [tmp_network_dict
]
297 tmp_dict
["network"] = tmp_network_dict
299 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
302 except Exception as ex
:
303 logging
.exception("Neutron: Show network exception.")
304 return Response(ex
.message
, status
=500, mimetype
='application/json')
307 class NeutronCreateNetwork(Resource
):
308 def __init__(self
, api
):
313 Creates a network with the name, specified within the request under ['network']['name'].
315 :return: * 400, if the network already exists.
316 * 500, if any exception occurred while creation.
317 * 201, if everything worked out.
318 :rtype: :class:`flask.response`
320 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
322 network_dict
= json
.loads(request
.data
)
323 name
= network_dict
['network']['name']
324 net
= self
.api
.compute
.find_network_by_name_or_id(name
)
326 return Response('Network already exists.\n', status
=400, mimetype
='application/json')
328 net
= self
.api
.compute
.create_network(name
)
329 return Response(json
.dumps({"network": net
.create_network_dict()}), status
=201, mimetype
='application/json')
330 except Exception as ex
:
331 LOG
.exception("Neutron: Create network excepiton.")
332 return Response(ex
.message
, status
=500, mimetype
='application/json')
335 class NeutronUpdateNetwork(Resource
):
336 def __init__(self
, api
):
339 def put(self
, network_id
): # TODO currently only the name will be changed
341 Updates the existing network with the given parameters.
343 :param network_id: The indicator string, which specifies the requested network.
344 :type network_id: ``str``
345 :return: * 404, if the network could not be found.
346 * 500, if any exception occurred while updating the network.
347 * 200, if everything worked out.
348 :rtype: :class:`flask.response`
350 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
352 if network_id
in self
.api
.compute
.nets
:
353 net
= self
.api
.compute
.nets
[network_id
]
354 network_dict
= json
.loads(request
.data
)
355 old_net
= copy
.copy(net
)
357 if "status" in network_dict
["network"]:
358 net
.status
= network_dict
["network"]["status"]
359 if "subnets" in network_dict
["network"]:
360 pass # tmp_network_dict["subnets"] = None
361 if "name" in network_dict
["network"] and net
.name
!= network_dict
["network"]["name"]:
362 net
.name
= network_dict
["network"]["name"]
363 if "admin_state_up" in network_dict
["network"]:
364 pass # tmp_network_dict["admin_state_up"] = True
365 if "tenant_id" in network_dict
["network"]:
366 pass # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
367 if "shared" in network_dict
["network"]:
368 pass # tmp_network_dict["shared"] = False
370 return Response(json
.dumps(network_dict
), status
=200, mimetype
='application/json')
372 return Response('Network not found.\n', status
=404, mimetype
='application/json')
374 except Exception as ex
:
375 LOG
.exception("Neutron: Show networks exception.")
376 return Response(ex
.message
, status
=500, mimetype
='application/json')
379 class NeutronDeleteNetwork(Resource
):
380 def __init__(self
, api
):
383 def delete(self
, network_id
):
385 Deletes the specified network and all its subnets.
387 :param network_id: The indicator string, which specifies the requested network.
388 :type network_id: ``str``
389 :return: * 404, if the network or the subnet could not be removed.
390 * 500, if any exception occurred while deletion.
391 * 204, if everything worked out.
392 :rtype: :class:`flask.response`
394 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
396 if network_id
not in self
.api
.compute
.nets
:
397 return Response('Could not find network. (' + network_id
+ ')\n',
398 status
=404, mimetype
='application/json')
400 net
= self
.api
.compute
.nets
[network_id
]
401 delete_subnet
= NeutronDeleteSubnet(self
.api
)
402 resp
= delete_subnet
.delete(net
.subnet_id
)
404 if not '204' in resp
.status
and not '404' in resp
.status
:
407 self
.api
.compute
.delete_network(network_id
)
409 return Response('', status
=204, mimetype
='application/json')
410 except Exception as ex
:
411 LOG
.exception("Neutron: Delete network exception.")
412 return Response(ex
.message
, status
=500, mimetype
='application/json')
415 class NeutronListSubnets(Resource
):
416 def __init__(self
, api
):
421 Lists all subnets, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
422 subnet with the name, or the subnets specified via id.
424 :return: Returns a json response, starting with 'subnets' as root node.
425 :rtype: :class:`flask.response`
427 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
429 if request
.args
.get('name'):
430 show_subnet
= NeutronShowSubnet(self
.api
)
431 return show_subnet
.get_subnet(request
.args
.get('name'), True)
432 id_list
= request
.args
.getlist('id')
433 if len(id_list
) == 1:
434 show_subnet
= NeutronShowSubnet(self
.api
)
435 return show_subnet
.get_subnet(id_list
[0], True)
440 if len(id_list
) == 0:
441 for net
in self
.api
.compute
.nets
.values():
442 if net
.subnet_id
is not None:
443 tmp_subnet_dict
= net
.create_subnet_dict()
444 subnet_list
.append(tmp_subnet_dict
)
446 for net
in self
.api
.compute
.nets
.values():
447 if net
.subnet_id
in id_list
:
448 tmp_subnet_dict
= net
.create_subnet_dict()
449 subnet_list
.append(tmp_subnet_dict
)
451 subnet_dict
["subnets"] = subnet_list
453 return Response(json
.dumps(subnet_dict
), status
=200, mimetype
='application/json')
455 except Exception as ex
:
456 LOG
.exception("Neutron: List subnets exception.")
457 return Response(ex
.message
, status
=500, mimetype
='application/json')
460 class NeutronShowSubnet(Resource
):
461 def __init__(self
, api
):
464 def get(self
, subnet_id
):
466 Returns the subnet, specified via 'subnet_id'.
468 :param subnet_id: The unique ID string of the subnet.
469 :type subnet_id: ``str``
470 :return: Returns a json response, starting with 'subnet' as root node and one subnet description.
471 :rtype: :class:`flask.response`
473 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
474 return self
.get_subnet(subnet_id
, False)
476 def get_subnet(self
, subnet_name_or_id
, as_list
):
478 Returns one subnet description of the subnet, specified via 'subnet_name_or_id'.
480 :param subnet_name_or_id: The indicator string, which specifies the requested subnet.
481 :type subnet_name_or_id: ``str``
482 :param as_list: Determines if the subnet description should start with the root node 'subnet' or 'subnets'.
483 :type as_list: ``bool``
484 :return: Returns a json response, with one subnet description.
485 :rtype: :class:`flask.response`
488 for net
in self
.api
.compute
.nets
.values():
489 if net
.subnet_id
== subnet_name_or_id
or net
.subnet_name
== subnet_name_or_id
:
490 tmp_subnet_dict
= net
.create_subnet_dict()
493 tmp_dict
["subnets"] = [tmp_subnet_dict
]
495 tmp_dict
["subnet"] = tmp_subnet_dict
496 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
498 return Response('Subnet not found. (' + subnet_name_or_id
+ ')\n', status
=404, mimetype
='application/json')
500 except Exception as ex
:
501 LOG
.exception("Neutron: Show subnet exception.")
502 return Response(ex
.message
, status
=500, mimetype
='application/json')
505 class NeutronCreateSubnet(Resource
):
506 def __init__(self
, api
):
511 Creates a subnet with the name, specified within the request under ['subnet']['name'].
513 :return: * 400, if the 'CIDR' format is wrong or it does not exist.
514 * 404, if the network was not found.
515 * 409, if the corresponding network already has one subnet.
516 * 500, if any exception occurred while creation and
517 * 201, if everything worked out.
518 :rtype: :class:`flask.response`
520 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
522 subnet_dict
= json
.loads(request
.data
)
523 net
= self
.api
.compute
.find_network_by_name_or_id(subnet_dict
['subnet']['network_id'])
526 return Response('Could not find network.\n', status
=404, mimetype
='application/json')
528 net
.subnet_name
= subnet_dict
["subnet"].get('name', str(net
.name
) + '-sub')
529 if net
.subnet_id
is not None:
530 LOG
.error("Only one subnet per network is supported: {}".format(net
.subnet_id
))
531 return Response('Only one subnet per network is supported\n', status
=409, mimetype
='application/json')
533 if "id" in subnet_dict
["subnet"]:
534 net
.subnet_id
= subnet_dict
["subnet"]["id"]
536 net
.subnet_id
= str(uuid
.uuid4())
537 import emuvim
.api
.openstack
.ip_handler
as IP
538 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
540 if "tenant_id" in subnet_dict
["subnet"]:
542 if "allocation_pools" in subnet_dict
["subnet"]:
544 if "gateway_ip" in subnet_dict
["subnet"]:
545 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
546 if "ip_version" in subnet_dict
["subnet"]:
548 if "enable_dhcp" in subnet_dict
["subnet"]:
551 return Response(json
.dumps({'subnet': net
.create_subnet_dict()}), status
=201, mimetype
='application/json')
553 except Exception as ex
:
554 LOG
.exception("Neutron: Create network excepiton.")
555 return Response(ex
.message
, status
=500, mimetype
='application/json')
558 class NeutronUpdateSubnet(Resource
):
559 def __init__(self
, api
):
562 def put(self
, subnet_id
):
564 Updates the existing subnet with the given parameters.
566 :param subnet_id: The indicator string, which specifies the requested subnet.
567 :type subnet_id: ``str``
568 :return: * 404, if the network could not be found.
569 * 500, if any exception occurred while updating the network.
570 * 200, if everything worked out.
571 :rtype: :class:`flask.response`
573 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
575 for net
in self
.api
.compute
.nets
.values():
576 if net
.subnet_id
== subnet_id
:
577 subnet_dict
= json
.loads(request
.data
)
579 if "name" in subnet_dict
["subnet"]:
580 net
.subnet_name
= subnet_dict
["subnet"]["name"]
581 if "network_id" in subnet_dict
["subnet"]:
582 net
.id = subnet_dict
["subnet"]["network_id"]
583 if "tenant_id" in subnet_dict
["subnet"]:
585 if "allocation_pools" in subnet_dict
["subnet"]:
587 if "gateway_ip" in subnet_dict
["subnet"]:
588 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
589 if "ip_version" in subnet_dict
["subnet"]:
591 if "cidr" in subnet_dict
["subnet"]:
592 net
.set_cidr(subnet_dict
["subnet"]["cidr"])
593 if "id" in subnet_dict
["subnet"]:
594 net
.subnet_id
= subnet_dict
["subnet"]["id"]
595 if "enable_dhcp" in subnet_dict
["subnet"]:
598 net
.subnet_update_time
= str(datetime
.now())
599 tmp_dict
= {'subnet': net
.create_subnet_dict()}
600 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
602 return Response('Network not found.\n', status
=404, mimetype
='application/json')
604 except Exception as ex
:
605 LOG
.exception("Neutron: Show networks exception.")
606 return Response(ex
.message
, status
=500, mimetype
='application/json')
609 class NeutronDeleteSubnet(Resource
):
610 def __init__(self
, api
):
613 def delete(self
, subnet_id
):
615 Deletes the specified subnet.
617 :param subnet_id: The indicator string, which specifies the requested subnet.
618 :type subnet_id: ``str``
619 :return: * 404, if the subnet could not be removed.
620 * 500, if any exception occurred while deletion.
621 * 204, if everything worked out.
622 :rtype: :class:`flask.response`
624 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
626 for net
in self
.api
.compute
.nets
.values():
627 if net
.subnet_id
== subnet_id
:
628 for server
in self
.api
.compute
.computeUnits
.values():
629 for port_name
in server
.port_names
:
630 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
632 LOG
.warning("Port search for {} returned None.".format(port_name
))
634 if port
.net_name
== net
.name
:
635 port
.ip_address
= None
636 self
.api
.compute
.dc
.net
.removeLink(
638 node1
=self
.api
.compute
.dc
.containers
[server
.name
],
639 node2
=self
.api
.compute
.dc
.switch
)
644 return Response('', status
=204, mimetype
='application/json')
646 return Response('Could not find subnet.', status
=404, mimetype
='application/json')
647 except Exception as ex
:
648 LOG
.exception("Neutron: Delete subnet exception.")
649 return Response(ex
.message
, status
=500, mimetype
='application/json')
652 class NeutronListPorts(Resource
):
653 def __init__(self
, api
):
658 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
659 port with the name, or the ports specified via id.
661 :return: Returns a json response, starting with 'ports' as root node.
662 :rtype: :class:`flask.response`
664 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
666 if request
.args
.get('name'):
667 show_port
= NeutronShowPort(self
.api
)
668 return show_port
.get_port(request
.args
.get('name'), True)
669 id_list
= request
.args
.getlist('id')
670 if len(id_list
) == 1:
671 show_port
= NeutronShowPort(self
.api
)
672 return show_port
.get_port(request
.args
.get('id'), True)
677 if len(id_list
) == 0:
678 for port
in self
.api
.compute
.ports
.values():
679 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
680 port_list
.append(tmp_port_dict
)
682 for port
in self
.api
.compute
.ports
.values():
683 if port
.id in id_list
:
684 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
685 port_list
.append(tmp_port_dict
)
687 port_dict
["ports"] = port_list
689 return Response(json
.dumps(port_dict
), status
=200, mimetype
='application/json')
691 except Exception as ex
:
692 LOG
.exception("Neutron: List ports exception.")
693 return Response(ex
.message
, status
=500, mimetype
='application/json')
696 class NeutronShowPort(Resource
):
697 def __init__(self
, api
):
700 def get(self
, port_id
):
702 Returns the port, specified via 'port_id'.
704 :param port_id: The unique ID string of the network.
705 :type port_id: ``str``
706 :return: Returns a json response, starting with 'port' as root node and one network description.
707 :rtype: :class:`flask.response`
709 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
710 return self
.get_port(port_id
, False)
712 def get_port(self
, port_name_or_id
, as_list
):
714 Returns one network description of the port, specified via 'port_name_or_id'.
716 :param port_name_or_id: The indicator string, which specifies the requested port.
717 :type port_name_or_id: ``str``
718 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
719 :type as_list: ``bool``
720 :return: Returns a json response, with one port description.
721 :rtype: :class:`flask.response`
724 port
= self
.api
.compute
.find_port_by_name_or_id(port_name_or_id
)
726 return Response('Port not found. (' + port_name_or_id
+ ')\n', status
=404, mimetype
='application/json')
727 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
730 tmp_dict
["ports"] = [tmp_port_dict
]
732 tmp_dict
["port"] = tmp_port_dict
733 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
734 except Exception as ex
:
735 LOG
.exception("Neutron: Show port exception.")
736 return Response(ex
.message
, status
=500, mimetype
='application/json')
739 class NeutronCreatePort(Resource
):
740 def __init__(self
, api
):
745 Creates a port with the name, specified within the request under ['port']['name'].
747 :return: * 404, if the network could not be found.
748 * 500, if any exception occurred while creation and
749 * 201, if everything worked out.
750 :rtype: :class:`flask.response`
752 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
754 port_dict
= json
.loads(request
.data
)
755 net_id
= port_dict
['port']['network_id']
757 if net_id
not in self
.api
.compute
.nets
:
758 return Response('Could not find network.\n', status
=404, mimetype
='application/json')
760 net
= self
.api
.compute
.nets
[net_id
]
761 if 'name' in port_dict
['port']:
762 name
= port_dict
['port']['name']
764 num_ports
= len(self
.api
.compute
.ports
)
765 name
= "port:cp%s:man:%s" % (num_ports
, str(uuid
.uuid4()))
767 if self
.api
.compute
.find_port_by_name_or_id(name
):
768 return Response("Port with name %s already exists.\n" % name
, status
=500, mimetype
='application/json')
770 port
= self
.api
.compute
.create_port(name
)
772 port
.net_name
= net
.name
773 port
.ip_address
= net
.get_new_ip_address(name
)
775 if "admin_state_up" in port_dict
["port"]:
777 if "device_id" in port_dict
["port"]:
779 if "device_owner" in port_dict
["port"]:
781 if "fixed_ips" in port_dict
["port"]:
783 if "mac_address" in port_dict
["port"]:
784 port
.mac_address
= port_dict
["port"]["mac_address"]
785 if "status" in port_dict
["port"]:
787 if "tenant_id" in port_dict
["port"]:
790 # add the port to a stack if the specified network is a stack network
791 for stack
in self
.api
.compute
.stacks
.values():
792 for net
in stack
.nets
.values():
794 stack
.ports
[name
] = port
796 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=201,
797 mimetype
='application/json')
798 except Exception as ex
:
799 LOG
.exception("Neutron: Show port exception.")
800 return Response(ex
.message
, status
=500, mimetype
='application/json')
803 class NeutronUpdatePort(Resource
):
804 def __init__(self
, api
):
807 def put(self
, port_id
):
809 Updates the existing port with the given parameters.
811 :param network_id: The indicator string, which specifies the requested port.
812 :type network_id: ``str``
813 :return: * 404, if the network could not be found.
814 * 500, if any exception occurred while updating the network.
815 * 200, if everything worked out.
816 :rtype: :class:`flask.response`
818 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
820 port_dict
= json
.loads(request
.data
)
821 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
823 return Response("Port with id %s does not exists.\n" % port_id
, status
=404, mimetype
='application/json')
824 old_port
= copy
.copy(port
)
827 for s
in self
.api
.compute
.stacks
.values():
828 for port
in s
.ports
.values():
829 if port
.id == port_id
:
831 if "admin_state_up" in port_dict
["port"]:
833 if "device_id" in port_dict
["port"]:
835 if "device_owner" in port_dict
["port"]:
837 if "fixed_ips" in port_dict
["port"]:
839 if "id" in port_dict
["port"]:
840 port
.id = port_dict
["port"]["id"]
841 if "mac_address" in port_dict
["port"]:
842 port
.mac_address
= port_dict
["port"]["mac_address"]
843 if "name" in port_dict
["port"] and port_dict
["port"]["name"] != port
.name
:
844 port
.set_name(port_dict
["port"]["name"])
845 if stack
is not None:
846 if port
.net_name
in stack
.nets
:
847 stack
.nets
[port
.net_name
].update_port_name_for_ip_address(port
.ip_address
, port
.name
)
848 stack
.ports
[port
.name
] = stack
.ports
[old_port
.name
]
849 del stack
.ports
[old_port
.name
]
850 if "network_id" in port_dict
["port"]:
852 if "status" in port_dict
["port"]:
854 if "tenant_id" in port_dict
["port"]:
857 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=200,
858 mimetype
='application/json')
859 except Exception as ex
:
860 LOG
.exception("Neutron: Update port exception.")
861 return Response(ex
.message
, status
=500, mimetype
='application/json')
864 class NeutronDeletePort(Resource
):
865 def __init__(self
, api
):
868 def delete(self
, port_id
):
870 Deletes the specified port.
872 :param port_id: The indicator string, which specifies the requested port.
873 :type port_id: ``str``
874 :return: * 404, if the port could not be found.
875 * 500, if any exception occurred while deletion.
876 * 204, if everything worked out.
877 :rtype: :class:`flask.response`
879 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
881 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
883 return Response("Port with id %s does not exists.\n" % port_id
, status
=404)
885 for s
in self
.api
.compute
.stacks
.values():
886 for p
in s
.ports
.values():
889 if stack
is not None:
890 if port
.net_name
in stack
.nets
:
891 stack
.nets
[port
.net_name
].withdraw_ip_address(port
.ip_address
)
892 for server
in stack
.servers
.values():
894 server
.port_names
.remove(port
.name
)
899 self
.api
.compute
.delete_port(port
.id)
901 return Response('', status
=204, mimetype
='application/json')
903 except Exception as ex
:
904 LOG
.exception("Neutron: Delete port exception.")
905 return Response(ex
.message
, status
=500, mimetype
='application/json')
908 class NeutronAddFloatingIp(Resource
):
909 def __init__(self
, api
):
914 Added a quick and dirty fake for the OSM integration. Returns a list of
915 floating IPs. Has nothing to do with the setup inside the emulator.
916 But its enough to make the OSM driver happy.
917 @PG Sandman: Feel free to improve this and let it do something meaningful.
920 resp
["floatingips"] = list()
921 # create a list of floting IP definitions and return it
922 for i
in range(100, 110):
924 ip
["router_id"] = "router_id"
925 ip
["description"] = "hardcoded in api"
926 ip
["created_at"] = "router_id"
927 ip
["updated_at"] = "router_id"
928 ip
["revision_number"] = 1
929 ip
["tenant_id"] = "tenant_id"
930 ip
["project_id"] = "project_id"
931 ip
["floating_network_id"] = str(i
)
932 ip
["status"] = "ACTIVE"
934 ip
["port_id"] = "port_id"
935 ip
["floating_ip_address"] = "172.0.0.%d" % i
936 ip
["fixed_ip_address"] = "10.0.0.%d" % i
937 resp
["floatingips"].append(ip
)
938 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
942 Adds a floating IP to neutron.
944 :return: Returns a floating network description.
945 :rtype: :class:`flask.response`
947 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
949 # Fiddle with floating_network !
950 req
= json
.loads(request
.data
)
952 network_id
= req
["floatingip"]["floating_network_id"]
953 net
= self
.api
.compute
.find_network_by_name_or_id(network_id
)
954 if net
!= self
.api
.manage
.floating_network
:
955 return Response("You have to specify the existing floating network\n",
956 status
=400, mimetype
='application/json')
958 port_id
= req
["floatingip"].get("port_id", None)
959 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
961 if port
.net_name
!= self
.api
.manage
.floating_network
.name
:
962 return Response("You have to specify a port in the floating network\n",
963 status
=400, mimetype
='application/json')
965 if port
.floating_ip
is not None:
966 return Response("We allow only one floating ip per port\n", status
=400, mimetype
='application/json')
968 num_ports
= len(self
.api
.compute
.ports
)
969 name
= "port:cp%s:fl:%s" % (num_ports
, str(uuid
.uuid4()))
970 port
= self
.api
.compute
.create_port(name
)
971 port
.net_name
= net
.name
972 port
.ip_address
= net
.get_new_ip_address(name
)
974 port
.floating_ip
= port
.ip_address
977 resp
= response
["floatingip"] = dict()
979 resp
["floating_network_id"] = net
.id
980 resp
["status"] = "ACTIVE"
982 resp
["port_id"] = port
.id
983 resp
["floating_ip_address"] = port
.floating_ip
984 resp
["fixed_ip_address"] = port
.floating_ip
986 return Response(json
.dumps(response
), status
=200, mimetype
='application/json')
987 except Exception as ex
:
988 LOG
.exception("Neutron: Create FloatingIP exception %s.", ex
)
989 return Response(ex
.message
, status
=500, mimetype
='application/json')