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(Shutdown
, "/shutdown")
49 self
.api
.add_resource(NeutronShowAPIv2Details
, "/v2.0")
50 self
.api
.add_resource(NeutronListNetworks
, "/v2.0/networks.json", "/v2.0/networks",
51 resource_class_kwargs
={'api': self
})
52 self
.api
.add_resource(NeutronShowNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
53 resource_class_kwargs
={'api': self
})
54 self
.api
.add_resource(NeutronCreateNetwork
, "/v2.0/networks.json", "/v2.0/networks",
55 resource_class_kwargs
={'api': self
})
56 self
.api
.add_resource(NeutronUpdateNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
57 resource_class_kwargs
={'api': self
})
58 self
.api
.add_resource(NeutronDeleteNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
59 resource_class_kwargs
={'api': self
})
60 self
.api
.add_resource(NeutronListSubnets
, "/v2.0/subnets.json", "/v2.0/subnets",
61 resource_class_kwargs
={'api': self
})
62 self
.api
.add_resource(NeutronShowSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
63 resource_class_kwargs
={'api': self
})
64 self
.api
.add_resource(NeutronCreateSubnet
, "/v2.0/subnets.json", "/v2.0/subnets",
65 resource_class_kwargs
={'api': self
})
66 self
.api
.add_resource(NeutronUpdateSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
67 resource_class_kwargs
={'api': self
})
68 self
.api
.add_resource(NeutronDeleteSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
69 resource_class_kwargs
={'api': self
})
70 self
.api
.add_resource(NeutronListPorts
, "/v2.0/ports.json", "/v2.0/ports",
71 resource_class_kwargs
={'api': self
})
72 self
.api
.add_resource(NeutronShowPort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
73 resource_class_kwargs
={'api': self
})
74 self
.api
.add_resource(NeutronCreatePort
, "/v2.0/ports.json", "/v2.0/ports",
75 resource_class_kwargs
={'api': self
})
76 self
.api
.add_resource(NeutronUpdatePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
77 resource_class_kwargs
={'api': self
})
78 self
.api
.add_resource(NeutronDeletePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
79 resource_class_kwargs
={'api': self
})
80 self
.api
.add_resource(NeutronAddFloatingIp
, "/v2.0/floatingips.json", "/v2.0/floatingips",
81 resource_class_kwargs
={'api': self
})
83 # Service Function Chaining (SFC) API
84 self
.api
.add_resource(SFC
.PortPairsCreate
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
85 resource_class_kwargs
={'api': self
})
86 self
.api
.add_resource(SFC
.PortPairsUpdate
, "/v2.0/sfc/port_pairs/<pair_id>.json",
87 "/v2.0/sfc/port_pairs/<pair_id>",
88 resource_class_kwargs
={'api': self
})
89 self
.api
.add_resource(SFC
.PortPairsDelete
, "/v2.0/sfc/port_pairs/<pair_id>.json",
90 "/v2.0/sfc/port_pairs/<pair_id>",
91 resource_class_kwargs
={'api': self
})
92 self
.api
.add_resource(SFC
.PortPairsList
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
93 resource_class_kwargs
={'api': self
})
94 self
.api
.add_resource(SFC
.PortPairsShow
, "/v2.0/sfc/port_pairs/<pair_id>.json",
95 "/v2.0/sfc/port_pairs/<pair_id>",
96 resource_class_kwargs
={'api': self
})
98 self
.api
.add_resource(SFC
.PortPairGroupCreate
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
99 resource_class_kwargs
={'api': self
})
100 self
.api
.add_resource(SFC
.PortPairGroupUpdate
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
101 "/v2.0/sfc/port_pair_groups/<group_id>",
102 resource_class_kwargs
={'api': self
})
103 self
.api
.add_resource(SFC
.PortPairGroupDelete
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
104 "/v2.0/sfc/port_pair_groups/<group_id>",
105 resource_class_kwargs
={'api': self
})
106 self
.api
.add_resource(SFC
.PortPairGroupList
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
107 resource_class_kwargs
={'api': self
})
108 self
.api
.add_resource(SFC
.PortPairGroupShow
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
109 "/v2.0/sfc/port_pair_groups/<group_id>",
110 resource_class_kwargs
={'api': self
})
112 self
.api
.add_resource(SFC
.FlowClassifierCreate
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
113 resource_class_kwargs
={'api': self
})
114 self
.api
.add_resource(SFC
.FlowClassifierUpdate
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
115 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
116 resource_class_kwargs
={'api': self
})
117 self
.api
.add_resource(SFC
.FlowClassifierDelete
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
118 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
119 resource_class_kwargs
={'api': self
})
120 self
.api
.add_resource(SFC
.FlowClassifierList
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
121 resource_class_kwargs
={'api': self
})
122 self
.api
.add_resource(SFC
.FlowClassifierShow
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
123 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
124 resource_class_kwargs
={'api': self
})
126 self
.api
.add_resource(SFC
.PortChainCreate
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
127 resource_class_kwargs
={'api': self
})
128 self
.api
.add_resource(SFC
.PortChainUpdate
, "/v2.0/sfc/port_chains/<chain_id>.json",
129 "/v2.0/sfc/port_chains/<chain_id>",
130 resource_class_kwargs
={'api': self
})
131 self
.api
.add_resource(SFC
.PortChainDelete
, "/v2.0/sfc/port_chains/<chain_id>.json",
132 "/v2.0/sfc/port_chains/<chain_id>",
133 resource_class_kwargs
={'api': self
})
134 self
.api
.add_resource(SFC
.PortChainList
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
135 resource_class_kwargs
={'api': self
})
136 self
.api
.add_resource(SFC
.PortChainShow
, "/v2.0/sfc/port_chains/<chain_id>.json",
137 "/v2.0/sfc/port_chains/<chain_id>",
138 resource_class_kwargs
={'api': self
})
140 def _start_flask(self
):
141 LOG
.info("Starting %s endpoint @ http://%s:%d" % (__name__
, self
.ip
, self
.port
))
142 if self
.app
is not None:
143 self
.app
.before_request(self
.dump_playbook
)
144 self
.app
.run(self
.ip
, self
.port
, debug
=True, use_reloader
=False)
147 class Shutdown(Resource
):
149 LOG
.debug(("%s is beeing shut down") % (__name__
))
150 func
= request
.environ
.get('werkzeug.server.shutdown')
152 raise RuntimeError('Not running with the Werkzeug Server')
156 class NeutronListAPIVersions(Resource
):
161 :return: Returns a json with API versions.
162 :rtype: :class:`flask.response`
164 LOG
.debug("API CALL: Neutron - List API Versions")
166 resp
['versions'] = dict()
173 "href": request
.url_root
+ '/v2.0',
178 resp
['versions'] = versions
180 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
183 class NeutronShowAPIv2Details(Resource
):
188 :return: Returns a json with API details.
189 :rtype: :class:`flask.response`
191 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
194 resp
['resources'] = dict()
195 resp
['resources'] = [{
198 "href": request
.url_root
+ 'v2.0/subnets',
203 "collection": "subnets"
208 "href": request
.url_root
+ 'v2.0/networks',
213 "collection": "networks"
218 "href": request
.url_root
+ 'v2.0/ports',
223 "collection": "ports"
227 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
230 class NeutronListNetworks(Resource
):
231 def __init__(self
, api
):
236 Lists all networks, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
237 network with the name, or the networks specified via id.
239 :return: Returns a json response, starting with 'networks' as root node.
240 :rtype: :class:`flask.response`
242 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
244 if request
.args
.get('name'):
245 tmp_network
= NeutronShowNetwork(self
.api
)
246 return tmp_network
.get_network(request
.args
.get('name'), True)
247 id_list
= request
.args
.getlist('id')
248 if len(id_list
) == 1:
249 tmp_network
= NeutronShowNetwork(self
.api
)
250 return tmp_network
.get_network(request
.args
.get('id'), True)
252 network_list
= list()
253 network_dict
= dict()
255 if len(id_list
) == 0:
256 for net
in self
.api
.compute
.nets
.values():
257 tmp_network_dict
= net
.create_network_dict()
258 if tmp_network_dict
not in network_list
:
259 network_list
.append(tmp_network_dict
)
261 for net
in self
.api
.compute
.nets
.values():
262 if net
.id in id_list
:
263 tmp_network_dict
= net
.create_network_dict()
264 if tmp_network_dict
not in network_list
:
265 network_list
.append(tmp_network_dict
)
267 network_dict
["networks"] = network_list
269 return Response(json
.dumps(network_dict
), status
=200, mimetype
='application/json')
271 except Exception as ex
:
272 LOG
.exception("Neutron: List networks exception.")
273 return Response(ex
.message
, status
=500, mimetype
='application/json')
276 class NeutronShowNetwork(Resource
):
277 def __init__(self
, api
):
280 def get(self
, network_id
):
282 Returns the network, specified via 'network_id'.
284 :param network_id: The unique ID string of the network.
285 :type network_id: ``str``
286 :return: Returns a json response, starting with 'network' as root node and one network description.
287 :rtype: :class:`flask.response`
289 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
290 return self
.get_network(network_id
, False)
292 def get_network(self
, network_name_or_id
, as_list
):
294 Returns one network description of the network, specified via 'network_name_or_id'.
296 :param network_name_or_id: The indicator string, which specifies the requested network.
297 :type network_name_or_id: ``str``
298 :param as_list: Determines if the network description should start with the root node 'network' or 'networks'.
299 :type as_list: ``bool``
300 :return: Returns a json response, with one network description.
301 :rtype: :class:`flask.response`
304 net
= self
.api
.compute
.find_network_by_name_or_id(network_name_or_id
)
306 return Response(u
'Network not found.\n', status
=404, mimetype
='application/json')
308 tmp_network_dict
= net
.create_network_dict()
311 tmp_dict
["networks"] = [tmp_network_dict
]
313 tmp_dict
["network"] = tmp_network_dict
315 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
318 except Exception as ex
:
319 logging
.exception("Neutron: Show network exception.")
320 return Response(ex
.message
, status
=500, mimetype
='application/json')
323 class NeutronCreateNetwork(Resource
):
324 def __init__(self
, api
):
329 Creates a network with the name, specified within the request under ['network']['name'].
331 :return: * 400, if the network already exists.
332 * 500, if any exception occurred while creation.
333 * 201, if everything worked out.
334 :rtype: :class:`flask.response`
336 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
338 network_dict
= json
.loads(request
.data
)
339 name
= network_dict
['network']['name']
340 net
= self
.api
.compute
.find_network_by_name_or_id(name
)
342 return Response('Network already exists.\n', status
=400, mimetype
='application/json')
344 net
= self
.api
.compute
.create_network(name
)
345 return Response(json
.dumps({"network": net
.create_network_dict()}), status
=201, mimetype
='application/json')
346 except Exception as ex
:
347 LOG
.exception("Neutron: Create network excepiton.")
348 return Response(ex
.message
, status
=500, mimetype
='application/json')
351 class NeutronUpdateNetwork(Resource
):
352 def __init__(self
, api
):
355 def put(self
, network_id
): # TODO currently only the name will be changed
357 Updates the existing network with the given parameters.
359 :param network_id: The indicator string, which specifies the requested network.
360 :type network_id: ``str``
361 :return: * 404, if the network could not be found.
362 * 500, if any exception occurred while updating the network.
363 * 200, if everything worked out.
364 :rtype: :class:`flask.response`
366 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
368 if network_id
in self
.api
.compute
.nets
:
369 net
= self
.api
.compute
.nets
[network_id
]
370 network_dict
= json
.loads(request
.data
)
371 old_net
= copy
.copy(net
)
373 if "status" in network_dict
["network"]:
374 net
.status
= network_dict
["network"]["status"]
375 if "subnets" in network_dict
["network"]:
376 pass # tmp_network_dict["subnets"] = None
377 if "name" in network_dict
["network"] and net
.name
!= network_dict
["network"]["name"]:
378 net
.name
= network_dict
["network"]["name"]
379 if "admin_state_up" in network_dict
["network"]:
380 pass # tmp_network_dict["admin_state_up"] = True
381 if "tenant_id" in network_dict
["network"]:
382 pass # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
383 if "shared" in network_dict
["network"]:
384 pass # tmp_network_dict["shared"] = False
386 return Response(json
.dumps(network_dict
), status
=200, mimetype
='application/json')
388 return Response('Network not found.\n', status
=404, mimetype
='application/json')
390 except Exception as ex
:
391 LOG
.exception("Neutron: Show networks exception.")
392 return Response(ex
.message
, status
=500, mimetype
='application/json')
395 class NeutronDeleteNetwork(Resource
):
396 def __init__(self
, api
):
399 def delete(self
, network_id
):
401 Deletes the specified network and all its subnets.
403 :param network_id: The indicator string, which specifies the requested network.
404 :type network_id: ``str``
405 :return: * 404, if the network or the subnet could not be removed.
406 * 500, if any exception occurred while deletion.
407 * 204, if everything worked out.
408 :rtype: :class:`flask.response`
410 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
412 if network_id
not in self
.api
.compute
.nets
:
413 return Response('Could not find network. (' + network_id
+ ')\n',
414 status
=404, mimetype
='application/json')
416 net
= self
.api
.compute
.nets
[network_id
]
417 delete_subnet
= NeutronDeleteSubnet(self
.api
)
418 resp
= delete_subnet
.delete(net
.subnet_id
)
420 if not '204' in resp
.status
and not '404' in resp
.status
:
423 self
.api
.compute
.delete_network(network_id
)
425 return Response('Network ' + str(network_id
) + ' deleted.\n', status
=204, mimetype
='application/json')
426 except Exception as ex
:
427 LOG
.exception("Neutron: Delete network exception.")
428 return Response(ex
.message
, status
=500, mimetype
='application/json')
431 class NeutronListSubnets(Resource
):
432 def __init__(self
, api
):
437 Lists all subnets, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
438 subnet with the name, or the subnets specified via id.
440 :return: Returns a json response, starting with 'subnets' as root node.
441 :rtype: :class:`flask.response`
443 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
445 if request
.args
.get('name'):
446 show_subnet
= NeutronShowSubnet(self
.api
)
447 return show_subnet
.get_subnet(request
.args
.get('name'), True)
448 id_list
= request
.args
.getlist('id')
449 if len(id_list
) == 1:
450 show_subnet
= NeutronShowSubnet(self
.api
)
451 return show_subnet
.get_subnet(id_list
[0], True)
456 if len(id_list
) == 0:
457 for net
in self
.api
.compute
.nets
.values():
458 if net
.subnet_id
is not None:
459 tmp_subnet_dict
= net
.create_subnet_dict()
460 subnet_list
.append(tmp_subnet_dict
)
462 for net
in self
.api
.compute
.nets
.values():
463 if net
.subnet_id
in id_list
:
464 tmp_subnet_dict
= net
.create_subnet_dict()
465 subnet_list
.append(tmp_subnet_dict
)
467 subnet_dict
["subnets"] = subnet_list
469 return Response(json
.dumps(subnet_dict
), status
=200, mimetype
='application/json')
471 except Exception as ex
:
472 LOG
.exception("Neutron: List subnets exception.")
473 return Response(ex
.message
, status
=500, mimetype
='application/json')
476 class NeutronShowSubnet(Resource
):
477 def __init__(self
, api
):
480 def get(self
, subnet_id
):
482 Returns the subnet, specified via 'subnet_id'.
484 :param subnet_id: The unique ID string of the subnet.
485 :type subnet_id: ``str``
486 :return: Returns a json response, starting with 'subnet' as root node and one subnet description.
487 :rtype: :class:`flask.response`
489 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
490 return self
.get_subnet(subnet_id
, False)
492 def get_subnet(self
, subnet_name_or_id
, as_list
):
494 Returns one subnet description of the subnet, specified via 'subnet_name_or_id'.
496 :param subnet_name_or_id: The indicator string, which specifies the requested subnet.
497 :type subnet_name_or_id: ``str``
498 :param as_list: Determines if the subnet description should start with the root node 'subnet' or 'subnets'.
499 :type as_list: ``bool``
500 :return: Returns a json response, with one subnet description.
501 :rtype: :class:`flask.response`
504 for net
in self
.api
.compute
.nets
.values():
505 if net
.subnet_id
== subnet_name_or_id
or net
.subnet_name
== subnet_name_or_id
:
506 tmp_subnet_dict
= net
.create_subnet_dict()
509 tmp_dict
["subnets"] = [tmp_subnet_dict
]
511 tmp_dict
["subnet"] = tmp_subnet_dict
512 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
514 return Response('Subnet not found. (' + subnet_name_or_id
+ ')\n', status
=404, mimetype
='application/json')
516 except Exception as ex
:
517 LOG
.exception("Neutron: Show subnet exception.")
518 return Response(ex
.message
, status
=500, mimetype
='application/json')
521 class NeutronCreateSubnet(Resource
):
522 def __init__(self
, api
):
527 Creates a subnet with the name, specified within the request under ['subnet']['name'].
529 :return: * 400, if the 'CIDR' format is wrong or it does not exist.
530 * 404, if the network was not found.
531 * 409, if the corresponding network already has one subnet.
532 * 500, if any exception occurred while creation and
533 * 201, if everything worked out.
534 :rtype: :class:`flask.response`
536 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
538 subnet_dict
= json
.loads(request
.data
)
539 net
= self
.api
.compute
.find_network_by_name_or_id(subnet_dict
['subnet']['network_id'])
542 return Response('Could not find network.\n', status
=404, mimetype
='application/json')
544 net
.subnet_name
= subnet_dict
["subnet"].get('name', str(net
.name
) + '-sub')
545 if net
.subnet_id
is not None:
546 return Response('Only one subnet per network is supported\n', status
=409, mimetype
='application/json')
548 if "id" in subnet_dict
["subnet"]:
549 net
.subnet_id
= subnet_dict
["subnet"]["id"]
551 net
.subnet_id
= str(uuid
.uuid4())
552 import emuvim
.api
.openstack
.ip_handler
as IP
553 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
555 if "tenant_id" in subnet_dict
["subnet"]:
557 if "allocation_pools" in subnet_dict
["subnet"]:
559 if "gateway_ip" in subnet_dict
["subnet"]:
560 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
561 if "ip_version" in subnet_dict
["subnet"]:
563 if "enable_dhcp" in subnet_dict
["subnet"]:
566 return Response(json
.dumps({'subnet': net
.create_subnet_dict()}), status
=201, mimetype
='application/json')
568 except Exception as ex
:
569 LOG
.exception("Neutron: Create network excepiton.")
570 return Response(ex
.message
, status
=500, mimetype
='application/json')
573 class NeutronUpdateSubnet(Resource
):
574 def __init__(self
, api
):
577 def put(self
, subnet_id
):
579 Updates the existing subnet with the given parameters.
581 :param subnet_id: The indicator string, which specifies the requested subnet.
582 :type subnet_id: ``str``
583 :return: * 404, if the network could not be found.
584 * 500, if any exception occurred while updating the network.
585 * 200, if everything worked out.
586 :rtype: :class:`flask.response`
588 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
590 for net
in self
.api
.compute
.nets
.values():
591 if net
.subnet_id
== subnet_id
:
592 subnet_dict
= json
.loads(request
.data
)
594 if "name" in subnet_dict
["subnet"]:
595 net
.subnet_name
= subnet_dict
["subnet"]["name"]
596 if "network_id" in subnet_dict
["subnet"]:
597 net
.id = subnet_dict
["subnet"]["network_id"]
598 if "tenant_id" in subnet_dict
["subnet"]:
600 if "allocation_pools" in subnet_dict
["subnet"]:
602 if "gateway_ip" in subnet_dict
["subnet"]:
603 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
604 if "ip_version" in subnet_dict
["subnet"]:
606 if "cidr" in subnet_dict
["subnet"]:
607 net
.set_cidr(subnet_dict
["subnet"]["cidr"])
608 if "id" in subnet_dict
["subnet"]:
609 net
.subnet_id
= subnet_dict
["subnet"]["id"]
610 if "enable_dhcp" in subnet_dict
["subnet"]:
613 net
.subnet_update_time
= str(datetime
.now())
614 tmp_dict
= {'subnet': net
.create_subnet_dict()}
615 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
617 return Response('Network not found.\n', status
=404, mimetype
='application/json')
619 except Exception as ex
:
620 LOG
.exception("Neutron: Show networks exception.")
621 return Response(ex
.message
, status
=500, mimetype
='application/json')
624 class NeutronDeleteSubnet(Resource
):
625 def __init__(self
, api
):
628 def delete(self
, subnet_id
):
630 Deletes the specified subnet.
632 :param subnet_id: The indicator string, which specifies the requested subnet.
633 :type subnet_id: ``str``
634 :return: * 404, if the subnet could not be removed.
635 * 500, if any exception occurred while deletion.
636 * 204, if everything worked out.
637 :rtype: :class:`flask.response`
639 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
641 for net
in self
.api
.compute
.nets
.values():
642 if net
.subnet_id
== subnet_id
:
643 for server
in self
.api
.compute
.computeUnits
.values():
644 for port_name
in server
.port_names
:
645 port
= self
.api
.compute
.find_port_by_name_or_id(port_name
)
646 if port
.net_name
== net
.name
:
647 port
.ip_address
= None
648 self
.api
.compute
.dc
.net
.removeLink(
650 node1
=self
.api
.compute
.dc
.containers
[server
.name
],
651 node2
=self
.api
.compute
.dc
.switch
)
656 return Response('Subnet ' + str(subnet_id
) + ' deleted.\n',
657 status
=204, mimetype
='application/json')
659 return Response('Could not find subnet.', status
=404, mimetype
='application/json')
660 except Exception as ex
:
661 LOG
.exception("Neutron: Delete subnet exception.")
662 return Response(ex
.message
, status
=500, mimetype
='application/json')
665 class NeutronListPorts(Resource
):
666 def __init__(self
, api
):
671 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
672 port with the name, or the ports specified via id.
674 :return: Returns a json response, starting with 'ports' as root node.
675 :rtype: :class:`flask.response`
677 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
679 if request
.args
.get('name'):
680 show_port
= NeutronShowPort(self
.api
)
681 return show_port
.get_port(request
.args
.get('name'), True)
682 id_list
= request
.args
.getlist('id')
683 if len(id_list
) == 1:
684 show_port
= NeutronShowPort(self
.api
)
685 return show_port
.get_port(request
.args
.get('id'), True)
690 if len(id_list
) == 0:
691 for port
in self
.api
.compute
.ports
.values():
692 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
693 port_list
.append(tmp_port_dict
)
695 for port
in self
.api
.compute
.ports
.values():
696 if port
.id in id_list
:
697 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
698 port_list
.append(tmp_port_dict
)
700 port_dict
["ports"] = port_list
702 return Response(json
.dumps(port_dict
), status
=200, mimetype
='application/json')
704 except Exception as ex
:
705 LOG
.exception("Neutron: List ports exception.")
706 return Response(ex
.message
, status
=500, mimetype
='application/json')
709 class NeutronShowPort(Resource
):
710 def __init__(self
, api
):
713 def get(self
, port_id
):
715 Returns the port, specified via 'port_id'.
717 :param port_id: The unique ID string of the network.
718 :type port_id: ``str``
719 :return: Returns a json response, starting with 'port' as root node and one network description.
720 :rtype: :class:`flask.response`
722 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
723 return self
.get_port(port_id
, False)
725 def get_port(self
, port_name_or_id
, as_list
):
727 Returns one network description of the port, specified via 'port_name_or_id'.
729 :param port_name_or_id: The indicator string, which specifies the requested port.
730 :type port_name_or_id: ``str``
731 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
732 :type as_list: ``bool``
733 :return: Returns a json response, with one port description.
734 :rtype: :class:`flask.response`
737 port
= self
.api
.compute
.find_port_by_name_or_id(port_name_or_id
)
739 return Response('Port not found. (' + port_name_or_id
+ ')\n', status
=404, mimetype
='application/json')
740 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
743 tmp_dict
["ports"] = [tmp_port_dict
]
745 tmp_dict
["port"] = tmp_port_dict
746 return Response(json
.dumps(tmp_dict
), status
=200, mimetype
='application/json')
747 except Exception as ex
:
748 LOG
.exception("Neutron: Show port exception.")
749 return Response(ex
.message
, status
=500, mimetype
='application/json')
752 class NeutronCreatePort(Resource
):
753 def __init__(self
, api
):
758 Creates a port with the name, specified within the request under ['port']['name'].
760 :return: * 404, if the network could not be found.
761 * 500, if any exception occurred while creation and
762 * 201, if everything worked out.
763 :rtype: :class:`flask.response`
765 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
767 port_dict
= json
.loads(request
.data
)
768 net_id
= port_dict
['port']['network_id']
770 if net_id
not in self
.api
.compute
.nets
:
771 return Response('Could not find network.\n', status
=404, mimetype
='application/json')
773 net
= self
.api
.compute
.nets
[net_id
]
774 if 'name' in port_dict
['port']:
775 name
= port_dict
['port']['name']
777 num_ports
= len(self
.api
.compute
.ports
)
778 name
= "port:cp%s:man:%s" % (num_ports
, str(uuid
.uuid4()))
780 if self
.api
.compute
.find_port_by_name_or_id(name
):
781 return Response("Port with name %s already exists.\n" % name
, status
=500, mimetype
='application/json')
783 port
= self
.api
.compute
.create_port(name
)
785 port
.net_name
= net
.name
786 port
.ip_address
= net
.get_new_ip_address(name
)
788 if "admin_state_up" in port_dict
["port"]:
790 if "device_id" in port_dict
["port"]:
792 if "device_owner" in port_dict
["port"]:
794 if "fixed_ips" in port_dict
["port"]:
796 if "mac_address" in port_dict
["port"]:
797 port
.mac_address
= port_dict
["port"]["mac_address"]
798 if "status" in port_dict
["port"]:
800 if "tenant_id" in port_dict
["port"]:
803 # add the port to a stack if the specified network is a stack network
804 for stack
in self
.api
.compute
.stacks
.values():
805 for net
in stack
.nets
.values():
807 stack
.ports
[name
] = port
809 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=201,
810 mimetype
='application/json')
811 except Exception as ex
:
812 LOG
.exception("Neutron: Show port exception.")
813 return Response(ex
.message
, status
=500, mimetype
='application/json')
816 class NeutronUpdatePort(Resource
):
817 def __init__(self
, api
):
820 def put(self
, port_id
):
822 Updates the existing port with the given parameters.
824 :param network_id: The indicator string, which specifies the requested port.
825 :type network_id: ``str``
826 :return: * 404, if the network could not be found.
827 * 500, if any exception occurred while updating the network.
828 * 200, if everything worked out.
829 :rtype: :class:`flask.response`
831 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
833 port_dict
= json
.loads(request
.data
)
834 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
836 return Response("Port with id %s does not exists.\n" % port_id
, status
=404, mimetype
='application/json')
837 old_port
= copy
.copy(port
)
840 for s
in self
.api
.compute
.stacks
.values():
841 for port
in s
.ports
.values():
842 if port
.id == port_id
:
844 if "admin_state_up" in port_dict
["port"]:
846 if "device_id" in port_dict
["port"]:
848 if "device_owner" in port_dict
["port"]:
850 if "fixed_ips" in port_dict
["port"]:
852 if "id" in port_dict
["port"]:
853 port
.id = port_dict
["port"]["id"]
854 if "mac_address" in port_dict
["port"]:
855 port
.mac_address
= port_dict
["port"]["mac_address"]
856 if "name" in port_dict
["port"] and port_dict
["port"]["name"] != port
.name
:
857 port
.set_name(port_dict
["port"]["name"])
858 if stack
is not None:
859 if port
.net_name
in stack
.nets
:
860 stack
.nets
[port
.net_name
].update_port_name_for_ip_address(port
.ip_address
, port
.name
)
861 stack
.ports
[port
.name
] = stack
.ports
[old_port
.name
]
862 del stack
.ports
[old_port
.name
]
863 if "network_id" in port_dict
["port"]:
865 if "status" in port_dict
["port"]:
867 if "tenant_id" in port_dict
["port"]:
870 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=200,
871 mimetype
='application/json')
872 except Exception as ex
:
873 LOG
.exception("Neutron: Update port exception.")
874 return Response(ex
.message
, status
=500, mimetype
='application/json')
877 class NeutronDeletePort(Resource
):
878 def __init__(self
, api
):
881 def delete(self
, port_id
):
883 Deletes the specified port.
885 :param port_id: The indicator string, which specifies the requested port.
886 :type port_id: ``str``
887 :return: * 404, if the port could not be found.
888 * 500, if any exception occurred while deletion.
889 * 204, if everything worked out.
890 :rtype: :class:`flask.response`
892 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
894 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
896 return Response("Port with id %s does not exists.\n" % port_id
, status
=404)
898 for s
in self
.api
.compute
.stacks
.values():
899 for p
in s
.ports
.values():
902 if stack
is not None:
903 if port
.net_name
in stack
.nets
:
904 stack
.nets
[port
.net_name
].withdraw_ip_address(port
.ip_address
)
905 for server
in stack
.servers
.values():
907 server
.port_names
.remove(port
.name
)
912 self
.api
.compute
.delete_port(port
.id)
914 return Response('Port ' + port_id
+ ' deleted.\n', status
=204, mimetype
='application/json')
916 except Exception as ex
:
917 LOG
.exception("Neutron: Delete port exception.")
918 return Response(ex
.message
, status
=500, mimetype
='application/json')
921 class NeutronAddFloatingIp(Resource
):
922 def __init__(self
, api
):
927 Added a quick and dirty fake for the OSM integration. Returns a list of
928 floating IPs. Has nothing to do with the setup inside the emulator.
929 But its enough to make the OSM driver happy.
930 @PG Sandman: Feel free to improve this and let it do something meaningful.
933 resp
["floatingips"] = list()
934 # create a list of floting IP definitions and return it
935 for i
in range(100, 110):
937 ip
["router_id"] = "router_id"
938 ip
["description"] = "hardcoded in api"
939 ip
["created_at"] = "router_id"
940 ip
["updated_at"] = "router_id"
941 ip
["revision_number"] = 1
942 ip
["tenant_id"] = "tenant_id"
943 ip
["project_id"] = "project_id"
944 ip
["floating_network_id"] = str(i
)
945 ip
["status"] = "ACTIVE"
947 ip
["port_id"] = "port_id"
948 ip
["floating_ip_address"] = "172.0.0.%d" % i
949 ip
["fixed_ip_address"] = "10.0.0.%d" % i
950 resp
["floatingips"].append(ip
)
951 return Response(json
.dumps(resp
), status
=200, mimetype
='application/json')
955 Adds a floating IP to neutron.
957 :return: Returns a floating network description.
958 :rtype: :class:`flask.response`
960 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
962 # Fiddle with floating_network !
963 req
= json
.loads(request
.data
)
965 network_id
= req
["floatingip"]["floating_network_id"]
966 net
= self
.api
.compute
.find_network_by_name_or_id(network_id
)
967 if net
!= self
.api
.manage
.floating_network
:
968 return Response("You have to specify the existing floating network\n",
969 status
=400, mimetype
='application/json')
971 port_id
= req
["floatingip"].get("port_id", None)
972 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
974 if port
.net_name
!= self
.api
.manage
.floating_network
.name
:
975 return Response("You have to specify a port in the floating network\n",
976 status
=400, mimetype
='application/json')
978 if port
.floating_ip
is not None:
979 return Response("We allow only one floating ip per port\n", status
=400, mimetype
='application/json')
981 num_ports
= len(self
.api
.compute
.ports
)
982 name
= "port:cp%s:fl:%s" % (num_ports
, str(uuid
.uuid4()))
983 port
= self
.api
.compute
.create_port(name
)
984 port
.net_name
= net
.name
985 port
.ip_address
= net
.get_new_ip_address(name
)
987 port
.floating_ip
= port
.ip_address
990 resp
= response
["floatingip"] = dict()
992 resp
["floating_network_id"] = net
.id
993 resp
["status"] = "ACTIVE"
995 resp
["port_id"] = port
.id
996 resp
["floating_ip_address"] = port
.floating_ip
997 resp
["fixed_ip_address"] = port
.floating_ip
999 return Response(json
.dumps(response
), status
=200, mimetype
='application/json')
1000 except Exception as ex
:
1001 LOG
.exception("Neutron: Create FloatingIP exception %s.", ex
)
1002 return Response(ex
.message
, status
=500, mimetype
='application/json')