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 \
30 from datetime
import datetime
31 import emuvim
.api
.openstack
.openstack_dummies
.neutron_sfc_dummy_api
as SFC
37 LOG
= logging
.getLogger("api.openstack.neutron")
40 class NeutronDummyApi(BaseOpenstackDummy
):
41 def __init__(self
, ip
, port
, compute
):
42 super(NeutronDummyApi
, self
).__init
__(ip
, port
)
43 self
.compute
= compute
45 # create default networks (OSM usually assumes to have these
47 self
.compute
.create_network("mgmt")
48 self
.compute
.create_network("mgmtnet")
50 self
.api
.add_resource(NeutronListAPIVersions
, "/")
51 self
.api
.add_resource(NeutronShowAPIv2Details
, "/v2.0")
52 self
.api
.add_resource(NeutronListNetworks
, "/v2.0/networks.json", "/v2.0/networks",
53 resource_class_kwargs
={'api': self
})
54 self
.api
.add_resource(NeutronShowNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
55 resource_class_kwargs
={'api': self
})
56 self
.api
.add_resource(NeutronCreateNetwork
, "/v2.0/networks.json", "/v2.0/networks",
57 resource_class_kwargs
={'api': self
})
58 self
.api
.add_resource(NeutronUpdateNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
59 resource_class_kwargs
={'api': self
})
60 self
.api
.add_resource(NeutronDeleteNetwork
, "/v2.0/networks/<network_id>.json", "/v2.0/networks/<network_id>",
61 resource_class_kwargs
={'api': self
})
62 self
.api
.add_resource(NeutronListSubnets
, "/v2.0/subnets.json", "/v2.0/subnets",
63 resource_class_kwargs
={'api': self
})
64 self
.api
.add_resource(NeutronShowSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
65 resource_class_kwargs
={'api': self
})
66 self
.api
.add_resource(NeutronCreateSubnet
, "/v2.0/subnets.json", "/v2.0/subnets",
67 resource_class_kwargs
={'api': self
})
68 self
.api
.add_resource(NeutronUpdateSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
69 resource_class_kwargs
={'api': self
})
70 self
.api
.add_resource(NeutronDeleteSubnet
, "/v2.0/subnets/<subnet_id>.json", "/v2.0/subnets/<subnet_id>",
71 resource_class_kwargs
={'api': self
})
72 self
.api
.add_resource(NeutronListPorts
, "/v2.0/ports.json", "/v2.0/ports",
73 resource_class_kwargs
={'api': self
})
74 self
.api
.add_resource(NeutronShowPort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
75 resource_class_kwargs
={'api': self
})
76 self
.api
.add_resource(NeutronCreatePort
, "/v2.0/ports.json", "/v2.0/ports",
77 resource_class_kwargs
={'api': self
})
78 self
.api
.add_resource(NeutronUpdatePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
79 resource_class_kwargs
={'api': self
})
80 self
.api
.add_resource(NeutronDeletePort
, "/v2.0/ports/<port_id>.json", "/v2.0/ports/<port_id>",
81 resource_class_kwargs
={'api': self
})
82 self
.api
.add_resource(NeutronAddFloatingIp
, "/v2.0/floatingips.json", "/v2.0/floatingips",
83 resource_class_kwargs
={'api': self
})
85 # Service Function Chaining (SFC) API
86 self
.api
.add_resource(SFC
.PortPairsCreate
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
87 resource_class_kwargs
={'api': self
})
88 self
.api
.add_resource(SFC
.PortPairsUpdate
, "/v2.0/sfc/port_pairs/<pair_id>.json",
89 "/v2.0/sfc/port_pairs/<pair_id>",
90 resource_class_kwargs
={'api': self
})
91 self
.api
.add_resource(SFC
.PortPairsDelete
, "/v2.0/sfc/port_pairs/<pair_id>.json",
92 "/v2.0/sfc/port_pairs/<pair_id>",
93 resource_class_kwargs
={'api': self
})
94 self
.api
.add_resource(SFC
.PortPairsList
, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
95 resource_class_kwargs
={'api': self
})
96 self
.api
.add_resource(SFC
.PortPairsShow
, "/v2.0/sfc/port_pairs/<pair_id>.json",
97 "/v2.0/sfc/port_pairs/<pair_id>",
98 resource_class_kwargs
={'api': self
})
100 self
.api
.add_resource(SFC
.PortPairGroupCreate
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
101 resource_class_kwargs
={'api': self
})
102 self
.api
.add_resource(SFC
.PortPairGroupUpdate
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
103 "/v2.0/sfc/port_pair_groups/<group_id>",
104 resource_class_kwargs
={'api': self
})
105 self
.api
.add_resource(SFC
.PortPairGroupDelete
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
106 "/v2.0/sfc/port_pair_groups/<group_id>",
107 resource_class_kwargs
={'api': self
})
108 self
.api
.add_resource(SFC
.PortPairGroupList
, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
109 resource_class_kwargs
={'api': self
})
110 self
.api
.add_resource(SFC
.PortPairGroupShow
, "/v2.0/sfc/port_pair_groups/<group_id>.json",
111 "/v2.0/sfc/port_pair_groups/<group_id>",
112 resource_class_kwargs
={'api': self
})
114 self
.api
.add_resource(SFC
.FlowClassifierCreate
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
115 resource_class_kwargs
={'api': self
})
116 self
.api
.add_resource(SFC
.FlowClassifierUpdate
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
117 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
118 resource_class_kwargs
={'api': self
})
119 self
.api
.add_resource(SFC
.FlowClassifierDelete
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
120 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
121 resource_class_kwargs
={'api': self
})
122 self
.api
.add_resource(SFC
.FlowClassifierList
, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
123 resource_class_kwargs
={'api': self
})
124 self
.api
.add_resource(SFC
.FlowClassifierShow
, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
125 "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
126 resource_class_kwargs
={'api': self
})
128 self
.api
.add_resource(SFC
.PortChainCreate
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
129 resource_class_kwargs
={'api': self
})
130 self
.api
.add_resource(SFC
.PortChainUpdate
, "/v2.0/sfc/port_chains/<chain_id>.json",
131 "/v2.0/sfc/port_chains/<chain_id>",
132 resource_class_kwargs
={'api': self
})
133 self
.api
.add_resource(SFC
.PortChainDelete
, "/v2.0/sfc/port_chains/<chain_id>.json",
134 "/v2.0/sfc/port_chains/<chain_id>",
135 resource_class_kwargs
={'api': self
})
136 self
.api
.add_resource(SFC
.PortChainList
, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
137 resource_class_kwargs
={'api': self
})
138 self
.api
.add_resource(SFC
.PortChainShow
, "/v2.0/sfc/port_chains/<chain_id>.json",
139 "/v2.0/sfc/port_chains/<chain_id>",
140 resource_class_kwargs
={'api': self
})
143 class NeutronListAPIVersions(Resource
):
148 :return: Returns a json with API versions.
149 :rtype: :class:`flask.response`
151 LOG
.debug("API CALL: Neutron - List API Versions")
153 resp
['versions'] = dict()
160 "href": request
.url_root
+ '/v2.0',
165 resp
['versions'] = versions
167 return Response(json
.dumps(resp
), status
=200,
168 mimetype
='application/json')
171 class NeutronShowAPIv2Details(Resource
):
176 :return: Returns a json with API details.
177 :rtype: :class:`flask.response`
179 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
182 resp
['resources'] = dict()
183 resp
['resources'] = [{
186 "href": request
.url_root
+ 'v2.0/subnets',
191 "collection": "subnets"
196 "href": request
.url_root
+ 'v2.0/networks',
201 "collection": "networks"
206 "href": request
.url_root
+ 'v2.0/ports',
211 "collection": "ports"
215 return Response(json
.dumps(resp
), status
=200,
216 mimetype
='application/json')
219 class NeutronListNetworks(Resource
):
220 def __init__(self
, api
):
225 Lists all networks, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
226 network with the name, or the networks specified via id.
228 :return: Returns a json response, starting with 'networks' as root node.
229 :rtype: :class:`flask.response`
231 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
232 # LOG.debug("ARGS: {}".format(request.args))
234 if request
.args
.get('name'):
235 tmp_network
= NeutronShowNetwork(self
.api
)
236 response
= tmp_network
.get_network(
237 request
.args
.get('name'), True)
238 LOG
.debug("{} RESPONSE (1): {}".format(
239 self
.__class
__.__name
__, response
))
241 id_list
= request
.args
.getlist('id')
242 if len(id_list
) == 1:
243 tmp_network
= NeutronShowNetwork(self
.api
)
244 response
= tmp_network
.get_network(
245 request
.args
.get('id'), True)
246 LOG
.debug("{} RESPONSE (2): {}".format(
247 self
.__class
__.__name
__, response
))
250 network_list
= list()
251 network_dict
= dict()
253 if len(id_list
) == 0:
254 for net
in self
.api
.compute
.nets
.values():
255 tmp_network_dict
= net
.create_network_dict()
256 if tmp_network_dict
not in network_list
:
257 network_list
.append(tmp_network_dict
)
259 for net
in self
.api
.compute
.nets
.values():
260 if net
.id in id_list
:
261 tmp_network_dict
= net
.create_network_dict()
262 if tmp_network_dict
not in network_list
:
263 network_list
.append(tmp_network_dict
)
265 network_dict
["networks"] = network_list
266 LOG
.debug("{} RESPONSE (3): {}".format(
267 self
.__class
__.__name
__, network_dict
))
268 return Response(json
.dumps(network_dict
),
269 status
=200, mimetype
='application/json')
271 except Exception as ex
:
272 LOG
.exception("Neutron: List networks exception.")
273 return Response(str(ex
), status
=500,
274 mimetype
='application/json')
277 class NeutronShowNetwork(Resource
):
278 def __init__(self
, api
):
281 def get(self
, network_id
):
283 Returns the network, specified via 'network_id'.
285 :param network_id: The unique ID string of the network.
286 :type network_id: ``str``
287 :return: Returns a json response, starting with 'network' as root node and one network description.
288 :rtype: :class:`flask.response`
290 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
291 return self
.get_network(network_id
, False)
293 def get_network(self
, network_name_or_id
, as_list
):
295 Returns one network description of the network, specified via 'network_name_or_id'.
297 :param network_name_or_id: The indicator string, which specifies the requested network.
298 :type network_name_or_id: ``str``
299 :param as_list: Determines if the network description should start with the root node 'network' or 'networks'.
300 :type as_list: ``bool``
301 :return: Returns a json response, with one network description.
302 :rtype: :class:`flask.response`
305 net
= self
.api
.compute
.find_network_by_name_or_id(
308 return Response(u
'Network not found.\n',
309 status
=404, mimetype
='application/json')
311 tmp_network_dict
= net
.create_network_dict()
314 tmp_dict
["networks"] = [tmp_network_dict
]
316 tmp_dict
["network"] = tmp_network_dict
318 return Response(json
.dumps(tmp_dict
), status
=200,
319 mimetype
='application/json')
321 except Exception as ex
:
322 logging
.exception("Neutron: Show network exception.")
323 return Response(str(ex
), status
=500,
324 mimetype
='application/json')
327 class NeutronCreateNetwork(Resource
):
328 def __init__(self
, api
):
333 Creates a network with the name, specified within the request under ['network']['name'].
335 :return: * 400, if the network already exists.
336 * 500, if any exception occurred while creation.
337 * 201, if everything worked out.
338 :rtype: :class:`flask.response`
340 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
342 network_dict
= json
.loads(request
.data
)
343 name
= network_dict
['network']['name']
344 net
= self
.api
.compute
.find_network_by_name_or_id(name
)
346 return Response('Network already exists.\n',
347 status
=400, mimetype
='application/json')
349 net
= self
.api
.compute
.create_network(name
)
350 return Response(json
.dumps(
351 {"network": net
.create_network_dict()}), status
=201, mimetype
='application/json')
352 except Exception as ex
:
353 LOG
.exception("Neutron: Create network excepiton.")
354 return Response(str(ex
), status
=500,
355 mimetype
='application/json')
358 class NeutronUpdateNetwork(Resource
):
359 def __init__(self
, api
):
362 def put(self
, network_id
): # TODO currently only the name will be changed
364 Updates the existing network with the given parameters.
366 :param network_id: The indicator string, which specifies the requested network.
367 :type network_id: ``str``
368 :return: * 404, if the network could not be found.
369 * 500, if any exception occurred while updating the network.
370 * 200, if everything worked out.
371 :rtype: :class:`flask.response`
373 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
375 if network_id
in self
.api
.compute
.nets
:
376 net
= self
.api
.compute
.nets
[network_id
]
377 network_dict
= json
.loads(request
.data
)
379 if "status" in network_dict
["network"]:
380 net
.status
= network_dict
["network"]["status"]
381 if "subnets" in network_dict
["network"]:
382 pass # tmp_network_dict["subnets"] = None
383 if "name" in network_dict
["network"] and net
.name
!= network_dict
["network"]["name"]:
384 net
.name
= network_dict
["network"]["name"]
385 if "admin_state_up" in network_dict
["network"]:
386 pass # tmp_network_dict["admin_state_up"] = True
387 if "tenant_id" in network_dict
["network"]:
388 # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
390 if "shared" in network_dict
["network"]:
391 pass # tmp_network_dict["shared"] = False
393 return Response(json
.dumps(network_dict
),
394 status
=200, mimetype
='application/json')
396 return Response('Network not found.\n', status
=404,
397 mimetype
='application/json')
399 except Exception as ex
:
400 LOG
.exception("Neutron: Show networks exception.")
401 return Response(str(ex
), status
=500,
402 mimetype
='application/json')
405 class NeutronDeleteNetwork(Resource
):
406 def __init__(self
, api
):
409 def delete(self
, network_id
):
411 Deletes the specified network and all its subnets.
413 :param network_id: The indicator string, which specifies the requested network.
414 :type network_id: ``str``
415 :return: * 404, if the network or the subnet could not be removed.
416 * 500, if any exception occurred while deletion.
417 * 204, if everything worked out.
418 :rtype: :class:`flask.response`
420 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
422 if network_id
not in self
.api
.compute
.nets
:
423 return Response('Could not find network. (' + network_id
+ ')\n',
424 status
=404, mimetype
='application/json')
426 net
= self
.api
.compute
.nets
[network_id
]
427 delete_subnet
= NeutronDeleteSubnet(self
.api
)
428 resp
= delete_subnet
.delete(net
.subnet_id
)
430 if '204' not in resp
.status
and '404' not in resp
.status
:
433 self
.api
.compute
.delete_network(network_id
)
435 return Response('', status
=204, mimetype
='application/json')
436 except Exception as ex
:
437 LOG
.exception("Neutron: Delete network exception.")
438 return Response(str(ex
), status
=500,
439 mimetype
='application/json')
442 class NeutronListSubnets(Resource
):
443 def __init__(self
, api
):
448 Lists all subnets, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
449 subnet with the name, or the subnets specified via id.
451 :return: Returns a json response, starting with 'subnets' as root node.
452 :rtype: :class:`flask.response`
454 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
456 if request
.args
.get('name'):
457 show_subnet
= NeutronShowSubnet(self
.api
)
458 return show_subnet
.get_subnet(request
.args
.get('name'), True)
459 id_list
= request
.args
.getlist('id')
460 if len(id_list
) == 1:
461 show_subnet
= NeutronShowSubnet(self
.api
)
462 return show_subnet
.get_subnet(id_list
[0], True)
467 if len(id_list
) == 0:
468 for net
in self
.api
.compute
.nets
.values():
469 if net
.subnet_id
is not None:
470 tmp_subnet_dict
= net
.create_subnet_dict()
471 subnet_list
.append(tmp_subnet_dict
)
473 for net
in self
.api
.compute
.nets
.values():
474 if net
.subnet_id
in id_list
:
475 tmp_subnet_dict
= net
.create_subnet_dict()
476 subnet_list
.append(tmp_subnet_dict
)
478 subnet_dict
["subnets"] = subnet_list
480 return Response(json
.dumps(subnet_dict
), status
=200,
481 mimetype
='application/json')
483 except Exception as ex
:
484 LOG
.exception("Neutron: List subnets exception.")
485 return Response(str(ex
), status
=500,
486 mimetype
='application/json')
489 class NeutronShowSubnet(Resource
):
490 def __init__(self
, api
):
493 def get(self
, subnet_id
):
495 Returns the subnet, specified via 'subnet_id'.
497 :param subnet_id: The unique ID string of the subnet.
498 :type subnet_id: ``str``
499 :return: Returns a json response, starting with 'subnet' as root node and one subnet description.
500 :rtype: :class:`flask.response`
502 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
503 return self
.get_subnet(subnet_id
, False)
505 def get_subnet(self
, subnet_name_or_id
, as_list
):
507 Returns one subnet description of the subnet, specified via 'subnet_name_or_id'.
509 :param subnet_name_or_id: The indicator string, which specifies the requested subnet.
510 :type subnet_name_or_id: ``str``
511 :param as_list: Determines if the subnet description should start with the root node 'subnet' or 'subnets'.
512 :type as_list: ``bool``
513 :return: Returns a json response, with one subnet description.
514 :rtype: :class:`flask.response`
517 for net
in self
.api
.compute
.nets
.values():
518 if net
.subnet_id
== subnet_name_or_id
or net
.subnet_name
== subnet_name_or_id
:
519 tmp_subnet_dict
= net
.create_subnet_dict()
522 tmp_dict
["subnets"] = [tmp_subnet_dict
]
524 tmp_dict
["subnet"] = tmp_subnet_dict
525 return Response(json
.dumps(tmp_dict
),
526 status
=200, mimetype
='application/json')
528 return Response('Subnet not found. (' + subnet_name_or_id
+
529 ')\n', status
=404, mimetype
='application/json')
531 except Exception as ex
:
532 LOG
.exception("Neutron: Show subnet exception.")
533 return Response(str(ex
), status
=500,
534 mimetype
='application/json')
537 class NeutronCreateSubnet(Resource
):
538 def __init__(self
, api
):
543 Creates a subnet with the name, specified within the request under ['subnet']['name'].
545 :return: * 400, if the 'CIDR' format is wrong or it does not exist.
546 * 404, if the network was not found.
547 * 409, if the corresponding network already has one subnet.
548 * 500, if any exception occurred while creation and
549 * 201, if everything worked out.
550 :rtype: :class:`flask.response`
552 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
554 subnet_dict
= json
.loads(request
.data
)
555 net
= self
.api
.compute
.find_network_by_name_or_id(
556 subnet_dict
['subnet']['network_id'])
559 return Response('Could not find network.\n',
560 status
=404, mimetype
='application/json')
562 net
.subnet_name
= subnet_dict
["subnet"].get(
563 'name', str(net
.name
) + '-sub')
564 if net
.subnet_id
is not None:
566 "Only one subnet per network is supported: {}".format(net
.subnet_id
))
567 return Response('Only one subnet per network is supported\n',
568 status
=409, mimetype
='application/json')
570 if "id" in subnet_dict
["subnet"]:
571 net
.subnet_id
= subnet_dict
["subnet"]["id"]
573 net
.subnet_id
= str(uuid
.uuid4())
574 import emuvim
.api
.openstack
.ip_handler
as IP
575 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
577 if "tenant_id" in subnet_dict
["subnet"]:
579 if "allocation_pools" in subnet_dict
["subnet"]:
581 if "gateway_ip" in subnet_dict
["subnet"]:
582 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
583 if "ip_version" in subnet_dict
["subnet"]:
585 if "enable_dhcp" in subnet_dict
["subnet"]:
588 return Response(json
.dumps(
589 {'subnet': net
.create_subnet_dict()}), status
=201, mimetype
='application/json')
591 except Exception as ex
:
592 LOG
.exception("Neutron: Create network excepiton.")
593 return Response(str(ex
), status
=500,
594 mimetype
='application/json')
597 class NeutronUpdateSubnet(Resource
):
598 def __init__(self
, api
):
601 def put(self
, subnet_id
):
603 Updates the existing subnet with the given parameters.
605 :param subnet_id: The indicator string, which specifies the requested subnet.
606 :type subnet_id: ``str``
607 :return: * 404, if the network could not be found.
608 * 500, if any exception occurred while updating the network.
609 * 200, if everything worked out.
610 :rtype: :class:`flask.response`
612 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
614 for net
in self
.api
.compute
.nets
.values():
615 if net
.subnet_id
== subnet_id
:
616 subnet_dict
= json
.loads(request
.data
)
618 if "name" in subnet_dict
["subnet"]:
619 net
.subnet_name
= subnet_dict
["subnet"]["name"]
620 if "network_id" in subnet_dict
["subnet"]:
621 net
.id = subnet_dict
["subnet"]["network_id"]
622 if "tenant_id" in subnet_dict
["subnet"]:
624 if "allocation_pools" in subnet_dict
["subnet"]:
626 if "gateway_ip" in subnet_dict
["subnet"]:
627 net
.gateway_ip
= subnet_dict
["subnet"]["gateway_ip"]
628 if "ip_version" in subnet_dict
["subnet"]:
630 if "cidr" in subnet_dict
["subnet"]:
631 net
.set_cidr(subnet_dict
["subnet"]["cidr"])
632 if "id" in subnet_dict
["subnet"]:
633 net
.subnet_id
= subnet_dict
["subnet"]["id"]
634 if "enable_dhcp" in subnet_dict
["subnet"]:
637 net
.subnet_update_time
= str(datetime
.now())
638 tmp_dict
= {'subnet': net
.create_subnet_dict()}
639 return Response(json
.dumps(tmp_dict
),
640 status
=200, mimetype
='application/json')
642 return Response('Network not found.\n', status
=404,
643 mimetype
='application/json')
645 except Exception as ex
:
646 LOG
.exception("Neutron: Show networks exception.")
647 return Response(str(ex
), status
=500,
648 mimetype
='application/json')
651 class NeutronDeleteSubnet(Resource
):
652 def __init__(self
, api
):
655 def delete(self
, subnet_id
):
657 Deletes the specified subnet.
659 :param subnet_id: The indicator string, which specifies the requested subnet.
660 :type subnet_id: ``str``
661 :return: * 404, if the subnet could not be removed.
662 * 500, if any exception occurred while deletion.
663 * 204, if everything worked out.
664 :rtype: :class:`flask.response`
666 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
668 for net
in self
.api
.compute
.nets
.values():
669 if net
.subnet_id
== subnet_id
:
670 for server
in self
.api
.compute
.computeUnits
.values():
671 for port_name
in server
.port_names
:
672 port
= self
.api
.compute
.find_port_by_name_or_id(
676 "Port search for {} returned None.".format(port_name
))
678 if port
.net_name
== net
.name
:
679 port
.ip_address
= None
680 self
.api
.compute
.dc
.net
.removeLink(
682 node1
=self
.api
.compute
.dc
.containers
[server
.name
],
683 node2
=self
.api
.compute
.dc
.switch
)
689 '', status
=204, mimetype
='application/json')
691 return Response('Could not find subnet.',
692 status
=404, mimetype
='application/json')
693 except Exception as ex
:
694 LOG
.exception("Neutron: Delete subnet exception.")
695 return Response(str(ex
), status
=500,
696 mimetype
='application/json')
699 class NeutronListPorts(Resource
):
700 def __init__(self
, api
):
705 Lists all ports, used in son-emu. If a 'name' or one or more 'id's are specified, it will only list the
706 port with the name, or the ports specified via id.
708 :return: Returns a json response, starting with 'ports' as root node.
709 :rtype: :class:`flask.response`
711 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
713 if request
.args
.get('name'):
714 show_port
= NeutronShowPort(self
.api
)
715 return show_port
.get_port(request
.args
.get('name'), True)
716 id_list
= request
.args
.getlist('id')
717 if len(id_list
) == 1:
718 show_port
= NeutronShowPort(self
.api
)
719 return show_port
.get_port(request
.args
.get('id'), True)
721 ports
= self
.api
.compute
.ports
.values()
722 if len(id_list
) != 0:
723 ports
= filter(lambda port
: port
.id in id_list
, ports
)
725 device_id
= request
.args
.get('device_id')
727 server
= self
.api
.compute
.find_server_by_name_or_id(device_id
)
729 raise RuntimeError("Unable to find server '%s' in order to return it's ports" % server
)
731 ports
= filter(lambda port
: (
734 lambda server_port_name_or_id
: (
735 port
.id == server_port_name_or_id
or port
.name
== server_port_name_or_id
743 port_dict
["ports"] = list(map(lambda x
: x
.create_port_dict(self
.api
.compute
), ports
))
745 return Response(json
.dumps(port_dict
), status
=200,
746 mimetype
='application/json')
748 except Exception as ex
:
749 LOG
.exception("Neutron: List ports exception.")
750 return Response(str(ex
), status
=500,
751 mimetype
='application/json')
754 class NeutronShowPort(Resource
):
755 def __init__(self
, api
):
758 def get(self
, port_id
):
760 Returns the port, specified via 'port_id'.
762 :param port_id: The unique ID string of the network.
763 :type port_id: ``str``
764 :return: Returns a json response, starting with 'port' as root node and one network description.
765 :rtype: :class:`flask.response`
767 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
768 return self
.get_port(port_id
, False)
770 def get_port(self
, port_name_or_id
, as_list
):
772 Returns one network description of the port, specified via 'port_name_or_id'.
774 :param port_name_or_id: The indicator string, which specifies the requested port.
775 :type port_name_or_id: ``str``
776 :param as_list: Determines if the port description should start with the root node 'port' or 'ports'.
777 :type as_list: ``bool``
778 :return: Returns a json response, with one port description.
779 :rtype: :class:`flask.response`
782 port
= self
.api
.compute
.find_port_by_name_or_id(port_name_or_id
)
784 return Response('Port not found. (' + port_name_or_id
+ ')\n',
785 status
=404, mimetype
='application/json')
786 tmp_port_dict
= port
.create_port_dict(self
.api
.compute
)
789 tmp_dict
["ports"] = [tmp_port_dict
]
791 tmp_dict
["port"] = tmp_port_dict
792 return Response(json
.dumps(tmp_dict
), status
=200,
793 mimetype
='application/json')
794 except Exception as ex
:
795 LOG
.exception("Neutron: Show port exception.")
796 return Response(str(ex
), status
=500,
797 mimetype
='application/json')
800 class NeutronCreatePort(Resource
):
801 def __init__(self
, api
):
806 Creates a port with the name, specified within the request under ['port']['name'].
808 :return: * 404, if the network could not be found.
809 * 500, if any exception occurred while creation and
810 * 201, if everything worked out.
811 :rtype: :class:`flask.response`
813 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
815 port_dict
= json
.loads(request
.data
)
816 net_id
= port_dict
['port']['network_id']
818 if net_id
not in self
.api
.compute
.nets
:
819 return Response('Could not find network.\n',
820 status
=404, mimetype
='application/json')
822 net
= self
.api
.compute
.nets
[net_id
]
823 if 'name' in port_dict
['port']:
824 name
= port_dict
['port']['name']
826 num_ports
= len(self
.api
.compute
.ports
)
827 name
= "port:cp%s:man:%s" % (num_ports
, str(uuid
.uuid4()))
829 port
= self
.api
.compute
.create_port(name
)
831 port
.net_name
= net
.name
832 port
.ip_address
= net
.get_new_ip_address(name
)
834 if "admin_state_up" in port_dict
["port"]:
836 if "device_id" in port_dict
["port"]:
838 if "device_owner" in port_dict
["port"]:
840 if "fixed_ips" in port_dict
["port"]:
842 if "mac_address" in port_dict
["port"]:
843 port
.mac_address
= port_dict
["port"]["mac_address"]
844 if "status" in port_dict
["port"]:
846 if "tenant_id" in port_dict
["port"]:
849 # add the port to a stack if the specified network is a stack
851 for stack
in self
.api
.compute
.stacks
.values():
852 for net
in stack
.nets
.values():
854 stack
.ports
[name
] = port
856 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=201,
857 mimetype
='application/json')
858 except Exception as ex
:
859 LOG
.exception("Neutron: Show port exception.")
860 return Response(str(ex
), status
=500,
861 mimetype
='application/json')
864 class NeutronUpdatePort(Resource
):
865 def __init__(self
, api
):
868 def put(self
, port_id
):
870 Updates the existing port with the given parameters.
872 :param network_id: The indicator string, which specifies the requested port.
873 :type network_id: ``str``
874 :return: * 404, if the network could not be found.
875 * 500, if any exception occurred while updating the network.
876 * 200, if everything worked out.
877 :rtype: :class:`flask.response`
879 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
881 port_dict
= json
.loads(request
.data
)
882 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
884 return Response("Port with id %s does not exists.\n" %
885 port_id
, status
=404, mimetype
='application/json')
886 old_port
= copy
.copy(port
)
889 for s
in self
.api
.compute
.stacks
.values():
890 for port
in s
.ports
.values():
891 if port
.id == port_id
:
893 if "admin_state_up" in port_dict
["port"]:
895 if "device_id" in port_dict
["port"]:
897 if "device_owner" in port_dict
["port"]:
899 if "fixed_ips" in port_dict
["port"]:
901 if "id" in port_dict
["port"]:
902 port
.id = port_dict
["port"]["id"]
903 if "mac_address" in port_dict
["port"]:
904 port
.mac_address
= port_dict
["port"]["mac_address"]
905 if "name" in port_dict
["port"] and port_dict
["port"]["name"] != port
.name
:
906 port
.set_name(port_dict
["port"]["name"])
907 if stack
is not None:
908 if port
.net_name
in stack
.nets
:
909 stack
.nets
[port
.net_name
].update_port_name_for_ip_address(
910 port
.ip_address
, port
.name
)
911 stack
.ports
[port
.name
] = stack
.ports
[old_port
.name
]
912 del stack
.ports
[old_port
.name
]
913 if "network_id" in port_dict
["port"]:
915 if "status" in port_dict
["port"]:
917 if "tenant_id" in port_dict
["port"]:
920 return Response(json
.dumps({'port': port
.create_port_dict(self
.api
.compute
)}), status
=200,
921 mimetype
='application/json')
922 except Exception as ex
:
923 LOG
.exception("Neutron: Update port exception.")
924 return Response(str(ex
), status
=500,
925 mimetype
='application/json')
928 class NeutronDeletePort(Resource
):
929 def __init__(self
, api
):
932 def delete(self
, port_id
):
934 Deletes the specified port.
936 :param port_id: The indicator string, which specifies the requested port.
937 :type port_id: ``str``
938 :return: * 404, if the port could not be found.
939 * 500, if any exception occurred while deletion.
940 * 204, if everything worked out.
941 :rtype: :class:`flask.response`
943 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
945 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
947 return Response("Port with id %s does not exists.\n" %
950 for s
in self
.api
.compute
.stacks
.values():
951 for p
in s
.ports
.values():
954 if stack
is not None:
955 if port
.net_name
in stack
.nets
:
956 stack
.nets
[port
.net_name
].withdraw_ip_address(
958 for server
in stack
.servers
.values():
960 server
.port_names
.remove(port
.name
)
965 self
.api
.compute
.delete_port(port
.id)
967 return Response('', status
=204, mimetype
='application/json')
969 except Exception as ex
:
970 LOG
.exception("Neutron: Delete port exception.")
971 return Response(str(ex
), status
=500,
972 mimetype
='application/json')
975 class NeutronAddFloatingIp(Resource
):
976 def __init__(self
, api
):
981 Returns a Floating IP for a port.
983 Currently ports are not mapped to individual IPs, but the
984 (potentially shared) Docker external IP is returned.
986 port_id
= request
.args
.get("port_id")
988 message
= "Neutron: List API for FloatingIPs is not implemented"
989 LOG
.exception(message
)
990 return Response(message
, status
=500,
991 mimetype
='application/json')
992 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
993 ip
= port
.assigned_container
.dcinfo
["NetworkSettings"]["IPAddress"]
995 resp
["floatingips"] = [
996 {'floating_ip_address': ip
}
998 return Response(json
.dumps(resp
), status
=200,
999 mimetype
='application/json')
1003 Adds a floating IP to neutron.
1005 :return: Returns a floating network description.
1006 :rtype: :class:`flask.response`
1008 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
1010 # Fiddle with floating_network !
1011 req
= json
.loads(request
.data
)
1013 network_id
= req
["floatingip"]["floating_network_id"]
1014 net
= self
.api
.compute
.find_network_by_name_or_id(network_id
)
1015 if net
!= self
.api
.manage
.floating_network
:
1016 return Response("You have to specify the existing floating network\n",
1017 status
=400, mimetype
='application/json')
1019 port_id
= req
["floatingip"].get("port_id", None)
1020 port
= self
.api
.compute
.find_port_by_name_or_id(port_id
)
1021 if port
is not None:
1022 if port
.net_name
!= self
.api
.manage
.floating_network
.name
:
1023 return Response("You have to specify a port in the floating network\n",
1024 status
=400, mimetype
='application/json')
1026 if port
.floating_ip
is not None:
1027 return Response("We allow only one floating ip per port\n",
1028 status
=400, mimetype
='application/json')
1030 num_ports
= len(self
.api
.compute
.ports
)
1031 name
= "port:cp%s:fl:%s" % (num_ports
, str(uuid
.uuid4()))
1032 port
= self
.api
.compute
.create_port(name
)
1033 port
.net_name
= net
.name
1034 port
.ip_address
= net
.get_new_ip_address(name
)
1036 port
.floating_ip
= port
.ip_address
1039 resp
= response
["floatingip"] = dict()
1041 resp
["floating_network_id"] = net
.id
1042 resp
["status"] = "ACTIVE"
1044 resp
["port_id"] = port
.id
1045 resp
["floating_ip_address"] = port
.floating_ip
1046 resp
["fixed_ip_address"] = port
.floating_ip
1048 return Response(json
.dumps(response
), status
=200,
1049 mimetype
='application/json')
1050 except Exception as ex
:
1051 LOG
.exception("Neutron: Create FloatingIP exception %s.", ex
)
1052 return Response(str(ex
), status
=500,
1053 mimetype
='application/json')