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 return Response('Only one subnet per network is supported\n', status
=409, mimetype
='application/json')
532 if "id" in subnet_dict
["subnet"]:
533 net
.subnet_id
= subnet_dict
["subnet"]["id"]
535 net
.subnet_id
= str(uuid
.uuid4())
536 import emuvim
.api
.openstack
.ip_handler
as IP
537 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
539 if "tenant_id" in subnet_dict
["subnet"]:
541 if "allocation_pools" in subnet_dict
["subnet"]:
543 if "gateway_ip" in subnet_dict
["subnet"]:
544 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
545 if "ip_version" in subnet_dict
["subnet"]:
547 if "enable_dhcp" in subnet_dict
["subnet"]:
550 return Response(json
.dumps({'subnet': net
.create_subnet_dict()}), status
=201, mimetype
='application/json')
552 except Exception as ex
:
553 LOG
.exception("Neutron: Create network excepiton.")
554 return Response(ex
.message
, status
=500, mimetype
='application/json')
557 class NeutronUpdateSubnet(Resource
):
558 def __init__(self
, api
):
561 def put(self
, subnet_id
):
563 Updates the existing subnet with the given parameters.
565 :param subnet_id: The indicator string, which specifies the requested subnet.
566 :type subnet_id: ``str``
567 :return: * 404, if the network could not be found.
568 * 500, if any exception occurred while updating the network.
569 * 200, if everything worked out.
570 :rtype: :class:`flask.response`
572 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
574 for net
in self
.api
.compute
.nets
.values():
575 if net
.subnet_id
== subnet_id
:
576 subnet_dict
= json
.loads(request
.data
)
578 if "name" in subnet_dict
["subnet"]:
579 net
.subnet_name
= subnet_dict
["subnet"]["name"]
580 if "network_id" in subnet_dict
["subnet"]:
581 net
.id = subnet_dict
["subnet"]["network_id"]
582 if "tenant_id" in subnet_dict
["subnet"]:
584 if "allocation_pools" in subnet_dict
["subnet"]:
586 if "gateway_ip" in subnet_dict
["subnet"]:
587 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
588 if "ip_version" in subnet_dict
["subnet"]:
590 if "cidr" in subnet_dict
["subnet"]:
591 net
.set_cidr(subnet_dict
["subnet"]["cidr"])
592 if "id" in subnet_dict
["subnet"]:
593 net
.subnet_id
= subnet_dict
["subnet"]["id"]
594 if "enable_dhcp" in subnet_dict
["subnet"]:
597 net
.subnet_update_time
= str(datetime
.now())
598 tmp_dict
= {'subnet': net
.create_subnet_dict()}
599 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
601 return Response('Network not found.\n', status
=404, mimetype
='application/json')
603 except Exception as ex
:
604 LOG
.exception("Neutron: Show networks exception.")
605 return Response(ex
.message
, status
=500, mimetype
='application/json')
608 class NeutronDeleteSubnet(Resource
):
609 def __init__(self
, api
):
612 def delete(self
, subnet_id
):
614 Deletes the specified subnet.
616 :param subnet_id: The indicator string, which specifies the requested subnet.
617 :type subnet_id: ``str``
618 :return: * 404, if the subnet could not be removed.
619 * 500, if any exception occurred while deletion.
620 * 204, if everything worked out.
621 :rtype: :class:`flask.response`
623 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
625 for net
in self
.api
.compute
.nets
.values():
626 if net
.subnet_id
== subnet_id
:
627 for server
in self
.api
.compute
.computeUnits
.values():
628 for port_name
in server
.port_names
:
629 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
630 if port
.net_name
== net
.name
:
631 port
.ip_address
= None
632 self
.api
.compute
.dc
.net
.removeLink(
634 node1
=self
.api
.compute
.dc
.containers
[server
.name
],
635 node2
=self
.api
.compute
.dc
.switch
)
640 return Response('', status
=204, mimetype
='application/json')
642 return Response('Could not find subnet.', status
=404, mimetype
='application/json')
643 except Exception as ex
:
644 LOG
.exception("Neutron: Delete subnet exception.")
645 return Response(ex
.message
, status
=500, mimetype
='application/json')
648 class NeutronListPorts(Resource
):
649 def __init__(self
, api
):
654 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
655 port with the name, or the ports specified via id.
657 :return: Returns a json response, starting with 'ports' as root node.
658 :rtype: :class:`flask.response`
660 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
662 if request
.args
.get('name'):
663 show_port
= NeutronShowPort(self
.api
)
664 return show_port
.get_port(request
.args
.get('name'), True)
665 id_list
= request
.args
.getlist('id')
666 if len(id_list
) == 1:
667 show_port
= NeutronShowPort(self
.api
)
668 return show_port
.get_port(request
.args
.get('id'), True)
673 if len(id_list
) == 0:
674 for port
in self
.api
.compute
.ports
.values():
675 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
676 port_list
.append(tmp_port_dict
)
678 for port
in self
.api
.compute
.ports
.values():
679 if port
.id in id_list
:
680 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
681 port_list
.append(tmp_port_dict
)
683 port_dict
["ports"] = port_list
685 return Response(json
.dumps(port_dict
), status
=200, mimetype
='application/json')
687 except Exception as ex
:
688 LOG
.exception("Neutron: List ports exception.")
689 return Response(ex
.message
, status
=500, mimetype
='application/json')
692 class NeutronShowPort(Resource
):
693 def __init__(self
, api
):
696 def get(self
, port_id
):
698 Returns the port, specified via 'port_id'.
700 :param port_id: The unique ID string of the network.
701 :type port_id: ``str``
702 :return: Returns a json response, starting with 'port' as root node and one network description.
703 :rtype: :class:`flask.response`
705 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
706 return self
.get_port(port_id
, False)
708 def get_port(self
, port_name_or_id
, as_list
):
710 Returns one network description of the port, specified via 'port_name_or_id'.
712 :param port_name_or_id: The indicator string, which specifies the requested port.
713 :type port_name_or_id: ``str``
714 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
715 :type as_list: ``bool``
716 :return: Returns a json response, with one port description.
717 :rtype: :class:`flask.response`
720 port
= self
.api
.compute
.find_port_by_name_or_id(port_name_or_id
)
722 return Response('Port not found. (' + port_name_or_id
+ ')\n', status
=404, mimetype
='application/json')
723 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
726 tmp_dict
["ports"] = [tmp_port_dict
]
728 tmp_dict
["port"] = tmp_port_dict
729 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
730 except Exception as ex
:
731 LOG
.exception("Neutron: Show port exception.")
732 return Response(ex
.message
, status
=500, mimetype
='application/json')
735 class NeutronCreatePort(Resource
):
736 def __init__(self
, api
):
741 Creates a port with the name, specified within the request under ['port']['name'].
743 :return: * 404, if the network could not be found.
744 * 500, if any exception occurred while creation and
745 * 201, if everything worked out.
746 :rtype: :class:`flask.response`
748 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
750 port_dict
= json
.loads(request
.data
)
751 net_id
= port_dict
['port']['network_id']
753 if net_id
not in self
.api
.compute
.nets
:
754 return Response('Could not find network.\n', status
=404, mimetype
='application/json')
756 net
= self
.api
.compute
.nets
[net_id
]
757 if 'name' in port_dict
['port']:
758 name
= port_dict
['port']['name']
760 num_ports
= len(self
.api
.compute
.ports
)
761 name
= "port:cp%s:man:%s" % (num_ports
, str(uuid
.uuid4()))
763 if self
.api
.compute
.find_port_by_name_or_id(name
):
764 return Response("Port with name %s already exists.\n" % name
, status
=500, mimetype
='application/json')
766 port
= self
.api
.compute
.create_port(name
)
768 port
.net_name
= net
.name
769 port
.ip_address
= net
.get_new_ip_address(name
)
771 if "admin_state_up" in port_dict
["port"]:
773 if "device_id" in port_dict
["port"]:
775 if "device_owner" in port_dict
["port"]:
777 if "fixed_ips" in port_dict
["port"]:
779 if "mac_address" in port_dict
["port"]:
780 port
.mac_address
= port_dict
["port"]["mac_address"]
781 if "status" in port_dict
["port"]:
783 if "tenant_id" in port_dict
["port"]:
786 # add the port to a stack if the specified network is a stack network
787 for stack
in self
.api
.compute
.stacks
.values():
788 for net
in stack
.nets
.values():
790 stack
.ports
[name
] = port
792 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=201,
793 mimetype
='application/json')
794 except Exception as ex
:
795 LOG
.exception("Neutron: Show port exception.")
796 return Response(ex
.message
, status
=500, mimetype
='application/json')
799 class NeutronUpdatePort(Resource
):
800 def __init__(self
, api
):
803 def put(self
, port_id
):
805 Updates the existing port with the given parameters.
807 :param network_id: The indicator string, which specifies the requested port.
808 :type network_id: ``str``
809 :return: * 404, if the network could not be found.
810 * 500, if any exception occurred while updating the network.
811 * 200, if everything worked out.
812 :rtype: :class:`flask.response`
814 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
816 port_dict
= json
.loads(request
.data
)
817 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
819 return Response("Port with id %s does not exists.\n" % port_id
, status
=404, mimetype
='application/json')
820 old_port
= copy
.copy(port
)
823 for s
in self
.api
.compute
.stacks
.values():
824 for port
in s
.ports
.values():
825 if port
.id == port_id
:
827 if "admin_state_up" in port_dict
["port"]:
829 if "device_id" in port_dict
["port"]:
831 if "device_owner" in port_dict
["port"]:
833 if "fixed_ips" in port_dict
["port"]:
835 if "id" in port_dict
["port"]:
836 port
.id = port_dict
["port"]["id"]
837 if "mac_address" in port_dict
["port"]:
838 port
.mac_address
= port_dict
["port"]["mac_address"]
839 if "name" in port_dict
["port"] and port_dict
["port"]["name"] != port
.name
:
840 port
.set_name(port_dict
["port"]["name"])
841 if stack
is not None:
842 if port
.net_name
in stack
.nets
:
843 stack
.nets
[port
.net_name
].update_port_name_for_ip_address(port
.ip_address
, port
.name
)
844 stack
.ports
[port
.name
] = stack
.ports
[old_port
.name
]
845 del stack
.ports
[old_port
.name
]
846 if "network_id" in port_dict
["port"]:
848 if "status" in port_dict
["port"]:
850 if "tenant_id" in port_dict
["port"]:
853 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=200,
854 mimetype
='application/json')
855 except Exception as ex
:
856 LOG
.exception("Neutron: Update port exception.")
857 return Response(ex
.message
, status
=500, mimetype
='application/json')
860 class NeutronDeletePort(Resource
):
861 def __init__(self
, api
):
864 def delete(self
, port_id
):
866 Deletes the specified port.
868 :param port_id: The indicator string, which specifies the requested port.
869 :type port_id: ``str``
870 :return: * 404, if the port could not be found.
871 * 500, if any exception occurred while deletion.
872 * 204, if everything worked out.
873 :rtype: :class:`flask.response`
875 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
877 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
879 return Response("Port with id %s does not exists.\n" % port_id
, status
=404)
881 for s
in self
.api
.compute
.stacks
.values():
882 for p
in s
.ports
.values():
885 if stack
is not None:
886 if port
.net_name
in stack
.nets
:
887 stack
.nets
[port
.net_name
].withdraw_ip_address(port
.ip_address
)
888 for server
in stack
.servers
.values():
890 server
.port_names
.remove(port
.name
)
895 self
.api
.compute
.delete_port(port
.id)
897 return Response('', status
=204, mimetype
='application/json')
899 except Exception as ex
:
900 LOG
.exception("Neutron: Delete port exception.")
901 return Response(ex
.message
, status
=500, mimetype
='application/json')
904 class NeutronAddFloatingIp(Resource
):
905 def __init__(self
, api
):
910 Added a quick and dirty fake for the OSM integration. Returns a list of
911 floating IPs. Has nothing to do with the setup inside the emulator.
912 But its enough to make the OSM driver happy.
913 @PG Sandman: Feel free to improve this and let it do something meaningful.
916 resp
["floatingips"] = list()
917 # create a list of floting IP definitions and return it
918 for i
in range(100, 110):
920 ip
["router_id"] = "router_id"
921 ip
["description"] = "hardcoded in api"
922 ip
["created_at"] = "router_id"
923 ip
["updated_at"] = "router_id"
924 ip
["revision_number"] = 1
925 ip
["tenant_id"] = "tenant_id"
926 ip
["project_id"] = "project_id"
927 ip
["floating_network_id"] = str(i
)
928 ip
["status"] = "ACTIVE"
930 ip
["port_id"] = "port_id"
931 ip
["floating_ip_address"] = "172.0.0.%d" % i
932 ip
["fixed_ip_address"] = "10.0.0.%d" % i
933 resp
["floatingips"].append(ip
)
934 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
938 Adds a floating IP to neutron.
940 :return: Returns a floating network description.
941 :rtype: :class:`flask.response`
943 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
945 # Fiddle with floating_network !
946 req
= json
.loads(request
.data
)
948 network_id
= req
["floatingip"]["floating_network_id"]
949 net
= self
.api
.compute
.find_network_by_name_or_id(network_id
)
950 if net
!= self
.api
.manage
.floating_network
:
951 return Response("You have to specify the existing floating network\n",
952 status
=400, mimetype
='application/json')
954 port_id
= req
["floatingip"].get("port_id", None)
955 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
957 if port
.net_name
!= self
.api
.manage
.floating_network
.name
:
958 return Response("You have to specify a port in the floating network\n",
959 status
=400, mimetype
='application/json')
961 if port
.floating_ip
is not None:
962 return Response("We allow only one floating ip per port\n", status
=400, mimetype
='application/json')
964 num_ports
= len(self
.api
.compute
.ports
)
965 name
= "port:cp%s:fl:%s" % (num_ports
, str(uuid
.uuid4()))
966 port
= self
.api
.compute
.create_port(name
)
967 port
.net_name
= net
.name
968 port
.ip_address
= net
.get_new_ip_address(name
)
970 port
.floating_ip
= port
.ip_address
973 resp
= response
["floatingip"] = dict()
975 resp
["floating_network_id"] = net
.id
976 resp
["status"] = "ACTIVE"
978 resp
["port_id"] = port
.id
979 resp
["floating_ip_address"] = port
.floating_ip
980 resp
["fixed_ip_address"] = port
.floating_ip
982 return Response(json
.dumps(response
), status
=200, mimetype
='application/json')
983 except Exception as ex
:
984 LOG
.exception("Neutron: Create FloatingIP exception %s.", ex
)
985 return Response(ex
.message
, status
=500, mimetype
='application/json')