1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # Neither the name of the SONATA-NFV, Paderborn University
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior written
21 # This work has been performed in the framework of the SONATA project,
22 # funded by the European Commission under Grant number 671517 through
23 # the Horizon 2020 and 5G-PPP programmes. The authors would like to
24 # acknowledge the contributions of their colleagues of the SONATA
25 # partner consortium (www.sonata-nfv.eu).
26 from flask_restful
import Resource
27 from flask
import request
, Response
28 from emuvim
.api
.openstack
.openstack_dummies
.base_openstack_dummy
import BaseOpenstackDummy
29 from datetime
import datetime
30 import neutron_sfc_dummy_api
as SFC
36 LOG
= logging
.getLogger("api.openstack.neutron")
39 class NeutronDummyApi(BaseOpenstackDummy
):
40 def __init__(self
, ip
, port
, compute
):
41 super(NeutronDummyApi
, self
).__init
__(ip
, port
)
42 self
.compute
= compute
44 # create default networks (OSM usually assumes to have these
46 self
.compute
.create_network("mgmt")
47 self
.compute
.create_network("mgmtnet")
49 self
.api
.add_resource(NeutronListAPIVersions
, "/")
50 self
.api
.add_resource(NeutronShowAPIv2Details
, "/v2.0")
51 self
.api
.add_resource(NeutronListNetworks
, "/v2.0/networks.json", "/v2.0/networks",
52 resource_class_kwargs
={'api': self
})
53 self
.api
.add_resource(NeutronShowNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
54 resource_class_kwargs
={'api': self
})
55 self
.api
.add_resource(NeutronCreateNetwork
, "/v2.0/networks.json", "/v2.0/networks",
56 resource_class_kwargs
={'api': self
})
57 self
.api
.add_resource(NeutronUpdateNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
58 resource_class_kwargs
={'api': self
})
59 self
.api
.add_resource(NeutronDeleteNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
60 resource_class_kwargs
={'api': self
})
61 self
.api
.add_resource(NeutronListSubnets
, "/v2.0/subnets.json", "/v2.0/subnets",
62 resource_class_kwargs
={'api': self
})
63 self
.api
.add_resource(NeutronShowSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
64 resource_class_kwargs
={'api': self
})
65 self
.api
.add_resource(NeutronCreateSubnet
, "/v2.0/subnets.json", "/v2.0/subnets",
66 resource_class_kwargs
={'api': self
})
67 self
.api
.add_resource(NeutronUpdateSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
68 resource_class_kwargs
={'api': self
})
69 self
.api
.add_resource(NeutronDeleteSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
70 resource_class_kwargs
={'api': self
})
71 self
.api
.add_resource(NeutronListPorts
, "/v2.0/ports.json", "/v2.0/ports",
72 resource_class_kwargs
={'api': self
})
73 self
.api
.add_resource(NeutronShowPort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
74 resource_class_kwargs
={'api': self
})
75 self
.api
.add_resource(NeutronCreatePort
, "/v2.0/ports.json", "/v2.0/ports",
76 resource_class_kwargs
={'api': self
})
77 self
.api
.add_resource(NeutronUpdatePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
78 resource_class_kwargs
={'api': self
})
79 self
.api
.add_resource(NeutronDeletePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
80 resource_class_kwargs
={'api': self
})
81 self
.api
.add_resource(NeutronAddFloatingIp
, "/v2.0/floatingips.json", "/v2.0/floatingips",
82 resource_class_kwargs
={'api': self
})
84 # Service Function Chaining (SFC) API
85 self
.api
.add_resource(SFC
.PortPairsCreate
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
86 resource_class_kwargs
={'api': self
})
87 self
.api
.add_resource(SFC
.PortPairsUpdate
, "/v2.0/sfc/port_pairs/<pair_id>.json",
88 "/v2.0/sfc/port_pairs/<pair_id>",
89 resource_class_kwargs
={'api': self
})
90 self
.api
.add_resource(SFC
.PortPairsDelete
, "/v2.0/sfc/port_pairs/<pair_id>.json",
91 "/v2.0/sfc/port_pairs/<pair_id>",
92 resource_class_kwargs
={'api': self
})
93 self
.api
.add_resource(SFC
.PortPairsList
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
94 resource_class_kwargs
={'api': self
})
95 self
.api
.add_resource(SFC
.PortPairsShow
, "/v2.0/sfc/port_pairs/<pair_id>.json",
96 "/v2.0/sfc/port_pairs/<pair_id>",
97 resource_class_kwargs
={'api': self
})
99 self
.api
.add_resource(SFC
.PortPairGroupCreate
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
100 resource_class_kwargs
={'api': self
})
101 self
.api
.add_resource(SFC
.PortPairGroupUpdate
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
102 "/v2.0/sfc/port_pair_groups/<group_id>",
103 resource_class_kwargs
={'api': self
})
104 self
.api
.add_resource(SFC
.PortPairGroupDelete
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
105 "/v2.0/sfc/port_pair_groups/<group_id>",
106 resource_class_kwargs
={'api': self
})
107 self
.api
.add_resource(SFC
.PortPairGroupList
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
108 resource_class_kwargs
={'api': self
})
109 self
.api
.add_resource(SFC
.PortPairGroupShow
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
110 "/v2.0/sfc/port_pair_groups/<group_id>",
111 resource_class_kwargs
={'api': self
})
113 self
.api
.add_resource(SFC
.FlowClassifierCreate
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
114 resource_class_kwargs
={'api': self
})
115 self
.api
.add_resource(SFC
.FlowClassifierUpdate
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
116 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
117 resource_class_kwargs
={'api': self
})
118 self
.api
.add_resource(SFC
.FlowClassifierDelete
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
119 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
120 resource_class_kwargs
={'api': self
})
121 self
.api
.add_resource(SFC
.FlowClassifierList
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
122 resource_class_kwargs
={'api': self
})
123 self
.api
.add_resource(SFC
.FlowClassifierShow
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
124 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
125 resource_class_kwargs
={'api': self
})
127 self
.api
.add_resource(SFC
.PortChainCreate
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
128 resource_class_kwargs
={'api': self
})
129 self
.api
.add_resource(SFC
.PortChainUpdate
, "/v2.0/sfc/port_chains/<chain_id>.json",
130 "/v2.0/sfc/port_chains/<chain_id>",
131 resource_class_kwargs
={'api': self
})
132 self
.api
.add_resource(SFC
.PortChainDelete
, "/v2.0/sfc/port_chains/<chain_id>.json",
133 "/v2.0/sfc/port_chains/<chain_id>",
134 resource_class_kwargs
={'api': self
})
135 self
.api
.add_resource(SFC
.PortChainList
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
136 resource_class_kwargs
={'api': self
})
137 self
.api
.add_resource(SFC
.PortChainShow
, "/v2.0/sfc/port_chains/<chain_id>.json",
138 "/v2.0/sfc/port_chains/<chain_id>",
139 resource_class_kwargs
={'api': self
})
142 class NeutronListAPIVersions(Resource
):
147 :return: Returns a json with API versions.
148 :rtype: :class:`flask.response`
150 LOG
.debug("API CALL: Neutron - List API Versions")
152 resp
['versions'] = dict()
159 "href": request
.url_root
+ '/v2.0',
164 resp
['versions'] = versions
166 return Response(json
.dumps(resp
), status
=200,
167 mimetype
='application/json')
170 class NeutronShowAPIv2Details(Resource
):
175 :return: Returns a json with API details.
176 :rtype: :class:`flask.response`
178 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
181 resp
['resources'] = dict()
182 resp
['resources'] = [{
185 "href": request
.url_root
+ 'v2.0/subnets',
190 "collection": "subnets"
195 "href": request
.url_root
+ 'v2.0/networks',
200 "collection": "networks"
205 "href": request
.url_root
+ 'v2.0/ports',
210 "collection": "ports"
214 return Response(json
.dumps(resp
), status
=200,
215 mimetype
='application/json')
218 class NeutronListNetworks(Resource
):
219 def __init__(self
, api
):
224 Lists all networks, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
225 network with the name, or the networks specified via id.
227 :return: Returns a json response, starting with 'networks' as root node.
228 :rtype: :class:`flask.response`
230 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
231 # LOG.debug("ARGS: {}".format(request.args))
233 if request
.args
.get('name'):
234 tmp_network
= NeutronShowNetwork(self
.api
)
235 response
= tmp_network
.get_network(
236 request
.args
.get('name'), True)
237 LOG
.debug("{} RESPONSE (1): {}".format(
238 self
.__class
__.__name
__, response
))
240 id_list
= request
.args
.getlist('id')
241 if len(id_list
) == 1:
242 tmp_network
= NeutronShowNetwork(self
.api
)
243 response
= tmp_network
.get_network(
244 request
.args
.get('id'), True)
245 LOG
.debug("{} RESPONSE (2): {}".format(
246 self
.__class
__.__name
__, response
))
249 network_list
= list()
250 network_dict
= dict()
252 if len(id_list
) == 0:
253 for net
in self
.api
.compute
.nets
.values():
254 tmp_network_dict
= net
.create_network_dict()
255 if tmp_network_dict
not in network_list
:
256 network_list
.append(tmp_network_dict
)
258 for net
in self
.api
.compute
.nets
.values():
259 if net
.id in id_list
:
260 tmp_network_dict
= net
.create_network_dict()
261 if tmp_network_dict
not in network_list
:
262 network_list
.append(tmp_network_dict
)
264 network_dict
["networks"] = network_list
265 LOG
.debug("{} RESPONSE (3): {}".format(
266 self
.__class
__.__name
__, network_dict
))
267 return Response(json
.dumps(network_dict
),
268 status
=200, mimetype
='application/json')
270 except Exception as ex
:
271 LOG
.exception("Neutron: List networks exception.")
272 return Response(ex
.message
, status
=500,
273 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(
307 return Response(u
'Network not found.\n',
308 status
=404, mimetype
='application/json')
310 tmp_network_dict
= net
.create_network_dict()
313 tmp_dict
["networks"] = [tmp_network_dict
]
315 tmp_dict
["network"] = tmp_network_dict
317 return Response(json
.dumps(tmp_dict
), status
=200,
318 mimetype
='application/json')
320 except Exception as ex
:
321 logging
.exception("Neutron: Show network exception.")
322 return Response(ex
.message
, status
=500,
323 mimetype
='application/json')
326 class NeutronCreateNetwork(Resource
):
327 def __init__(self
, api
):
332 Creates a network with the name, specified within the request under ['network']['name'].
334 :return: * 400, if the network already exists.
335 * 500, if any exception occurred while creation.
336 * 201, if everything worked out.
337 :rtype: :class:`flask.response`
339 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
341 network_dict
= json
.loads(request
.data
)
342 name
= network_dict
['network']['name']
343 net
= self
.api
.compute
.find_network_by_name_or_id(name
)
345 return Response('Network already exists.\n',
346 status
=400, mimetype
='application/json')
348 net
= self
.api
.compute
.create_network(name
)
349 return Response(json
.dumps(
350 {"network": net
.create_network_dict()}), status
=201, mimetype
='application/json')
351 except Exception as ex
:
352 LOG
.exception("Neutron: Create network excepiton.")
353 return Response(ex
.message
, status
=500,
354 mimetype
='application/json')
357 class NeutronUpdateNetwork(Resource
):
358 def __init__(self
, api
):
361 def put(self
, network_id
): # TODO currently only the name will be changed
363 Updates the existing network with the given parameters.
365 :param network_id: The indicator string, which specifies the requested network.
366 :type network_id: ``str``
367 :return: * 404, if the network could not be found.
368 * 500, if any exception occurred while updating the network.
369 * 200, if everything worked out.
370 :rtype: :class:`flask.response`
372 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
374 if network_id
in self
.api
.compute
.nets
:
375 net
= self
.api
.compute
.nets
[network_id
]
376 network_dict
= json
.loads(request
.data
)
378 if "status" in network_dict
["network"]:
379 net
.status
= network_dict
["network"]["status"]
380 if "subnets" in network_dict
["network"]:
381 pass # tmp_network_dict["subnets"] = None
382 if "name" in network_dict
["network"] and net
.name
!= network_dict
["network"]["name"]:
383 net
.name
= network_dict
["network"]["name"]
384 if "admin_state_up" in network_dict
["network"]:
385 pass # tmp_network_dict["admin_state_up"] = True
386 if "tenant_id" in network_dict
["network"]:
387 # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
389 if "shared" in network_dict
["network"]:
390 pass # tmp_network_dict["shared"] = False
392 return Response(json
.dumps(network_dict
),
393 status
=200, mimetype
='application/json')
395 return Response('Network not found.\n', status
=404,
396 mimetype
='application/json')
398 except Exception as ex
:
399 LOG
.exception("Neutron: Show networks exception.")
400 return Response(ex
.message
, status
=500,
401 mimetype
='application/json')
404 class NeutronDeleteNetwork(Resource
):
405 def __init__(self
, api
):
408 def delete(self
, network_id
):
410 Deletes the specified network and all its subnets.
412 :param network_id: The indicator string, which specifies the requested network.
413 :type network_id: ``str``
414 :return: * 404, if the network or the subnet could not be removed.
415 * 500, if any exception occurred while deletion.
416 * 204, if everything worked out.
417 :rtype: :class:`flask.response`
419 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
421 if network_id
not in self
.api
.compute
.nets
:
422 return Response('Could not find network. (' + network_id
+ ')\n',
423 status
=404, mimetype
='application/json')
425 net
= self
.api
.compute
.nets
[network_id
]
426 delete_subnet
= NeutronDeleteSubnet(self
.api
)
427 resp
= delete_subnet
.delete(net
.subnet_id
)
429 if '204' not in resp
.status
and '404' not in resp
.status
:
432 self
.api
.compute
.delete_network(network_id
)
434 return Response('', status
=204, mimetype
='application/json')
435 except Exception as ex
:
436 LOG
.exception("Neutron: Delete network exception.")
437 return Response(ex
.message
, status
=500,
438 mimetype
='application/json')
441 class NeutronListSubnets(Resource
):
442 def __init__(self
, api
):
447 Lists all subnets, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
448 subnet with the name, or the subnets specified via id.
450 :return: Returns a json response, starting with 'subnets' as root node.
451 :rtype: :class:`flask.response`
453 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
455 if request
.args
.get('name'):
456 show_subnet
= NeutronShowSubnet(self
.api
)
457 return show_subnet
.get_subnet(request
.args
.get('name'), True)
458 id_list
= request
.args
.getlist('id')
459 if len(id_list
) == 1:
460 show_subnet
= NeutronShowSubnet(self
.api
)
461 return show_subnet
.get_subnet(id_list
[0], True)
466 if len(id_list
) == 0:
467 for net
in self
.api
.compute
.nets
.values():
468 if net
.subnet_id
is not None:
469 tmp_subnet_dict
= net
.create_subnet_dict()
470 subnet_list
.append(tmp_subnet_dict
)
472 for net
in self
.api
.compute
.nets
.values():
473 if net
.subnet_id
in id_list
:
474 tmp_subnet_dict
= net
.create_subnet_dict()
475 subnet_list
.append(tmp_subnet_dict
)
477 subnet_dict
["subnets"] = subnet_list
479 return Response(json
.dumps(subnet_dict
), status
=200,
480 mimetype
='application/json')
482 except Exception as ex
:
483 LOG
.exception("Neutron: List subnets exception.")
484 return Response(ex
.message
, status
=500,
485 mimetype
='application/json')
488 class NeutronShowSubnet(Resource
):
489 def __init__(self
, api
):
492 def get(self
, subnet_id
):
494 Returns the subnet, specified via 'subnet_id'.
496 :param subnet_id: The unique ID string of the subnet.
497 :type subnet_id: ``str``
498 :return: Returns a json response, starting with 'subnet' as root node and one subnet description.
499 :rtype: :class:`flask.response`
501 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
502 return self
.get_subnet(subnet_id
, False)
504 def get_subnet(self
, subnet_name_or_id
, as_list
):
506 Returns one subnet description of the subnet, specified via 'subnet_name_or_id'.
508 :param subnet_name_or_id: The indicator string, which specifies the requested subnet.
509 :type subnet_name_or_id: ``str``
510 :param as_list: Determines if the subnet description should start with the root node 'subnet' or 'subnets'.
511 :type as_list: ``bool``
512 :return: Returns a json response, with one subnet description.
513 :rtype: :class:`flask.response`
516 for net
in self
.api
.compute
.nets
.values():
517 if net
.subnet_id
== subnet_name_or_id
or net
.subnet_name
== subnet_name_or_id
:
518 tmp_subnet_dict
= net
.create_subnet_dict()
521 tmp_dict
["subnets"] = [tmp_subnet_dict
]
523 tmp_dict
["subnet"] = tmp_subnet_dict
524 return Response(json
.dumps(tmp_dict
),
525 status
=200, mimetype
='application/json')
527 return Response('Subnet not found. (' + subnet_name_or_id
+
528 ')\n', status
=404, mimetype
='application/json')
530 except Exception as ex
:
531 LOG
.exception("Neutron: Show subnet exception.")
532 return Response(ex
.message
, status
=500,
533 mimetype
='application/json')
536 class NeutronCreateSubnet(Resource
):
537 def __init__(self
, api
):
542 Creates a subnet with the name, specified within the request under ['subnet']['name'].
544 :return: * 400, if the 'CIDR' format is wrong or it does not exist.
545 * 404, if the network was not found.
546 * 409, if the corresponding network already has one subnet.
547 * 500, if any exception occurred while creation and
548 * 201, if everything worked out.
549 :rtype: :class:`flask.response`
551 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
553 subnet_dict
= json
.loads(request
.data
)
554 net
= self
.api
.compute
.find_network_by_name_or_id(
555 subnet_dict
['subnet']['network_id'])
558 return Response('Could not find network.\n',
559 status
=404, mimetype
='application/json')
561 net
.subnet_name
= subnet_dict
["subnet"].get(
562 'name', str(net
.name
) + '-sub')
563 if net
.subnet_id
is not None:
565 "Only one subnet per network is supported: {}".format(net
.subnet_id
))
566 return Response('Only one subnet per network is supported\n',
567 status
=409, mimetype
='application/json')
569 if "id" in subnet_dict
["subnet"]:
570 net
.subnet_id
= subnet_dict
["subnet"]["id"]
572 net
.subnet_id
= str(uuid
.uuid4())
573 import emuvim
.api
.openstack
.ip_handler
as IP
574 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
576 if "tenant_id" in subnet_dict
["subnet"]:
578 if "allocation_pools" in subnet_dict
["subnet"]:
580 if "gateway_ip" in subnet_dict
["subnet"]:
581 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
582 if "ip_version" in subnet_dict
["subnet"]:
584 if "enable_dhcp" in subnet_dict
["subnet"]:
587 return Response(json
.dumps(
588 {'subnet': net
.create_subnet_dict()}), status
=201, mimetype
='application/json')
590 except Exception as ex
:
591 LOG
.exception("Neutron: Create network excepiton.")
592 return Response(ex
.message
, status
=500,
593 mimetype
='application/json')
596 class NeutronUpdateSubnet(Resource
):
597 def __init__(self
, api
):
600 def put(self
, subnet_id
):
602 Updates the existing subnet with the given parameters.
604 :param subnet_id: The indicator string, which specifies the requested subnet.
605 :type subnet_id: ``str``
606 :return: * 404, if the network could not be found.
607 * 500, if any exception occurred while updating the network.
608 * 200, if everything worked out.
609 :rtype: :class:`flask.response`
611 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
613 for net
in self
.api
.compute
.nets
.values():
614 if net
.subnet_id
== subnet_id
:
615 subnet_dict
= json
.loads(request
.data
)
617 if "name" in subnet_dict
["subnet"]:
618 net
.subnet_name
= subnet_dict
["subnet"]["name"]
619 if "network_id" in subnet_dict
["subnet"]:
620 net
.id = subnet_dict
["subnet"]["network_id"]
621 if "tenant_id" in subnet_dict
["subnet"]:
623 if "allocation_pools" in subnet_dict
["subnet"]:
625 if "gateway_ip" in subnet_dict
["subnet"]:
626 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
627 if "ip_version" in subnet_dict
["subnet"]:
629 if "cidr" in subnet_dict
["subnet"]:
630 net
.set_cidr(subnet_dict
["subnet"]["cidr"])
631 if "id" in subnet_dict
["subnet"]:
632 net
.subnet_id
= subnet_dict
["subnet"]["id"]
633 if "enable_dhcp" in subnet_dict
["subnet"]:
636 net
.subnet_update_time
= str(datetime
.now())
637 tmp_dict
= {'subnet': net
.create_subnet_dict()}
638 return Response(json
.dumps(tmp_dict
),
639 status
=200, mimetype
='application/json')
641 return Response('Network not found.\n', status
=404,
642 mimetype
='application/json')
644 except Exception as ex
:
645 LOG
.exception("Neutron: Show networks exception.")
646 return Response(ex
.message
, status
=500,
647 mimetype
='application/json')
650 class NeutronDeleteSubnet(Resource
):
651 def __init__(self
, api
):
654 def delete(self
, subnet_id
):
656 Deletes the specified subnet.
658 :param subnet_id: The indicator string, which specifies the requested subnet.
659 :type subnet_id: ``str``
660 :return: * 404, if the subnet could not be removed.
661 * 500, if any exception occurred while deletion.
662 * 204, if everything worked out.
663 :rtype: :class:`flask.response`
665 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
667 for net
in self
.api
.compute
.nets
.values():
668 if net
.subnet_id
== subnet_id
:
669 for server
in self
.api
.compute
.computeUnits
.values():
670 for port_name
in server
.port_names
:
671 port
= self
.api
.compute
.find_port_by_name_or_id(
675 "Port search for {} returned None.".format(port_name
))
677 if port
.net_name
== net
.name
:
678 port
.ip_address
= None
679 self
.api
.compute
.dc
.net
.removeLink(
681 node1
=self
.api
.compute
.dc
.containers
[server
.name
],
682 node2
=self
.api
.compute
.dc
.switch
)
688 '', status
=204, mimetype
='application/json')
690 return Response('Could not find subnet.',
691 status
=404, mimetype
='application/json')
692 except Exception as ex
:
693 LOG
.exception("Neutron: Delete subnet exception.")
694 return Response(ex
.message
, status
=500,
695 mimetype
='application/json')
698 class NeutronListPorts(Resource
):
699 def __init__(self
, api
):
704 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
705 port with the name, or the ports specified via id.
707 :return: Returns a json response, starting with 'ports' as root node.
708 :rtype: :class:`flask.response`
710 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
712 if request
.args
.get('name'):
713 show_port
= NeutronShowPort(self
.api
)
714 return show_port
.get_port(request
.args
.get('name'), True)
715 id_list
= request
.args
.getlist('id')
716 if len(id_list
) == 1:
717 show_port
= NeutronShowPort(self
.api
)
718 return show_port
.get_port(request
.args
.get('id'), True)
720 ports
= self
.api
.compute
.ports
.values()
721 if len(id_list
) != 0:
722 ports
= filter(lambda port
: port
.id in id_list
, ports
)
724 device_id
= request
.args
.get('device_id')
726 server
= self
.api
.compute
.find_server_by_name_or_id(device_id
)
728 raise RuntimeError("Unable to find server '%s' in order to return it's ports" % server
)
730 ports
= filter(lambda port
: (
733 lambda server_port_name_or_id
: (
734 port
.id == server_port_name_or_id
or port
.name
== server_port_name_or_id
742 port_dict
["ports"] = map(lambda x
: x
.create_port_dict(self
.api
.compute
), ports
)
744 return Response(json
.dumps(port_dict
), status
=200,
745 mimetype
='application/json')
747 except Exception as ex
:
748 LOG
.exception("Neutron: List ports exception.")
749 return Response(ex
.message
, status
=500,
750 mimetype
='application/json')
753 class NeutronShowPort(Resource
):
754 def __init__(self
, api
):
757 def get(self
, port_id
):
759 Returns the port, specified via 'port_id'.
761 :param port_id: The unique ID string of the network.
762 :type port_id: ``str``
763 :return: Returns a json response, starting with 'port' as root node and one network description.
764 :rtype: :class:`flask.response`
766 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
767 return self
.get_port(port_id
, False)
769 def get_port(self
, port_name_or_id
, as_list
):
771 Returns one network description of the port, specified via 'port_name_or_id'.
773 :param port_name_or_id: The indicator string, which specifies the requested port.
774 :type port_name_or_id: ``str``
775 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
776 :type as_list: ``bool``
777 :return: Returns a json response, with one port description.
778 :rtype: :class:`flask.response`
781 port
= self
.api
.compute
.find_port_by_name_or_id(port_name_or_id
)
783 return Response('Port not found. (' + port_name_or_id
+ ')\n',
784 status
=404, mimetype
='application/json')
785 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
788 tmp_dict
["ports"] = [tmp_port_dict
]
790 tmp_dict
["port"] = tmp_port_dict
791 return Response(json
.dumps(tmp_dict
), status
=200,
792 mimetype
='application/json')
793 except Exception as ex
:
794 LOG
.exception("Neutron: Show port exception.")
795 return Response(ex
.message
, status
=500,
796 mimetype
='application/json')
799 class NeutronCreatePort(Resource
):
800 def __init__(self
, api
):
805 Creates a port with the name, specified within the request under ['port']['name'].
807 :return: * 404, if the network could not be found.
808 * 500, if any exception occurred while creation and
809 * 201, if everything worked out.
810 :rtype: :class:`flask.response`
812 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
814 port_dict
= json
.loads(request
.data
)
815 net_id
= port_dict
['port']['network_id']
817 if net_id
not in self
.api
.compute
.nets
:
818 return Response('Could not find network.\n',
819 status
=404, mimetype
='application/json')
821 net
= self
.api
.compute
.nets
[net_id
]
822 if 'name' in port_dict
['port']:
823 name
= port_dict
['port']['name']
825 num_ports
= len(self
.api
.compute
.ports
)
826 name
= "port:cp%s:man:%s" % (num_ports
, str(uuid
.uuid4()))
828 port
= self
.api
.compute
.create_port(name
)
830 port
.net_name
= net
.name
831 port
.ip_address
= net
.get_new_ip_address(name
)
833 if "admin_state_up" in port_dict
["port"]:
835 if "device_id" in port_dict
["port"]:
837 if "device_owner" in port_dict
["port"]:
839 if "fixed_ips" in port_dict
["port"]:
841 if "mac_address" in port_dict
["port"]:
842 port
.mac_address
= port_dict
["port"]["mac_address"]
843 if "status" in port_dict
["port"]:
845 if "tenant_id" in port_dict
["port"]:
848 # add the port to a stack if the specified network is a stack
850 for stack
in self
.api
.compute
.stacks
.values():
851 for net
in stack
.nets
.values():
853 stack
.ports
[name
] = port
855 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=201,
856 mimetype
='application/json')
857 except Exception as ex
:
858 LOG
.exception("Neutron: Show port exception.")
859 return Response(ex
.message
, status
=500,
860 mimetype
='application/json')
863 class NeutronUpdatePort(Resource
):
864 def __init__(self
, api
):
867 def put(self
, port_id
):
869 Updates the existing port with the given parameters.
871 :param network_id: The indicator string, which specifies the requested port.
872 :type network_id: ``str``
873 :return: * 404, if the network could not be found.
874 * 500, if any exception occurred while updating the network.
875 * 200, if everything worked out.
876 :rtype: :class:`flask.response`
878 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
880 port_dict
= json
.loads(request
.data
)
881 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
883 return Response("Port with id %s does not exists.\n" %
884 port_id
, status
=404, mimetype
='application/json')
885 old_port
= copy
.copy(port
)
888 for s
in self
.api
.compute
.stacks
.values():
889 for port
in s
.ports
.values():
890 if port
.id == port_id
:
892 if "admin_state_up" in port_dict
["port"]:
894 if "device_id" in port_dict
["port"]:
896 if "device_owner" in port_dict
["port"]:
898 if "fixed_ips" in port_dict
["port"]:
900 if "id" in port_dict
["port"]:
901 port
.id = port_dict
["port"]["id"]
902 if "mac_address" in port_dict
["port"]:
903 port
.mac_address
= port_dict
["port"]["mac_address"]
904 if "name" in port_dict
["port"] and port_dict
["port"]["name"] != port
.name
:
905 port
.set_name(port_dict
["port"]["name"])
906 if stack
is not None:
907 if port
.net_name
in stack
.nets
:
908 stack
.nets
[port
.net_name
].update_port_name_for_ip_address(
909 port
.ip_address
, port
.name
)
910 stack
.ports
[port
.name
] = stack
.ports
[old_port
.name
]
911 del stack
.ports
[old_port
.name
]
912 if "network_id" in port_dict
["port"]:
914 if "status" in port_dict
["port"]:
916 if "tenant_id" in port_dict
["port"]:
919 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=200,
920 mimetype
='application/json')
921 except Exception as ex
:
922 LOG
.exception("Neutron: Update port exception.")
923 return Response(ex
.message
, status
=500,
924 mimetype
='application/json')
927 class NeutronDeletePort(Resource
):
928 def __init__(self
, api
):
931 def delete(self
, port_id
):
933 Deletes the specified port.
935 :param port_id: The indicator string, which specifies the requested port.
936 :type port_id: ``str``
937 :return: * 404, if the port could not be found.
938 * 500, if any exception occurred while deletion.
939 * 204, if everything worked out.
940 :rtype: :class:`flask.response`
942 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
944 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
946 return Response("Port with id %s does not exists.\n" %
949 for s
in self
.api
.compute
.stacks
.values():
950 for p
in s
.ports
.values():
953 if stack
is not None:
954 if port
.net_name
in stack
.nets
:
955 stack
.nets
[port
.net_name
].withdraw_ip_address(
957 for server
in stack
.servers
.values():
959 server
.port_names
.remove(port
.name
)
964 self
.api
.compute
.delete_port(port
.id)
966 return Response('', status
=204, mimetype
='application/json')
968 except Exception as ex
:
969 LOG
.exception("Neutron: Delete port exception.")
970 return Response(ex
.message
, status
=500,
971 mimetype
='application/json')
974 class NeutronAddFloatingIp(Resource
):
975 def __init__(self
, api
):
980 Returns a Floating IP for a port.
982 Currently ports are not mapped to individual IPs, but the
983 (potentially shared) Docker external IP is returned.
985 port_id
= request
.args
.get("port_id")
987 message
= "Neutron: List API for FloatingIPs is not implemented"
988 LOG
.exception(message
)
989 return Response(message
, status
=500,
990 mimetype
='application/json')
991 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
992 ip
= port
.assigned_container
.dcinfo
["NetworkSettings"]["IPAddress"]
994 resp
["floatingips"] = [
995 {'floating_ip_address': ip
}
997 return Response(json
.dumps(resp
), status
=200,
998 mimetype
='application/json')
1002 Adds a floating IP to neutron.
1004 :return: Returns a floating network description.
1005 :rtype: :class:`flask.response`
1007 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
1009 # Fiddle with floating_network !
1010 req
= json
.loads(request
.data
)
1012 network_id
= req
["floatingip"]["floating_network_id"]
1013 net
= self
.api
.compute
.find_network_by_name_or_id(network_id
)
1014 if net
!= self
.api
.manage
.floating_network
:
1015 return Response("You have to specify the existing floating network\n",
1016 status
=400, mimetype
='application/json')
1018 port_id
= req
["floatingip"].get("port_id", None)
1019 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
1020 if port
is not None:
1021 if port
.net_name
!= self
.api
.manage
.floating_network
.name
:
1022 return Response("You have to specify a port in the floating network\n",
1023 status
=400, mimetype
='application/json')
1025 if port
.floating_ip
is not None:
1026 return Response("We allow only one floating ip per port\n",
1027 status
=400, mimetype
='application/json')
1029 num_ports
= len(self
.api
.compute
.ports
)
1030 name
= "port:cp%s:fl:%s" % (num_ports
, str(uuid
.uuid4()))
1031 port
= self
.api
.compute
.create_port(name
)
1032 port
.net_name
= net
.name
1033 port
.ip_address
= net
.get_new_ip_address(name
)
1035 port
.floating_ip
= port
.ip_address
1038 resp
= response
["floatingip"] = dict()
1040 resp
["floating_network_id"] = net
.id
1041 resp
["status"] = "ACTIVE"
1043 resp
["port_id"] = port
.id
1044 resp
["floating_ip_address"] = port
.floating_ip
1045 resp
["fixed_ip_address"] = port
.floating_ip
1047 return Response(json
.dumps(response
), status
=200,
1048 mimetype
='application/json')
1049 except Exception as ex
:
1050 LOG
.exception("Neutron: Create FloatingIP exception %s.", ex
)
1051 return Response(ex
.message
, status
=500,
1052 mimetype
='application/json')