5 from mininet
.node
import OVSSwitch
7 from flask
import Flask
8 from flask
import Response
, request
9 from flask_restful
import Api
, Resource
10 from mininet
.link
import Link
14 class ChainApi(Resource
):
16 The chain API is a component that is not used in OpenStack.
17 It is a custom built REST API that can be used to create network chains and loadbalancers.
20 def __init__(self
, inc_ip
, inc_port
, manage
):
22 self
.app
= Flask(__name__
)
23 self
.api
= Api(self
.app
)
27 self
.playbook_file
= '/tmp/son-emu-requests.log'
28 self
.api
.add_resource(ChainVersionsList
, "/",
29 resource_class_kwargs
={'api': self
})
30 self
.api
.add_resource(ChainList
, "/v1/chain/list",
31 resource_class_kwargs
={'api': self
})
32 self
.api
.add_resource(ChainVnfInterfaces
, "/v1/chain/<src_vnf>/<src_intfs>/<dst_vnf>/<dst_intfs>",
33 resource_class_kwargs
={'api': self
})
34 self
.api
.add_resource(ChainVnfDcStackInterfaces
,
35 "/v1/chain/<src_dc>/<src_stack>/<src_vnf>/<src_intfs>/<dst_dc>/<dst_stack>/<dst_vnf>/<dst_intfs>",
36 resource_class_kwargs
={'api': self
})
37 self
.api
.add_resource(BalanceHostList
, "/v1/lb/list",
38 resource_class_kwargs
={'api': self
})
39 self
.api
.add_resource(BalanceHost
, "/v1/lb/<vnf_src_name>/<vnf_src_interface>",
40 resource_class_kwargs
={'api': self
})
41 self
.api
.add_resource(BalanceHostDcStack
, "/v1/lb/<src_dc>/<src_stack>/<vnf_src_name>/<vnf_src_interface>",
42 resource_class_kwargs
={'api': self
})
43 self
.api
.add_resource(QueryTopology
, "/v1/topo",
44 resource_class_kwargs
={'api': self
})
45 self
.api
.add_resource(Shutdown
, "/shutdown")
47 @self.app
.after_request
48 def add_access_control_header(response
):
49 response
.headers
['Access-Control-Allow-Origin'] = '*'
52 def _start_flask(self
):
53 logging
.info("Starting %s endpoint @ http://%s:%d" % ("ChainDummyApi", self
.ip
, self
.port
))
54 if self
.app
is not None:
55 self
.app
.before_request(self
.dump_playbook
)
56 self
.app
.run(self
.ip
, self
.port
, debug
=True, use_reloader
=False)
58 def dump_playbook(self
):
59 with self
.manage
.lock
:
60 with
open(self
.playbook_file
, 'a') as logfile
:
61 if len(request
.data
) > 0:
62 data
= "# CHAIN API\n"
63 data
+= "curl -X {type} -H \"Content-type: application/json\" -d '{data}' {url}".format(type=request
.method
,
66 logfile
.write(data
+ "\n")
69 class Shutdown(Resource
):
71 logging
.debug(("%s is beeing shut down") % (__name__
))
72 func
= request
.environ
.get('werkzeug.server.shutdown')
74 raise RuntimeError('Not running with the Werkzeug Server')
78 class ChainVersionsList(Resource
):
80 Entrypoint to find versions of the chain api.
83 def __init__(self
, api
):
88 :return: flask.Response containing the openstack like description of the chain api
90 # at least let it look like an open stack function
99 "href": "http://%s:%d/v1/",
106 "updated": "2013-07-23T11:33:21Z"
110 """ % (self
.api
.ip
, self
.api
.port
)
112 return Response(resp
, status
=200, mimetype
="application/json")
114 except Exception as ex
:
115 logging
.exception(u
"%s: Could not show list of versions." % __name__
)
116 return ex
.message
, 500
119 class ChainList(Resource
):
121 Will retrieve all chains including their paths.
124 def __init__(self
, api
):
129 :return: flask.Response containing all live chains
131 # at least let it look like an open stack function
133 resp
= {"chains": list()}
135 for chain
in self
.api
.manage
.full_chain_data
.values():
136 resp
["chains"].append(chain
)
138 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
140 except Exception as ex
:
141 logging
.exception(u
"%s: Could not list all network chains." % __name__
)
142 return ex
.message
, 500
145 class BalanceHostList(Resource
):
147 Will retrieve all loadbalance rules including their paths.
150 def __init__(self
, api
):
155 :return: flask.Response containing all live loadbalancer rules
157 # at least let it look like an open stack function
159 resp
= {"loadbalancers": list()}
161 for lb
in self
.api
.manage
.full_lb_data
.values():
162 resp
["loadbalancers"].append(lb
)
164 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
166 except Exception as ex
:
167 logging
.exception(u
"%s: Could not list all live loadbalancers." % __name__
)
168 return ex
.message
, 500
171 class ChainVnfInterfaces(Resource
):
173 Handles requests targeted at: "/v1/chain/<src_vnf>/<src_intfs>/<dst_vnf>/<dst_intfs>"
174 Requests are for tearing down or setting up a chain between two vnfs
177 def __init__(self
, api
):
180 def put(self
, src_vnf
, src_intfs
, dst_vnf
, dst_intfs
):
182 A put request to "/v1/chain/<src_vnf>/<src_intfs>/<dst_vnf>/<dst_intfs>"
183 will create a chain between two interfaces at the specified vnfs.
186 Does not allow a custom path. Uses ``.post``
187 Internally just makes a POST request with no POST data!
189 :param src_vnf: Name of the source VNF
190 :type src_vnf: ``str``
191 :param src_intfs: Name of the source VNF interface to chain on
192 :type src_intfs: ``str``
193 :param dst_vnf: Name of the destination VNF
194 :type dst_vnf: ``str``
195 :param dst_intfs: Name of the destination VNF interface to chain on
196 :type dst_intfs: ``str``
197 :return: flask.Response 200 if set up correctly else 500 also returns the cookie as dict {'cookie': value}
198 501 if one of the VNF / intfs does not exist
199 :rtype: :class:`flask.Response`
201 return self
.post(src_vnf
, src_intfs
, dst_vnf
, dst_intfs
)
203 def post(self
, src_vnf
, src_intfs
, dst_vnf
, dst_intfs
):
205 A post request to "/v1/chain/<src_vnf>/<src_intfs>/<dst_vnf>/<dst_intfs>"
206 will create a chain between two interfaces at the specified vnfs.
207 The POST data contains the path like this.
208 { "path": ["dc1.s1", "s1", "dc4.s1"]}
209 path specifies the destination vnf and interface and contains a list of switches
210 that the path traverses. The path may not contain single hop loops like:
212 This is a limitation of Ryu, as Ryu does not allow the `INPUT_PORT` action!
214 :param src_vnf: Name of the source VNF
215 :type src_vnf: ``str``
216 :param src_intfs: Name of the source VNF interface to chain on
217 :type src_intfs: ``str``
218 :param dst_vnf: Name of the destination VNF
219 :type dst_vnf: ``str``
220 :param dst_intfs: Name of the destination VNF interface to chain on
221 :type dst_intfs: ``str``
222 :return: flask.Response 200 if set up correctly else 500 also returns the cookie as dict {'cookie': value}
223 501 if one of the VNF / intfs does not exist
224 :rtype: :class:`flask.Response`
229 path
= request
.json
.get('path')
230 layer2
= request
.json
.get('layer2', True)
235 # check if both VNFs exist
236 if not self
.api
.manage
.check_vnf_intf_pair(src_vnf
, src_intfs
):
237 return Response(u
"VNF %s or intfs %s does not exist" % (src_vnf
, src_intfs
), status
=501,
238 mimetype
="application/json")
239 if not self
.api
.manage
.check_vnf_intf_pair(dst_vnf
, dst_intfs
):
240 return Response(u
"VNF %s or intfs %s does not exist" % (dst_vnf
, dst_intfs
), status
=501,
241 mimetype
="application/json")
243 cookie
= self
.api
.manage
.network_action_start(src_vnf
, dst_vnf
, vnf_src_interface
=src_intfs
,
244 vnf_dst_interface
=dst_intfs
, bidirectional
=True,
245 path
=path
, layer2
=layer2
)
246 resp
= {'cookie': cookie
}
247 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
249 except Exception as e
:
250 logging
.exception(u
"%s: Error setting up the chain.\n %s" % (__name__
, e
))
251 return Response(u
"Error setting up the chain", status
=500, mimetype
="application/json")
253 def delete(self
, src_vnf
, src_intfs
, dst_vnf
, dst_intfs
):
255 A DELETE request to "/v1/chain/<src_vnf>/<src_intfs>/<dst_vnf>/<dst_intfs>"
256 will delete a previously created chain.
258 :param src_vnf: Name of the source VNF
259 :type src_vnf: ``str``
260 :param src_intfs: Name of the source VNF interface to chain on
261 :type src_intfs: ``str``
262 :param dst_vnf: Name of the destination VNF
263 :type dst_vnf: ``str``
264 :param dst_intfs: Name of the destination VNF interface to chain on
265 :type dst_intfs: ``str``
266 :return: flask.Response 200 if set up correctly else 500\
267 also returns the cookie as dict {'cookie': value}
268 501 if one of the VNF / intfs does not exist
269 :rtype: :class:`flask.Response`
272 # check if both VNFs exist
273 # check if both VNFs exist
274 if not self
.api
.manage
.check_vnf_intf_pair(src_vnf
, src_intfs
):
275 return Response(u
"VNF %s or intfs %s does not exist" % (src_vnf
, src_intfs
), status
=501,
276 mimetype
="application/json")
277 if not self
.api
.manage
.check_vnf_intf_pair(dst_vnf
, dst_intfs
):
278 return Response(u
"VNF %s or intfs %s does not exist" % (dst_vnf
, dst_intfs
), status
=501,
279 mimetype
="application/json")
281 cookie
= self
.api
.manage
.network_action_stop(src_vnf
, dst_vnf
, vnf_src_interface
=src_intfs
,
282 vnf_dst_interface
=dst_intfs
, bidirectional
=True)
283 return Response(json
.dumps(cookie
), status
=200, mimetype
="application/json")
284 except Exception as e
:
285 logging
.exception(u
"%s: Error deleting the chain.\n %s" % (__name__
, e
))
286 return Response(u
"Error deleting the chain", status
=500, mimetype
="application/json")
289 class ChainVnfDcStackInterfaces(Resource
):
291 Handles requests targeted at: "/v1/chain/<src_dc>/<src_stack>/<src_vnf>/<src_intfs>/<dst_dc>/<dst_stack>/<dst_vnf>/<dst_intfs>"
292 Handles tearing down or setting up a chain between two vnfs for stacks.
295 def __init__(self
, api
):
298 def put(self
, src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
):
300 A PUT request to "/v1/chain/<src_dc>/<src_stack>/<src_vnf>/<src_intfs>/<dst_dc>/<dst_stack>/<dst_vnf>/<dst_intfs>"
303 :Note: PUT Requests can not set up custom paths!
305 :param src_dc: Name of the source datacenter
307 :param src_stack: Name of the source stack
308 :type src_stack: `str`
309 :param src_vnf: Name of the source VNF
310 :type src_vnf: ``str``
311 :param src_intfs: Name of the source VNF interface to chain on
312 :type src_intfs: ``str``
313 :param dst_dc: Name of the destination datacenter
314 :type dst_dc: ``str``
315 :param dst_stack: Name of the destination stack
316 :type dst_stack: ``str``
317 :param dst_vnf: Name of the destination VNF
318 :type dst_vnf: ``str``
319 :param dst_intfs: Name of the destination VNF interface to chain on
320 :type dst_intfs: ``str``
321 :return: flask.Response 200 if set up correctly else 500\
322 also returns the cookie as dict {'cookie': value}
323 501 if VNF or intfs does not exist
324 :rtype: :class:`flask.Response`
327 # search for real names
328 real_names
= self
._findNames
(src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
)
329 if type(real_names
) is not tuple:
330 # something went wrong
333 container_src
, container_dst
, interface_src
, interface_dst
= real_names
335 # check if both VNFs exist
336 if not self
.api
.manage
.check_vnf_intf_pair(container_src
, interface_src
):
337 return Response(u
"VNF %s or intfs %s does not exist" % (container_src
, interface_src
), status
=501,
338 mimetype
="application/json")
339 if not self
.api
.manage
.check_vnf_intf_pair(container_dst
, interface_dst
):
340 return Response(u
"VNF %s or intfs %s does not exist" % (container_dst
, interface_dst
), status
=501,
341 mimetype
="application/json")
344 cookie
= self
.api
.manage
.network_action_start(container_src
, container_dst
, vnf_src_interface
=interface_src
,
345 vnf_dst_interface
=interface_dst
, bidirectional
=True,
347 resp
= {'cookie': cookie
}
348 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
350 except Exception as e
:
351 logging
.exception(u
"%s: Error setting up the chain.\n %s" % (__name__
, e
))
352 return Response(u
"Error setting up the chain", status
=500, mimetype
="application/json")
354 def post(self
, src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
):
356 A post request to "/v1/chain/<src_dc>/<src_stack>/<src_vnf>/<src_intfs>/<dst_dc>/<dst_stack>/<dst_vnf>/<dst_intfs>"
357 will create a chain between two interfaces at the specified vnfs.
358 The POST data contains the path like this.
359 { "path": ["dc1.s1", "s1", "dc4.s1"]}
360 path specifies the destination vnf and interface and contains a list of switches
361 that the path traverses. The path may not contain single hop loops like:
363 This is a limitation of Ryu, as Ryu does not allow the `INPUT_PORT` action!
365 :param src_vnf: Name of the source VNF
366 :type src_vnf: ``str``
367 :param src_intfs: Name of the source VNF interface to chain on
368 :type src_intfs: ``str``
369 :param dst_vnf: Name of the destination VNF
370 :type dst_vnf: ``str``
371 :param dst_intfs: Name of the destination VNF interface to chain on
372 :type dst_intfs: ``str``
373 :return: flask.Response 200 if set up correctly else 500 also returns the cookie as dict {'cookie': value}
374 501 if vnf / intfs do not exist
375 :rtype: :class:`flask.Response`
379 path
= request
.json
.get('path')
380 layer2
= request
.json
.get('layer2', True)
385 # search for real names
386 real_names
= self
._findNames
(src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
)
387 if type(real_names
) is not tuple:
388 # something went wrong
391 container_src
, container_dst
, interface_src
, interface_dst
= real_names
394 cookie
= self
.api
.manage
.network_action_start(container_src
, container_dst
, vnf_src_interface
=interface_src
,
395 vnf_dst_interface
=interface_dst
, bidirectional
=True,
396 path
=path
, layer2
=layer2
)
397 resp
= {'cookie': cookie
}
398 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
400 except Exception as e
:
401 logging
.exception(u
"%s: Error setting up the chain.\n %s" % (__name__
, e
))
402 return Response(u
"Error setting up the chain", status
=500, mimetype
="application/json")
404 def delete(self
, src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
):
406 A DELETE request to "/v1/chain/<src_dc>/<src_stack>/<src_vnf>/<src_intfs>/<dst_dc>/<dst_stack>/<dst_vnf>/<dst_intfs>"
407 will delete a previously created chain.
409 :param src_dc: Name of the source datacenter
411 :param src_stack: Name of the source stack
412 :type src_stack: `str`
413 :param src_vnf: Name of the source VNF
414 :type src_vnf: ``str``
415 :param src_intfs: Name of the source VNF interface to chain on
416 :type src_intfs: ``str``
417 :param dst_dc: Name of the destination datacenter
418 :type dst_dc: ``str``
419 :param dst_stack: Name of the destination stack
420 :type dst_stack: ``str``
421 :param dst_vnf: Name of the destination VNF
422 :type dst_vnf: ``str``
423 :param dst_intfs: Name of the destination VNF interface to chain on
424 :type dst_intfs: ``str``
425 :return: flask.Response 200 if set up correctly else 500\
426 also returns the cookie as dict {'cookie': value}
427 501 if one of the VNF / intfs does not exist
428 :rtype: :class:`flask.Response`
431 # search for real names
432 real_names
= self
._findNames
(src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
)
433 if type(real_names
) is not tuple:
434 # something went wrong, real_names is a Response object
437 container_src
, container_dst
, interface_src
, interface_dst
= real_names
440 cookie
= self
.api
.manage
.network_action_stop(container_src
, container_dst
, vnf_src_interface
=interface_src
,
441 vnf_dst_interface
=interface_dst
, bidirectional
=True)
442 return Response(json
.dumps(cookie
), status
=200, mimetype
="application/json")
443 except Exception as e
:
444 logging
.exception(u
"%s: Error deleting the chain.\n %s" % (__name__
, e
))
445 return Response(u
"Error deleting the chain", status
=500, mimetype
="application/json")
447 # Tries to find real container and interface names according to heat template names
448 # Returns a tuple of 4 or a Response object
449 def _findNames(self
, src_dc
, src_stack
, src_vnf
, src_intfs
, dst_dc
, dst_stack
, dst_vnf
, dst_intfs
):
450 # search for datacenters
451 if src_dc
not in self
.api
.manage
.net
.dcs
or dst_dc
not in self
.api
.manage
.net
.dcs
:
452 return Response(u
"At least one DC does not exist", status
=500, mimetype
="application/json")
453 dc_src
= self
.api
.manage
.net
.dcs
[src_dc
]
454 dc_dst
= self
.api
.manage
.net
.dcs
[dst_dc
]
455 # search for related OpenStackAPIs
458 from openstack_api_endpoint
import OpenstackApiEndpoint
459 for api
in OpenstackApiEndpoint
.dc_apis
:
460 if api
.compute
.dc
== dc_src
:
462 if api
.compute
.dc
== dc_dst
:
464 if api_src
is None or api_dst
is None:
465 return Response(u
"At least one OpenStackAPI does not exist", status
=500, mimetype
="application/json")
469 for stack
in api_src
.compute
.stacks
.values():
470 if stack
.stack_name
== src_stack
:
472 for stack
in api_dst
.compute
.stacks
.values():
473 if stack
.stack_name
== dst_stack
:
475 if stack_src
is None or stack_dst
is None:
476 return Response(u
"At least one Stack does not exist", status
=500, mimetype
="application/json")
480 for server
in stack_src
.servers
.values():
481 if server
.template_name
== src_vnf
:
484 for server
in stack_dst
.servers
.values():
485 if server
.template_name
== dst_vnf
:
488 if server_src
is None or server_dst
is None:
489 return Response(u
"At least one VNF does not exist", status
=500, mimetype
="application/json")
491 container_src
= server_src
.name
492 container_dst
= server_dst
.name
497 if src_intfs
in server_src
.port_names
:
498 port_src
= stack_src
.ports
[src_intfs
]
499 if dst_intfs
in server_dst
.port_names
:
500 port_dst
= stack_dst
.ports
[dst_intfs
]
501 if port_src
is None or port_dst
is None:
502 return Response(u
"At least one Port does not exist", status
=500, mimetype
="application/json")
504 interface_src
= port_src
.intf_name
505 interface_dst
= port_dst
.intf_name
507 return container_src
, container_dst
, interface_src
, interface_dst
510 class BalanceHostDcStack(Resource
):
512 Handles requests to "/v1/lb/<src_dc>/<src_stack>/<vnf_src_name>/<vnf_src_interface>"
513 Sets up LoadBalancers for VNFs that are belonging to a certain stack.
516 def __init__(self
, api
):
519 def post(self
, src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
):
521 A POST request to "/v1/lb/<src_dc>/<src_stack>/<vnf_src_name>/<vnf_src_interface>"
522 will set up a loadbalancer. The target VNFs and interfaces are in the post data.
525 See :class:`heat.chain_api.BalanceHost.post`
527 :param src_dc: Name of the source VNF
528 :type src_dc: ``str``
529 :param src_stack: Name of the source VNF interface to chain on
530 :type src_stack: ``str``
531 * src_stack == "floating" sets up a new floating node, so only use this name if you know what you are doing.
533 :type vnf_src_name: ``str``
534 :param vnf_src_interface:
535 :type vnf_src_interface: ``str``
536 :return: flask.Response 200 if set up correctly else 500
537 :rtype: :class:`flask.Response`
542 if req
is None or len(req
) == 0 or "dst_vnf_interfaces" not in req
:
543 return Response(u
"You have to specify destination vnfs via the POST data.",
544 status
=500, mimetype
="application/json")
546 dst_vnfs
= req
.get('dst_vnf_interfaces')
551 if src_stack
!= "floating":
552 real_src
= self
._findName
(src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
)
553 if type(real_src
) is not tuple:
554 # something went wrong, real_src is a Response object
557 container_src
, interface_src
= real_src
560 for dst_vnf
in dst_vnfs
:
561 dst_dc
= dst_vnf
.get('pop', None)
562 dst_stack
= dst_vnf
.get('stack', None)
563 dst_server
= dst_vnf
.get('server', None)
564 dst_port
= dst_vnf
.get('port', None)
565 if dst_dc
is not None and dst_stack
is not None and dst_server
is not None and dst_port
is not None:
566 real_dst
= self
._findName
(dst_dc
, dst_stack
, dst_server
, dst_port
)
567 if type(real_dst
) is not tuple:
568 # something went wrong, real_dst is a Response object
570 real_dst_dict
[real_dst
[0]] = real_dst
[1]
572 input_object
= {"dst_vnf_interfaces": real_dst_dict
, "path": req
.get("path", None)}
574 if src_stack
!= "floating":
575 self
.api
.manage
.add_loadbalancer(container_src
, interface_src
, lb_data
=input_object
)
576 return Response(u
"Loadbalancer set up at %s:%s" % (container_src
, interface_src
),
577 status
=200, mimetype
="application/json")
579 cookie
, floating_ip
= self
.api
.manage
.add_floating_lb(src_dc
, lb_data
=input_object
)
581 return Response(json
.dumps({"cookie": "%d" % cookie
, "floating_ip": "%s" % floating_ip
}),
582 status
=200, mimetype
="application/json")
584 except Exception as e
:
585 logging
.exception(u
"%s: Error setting up the loadbalancer at %s %s %s:%s.\n %s" %
586 (__name__
, src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
, e
))
587 return Response(u
"%s: Error setting up the loadbalancer at %s %s %s:%s.\n %s" %
588 (__name__
, src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
, e
), status
=500,
589 mimetype
="application/json")
591 def delete(self
, src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
):
593 Will delete a load balancer that sits behind a specified interface at a vnf for a specific stack
595 :param src_dc: Name of the source VNF
596 :type src_dc: ``str``
597 :param src_stack: Name of the source VNF interface to chain on
598 :type src_stack: ``str``
600 :type vnf_src_name: ``str``
601 :param vnf_src_interface:
602 :type vnf_src_interface: ``str``
603 :return: flask.Response 200 if set up correctly else 500
604 :rtype: :class:`flask.Response`
609 if src_stack
!= "floating":
610 real_src
= self
._findName
(src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
)
611 if type(real_src
) is not tuple:
612 # something went wrong, real_src is a Response object
615 container_src
, interface_src
= real_src
617 self
.api
.manage
.delete_loadbalancer(container_src
, interface_src
)
618 return Response(u
"Loadbalancer deleted at %s:%s" % (vnf_src_name
, vnf_src_interface
),
619 status
=200, mimetype
="application/json")
621 cookie
= vnf_src_name
622 self
.api
.manage
.delete_floating_lb(cookie
)
623 return Response(u
"Floating loadbalancer with cookie %s deleted" % (cookie
),
624 status
=200, mimetype
="application/json")
626 except Exception as e
:
627 logging
.exception(u
"%s: Error deleting the loadbalancer at %s %s %s%s.\n %s" %
628 (__name__
, src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
, e
))
629 return Response(u
"%s: Error deleting the loadbalancer at %s %s %s%s." %
630 (__name__
, src_dc
, src_stack
, vnf_src_name
, vnf_src_interface
), status
=500,
631 mimetype
="application/json")
633 # Tries to find real container and port name according to heat template names
634 # Returns a string or a Response object
635 def _findName(self
, dc
, stack
, vnf
, port
):
636 # search for datacenters
637 if dc
not in self
.api
.manage
.net
.dcs
:
638 return Response(u
"DC does not exist", status
=500, mimetype
="application/json")
639 dc_real
= self
.api
.manage
.net
.dcs
[dc
]
640 # search for related OpenStackAPIs
642 from openstack_api_endpoint
import OpenstackApiEndpoint
643 for api
in OpenstackApiEndpoint
.dc_apis
:
644 if api
.compute
.dc
== dc_real
:
647 return Response(u
"OpenStackAPI does not exist", status
=500, mimetype
="application/json")
650 for stackObj
in api_real
.compute
.stacks
.values():
651 if stackObj
.stack_name
== stack
:
652 stack_real
= stackObj
653 if stack_real
is None:
654 return Response(u
"Stack does not exist", status
=500, mimetype
="application/json")
657 for server
in stack_real
.servers
.values():
658 if server
.template_name
== vnf
:
661 if server_real
is None:
662 return Response(u
"VNF does not exist", status
=500, mimetype
="application/json")
664 container_real
= server_real
.name
668 if port
in server_real
.port_names
:
669 port_real
= stack_real
.ports
[port
]
670 if port_real
is None:
671 return Response(u
"At least one Port does not exist", status
=500, mimetype
="application/json")
673 interface_real
= port_real
.intf_name
675 return container_real
, interface_real
678 class BalanceHost(Resource
):
680 Handles requests at "/v1/lb/<vnf_src_name>/<vnf_src_interface>"
681 to set up or delete Load Balancers.
684 def __init__(self
, api
):
687 def post(self
, vnf_src_name
, vnf_src_interface
):
689 Will set up a Load balancer behind an interface at a specified vnf
690 We need both to avoid naming conflicts as interface names are not unique
692 :param vnf_src_name: Name of the source VNF
693 :type vnf_src_name: ``str``
694 :param vnf_src_interface: Name of the source VNF interface to chain on
695 :type vnf_src_interface: ``str``
696 :return: flask.Response 200 if set up correctly else 500
697 501 if VNF or intfs does not exist
698 :rtype: :class:`flask.Response`
703 if req
is None or len(req
) == 0 or "dst_vnf_interfaces" not in req
:
704 return Response(u
"You have to specify destination vnfs via the POST data.",
705 status
=500, mimetype
="application/json")
707 if vnf_src_name
!= "floating":
709 if not self
.api
.manage
.check_vnf_intf_pair(vnf_src_name
, vnf_src_interface
):
710 return Response(u
"VNF %s or intfs %s does not exist" % (vnf_src_name
, vnf_src_interface
),
712 mimetype
="application/json")
713 self
.api
.manage
.add_loadbalancer(vnf_src_name
, vnf_src_interface
, lb_data
=req
)
715 return Response(u
"Loadbalancer set up at %s:%s" % (vnf_src_name
, vnf_src_interface
),
716 status
=200, mimetype
="application/json")
718 cookie
, floating_ip
= self
.api
.manage
.add_floating_lb(vnf_src_interface
, lb_data
=req
)
720 return Response(json
.dumps({"cookie": "%d" % cookie
, "floating_ip": "%s" % floating_ip
}),
721 status
=200, mimetype
="application/json")
722 except Exception as e
:
723 logging
.exception(u
"%s: Error setting up the loadbalancer at %s:%s.\n %s" %
724 (__name__
, vnf_src_name
, vnf_src_interface
, e
))
725 return Response(u
"%s: Error setting up the loadbalancer at %s:%s.\n %s" %
726 (__name__
, vnf_src_name
, vnf_src_interface
, e
), status
=500, mimetype
="application/json")
728 def delete(self
, vnf_src_name
, vnf_src_interface
):
730 Will delete a load balancer that sits behind a specified interface at a vnf
732 :param vnf_src_name: Name of the source VNF
733 :type vnf_src_name: ``str``
734 :param vnf_src_interface: Name of the source VNF interface to chain on
735 :type vnf_src_interface: ``str``
736 :return: flask.Response 200 if set up correctly else 500
737 501 if VNF or intfs does not exist
738 :rtype: :class:`flask.Response`
742 if not self
.api
.manage
.check_vnf_intf_pair(vnf_src_name
, vnf_src_interface
):
743 return Response(u
"VNF %s or intfs %s does not exist" % (vnf_src_name
, vnf_src_interface
), status
=501,
744 mimetype
="application/json")
746 logging
.debug("Deleting loadbalancer at %s: interface: %s" % (vnf_src_name
, vnf_src_interface
))
747 net
= self
.api
.manage
.net
749 if vnf_src_name
!= "floating":
750 # check if VNF exists
751 if vnf_src_name
not in net
:
752 return Response(u
"Source VNF or interface can not be found." % vnf_src_name
,
753 status
=404, mimetype
="application/json")
755 self
.api
.manage
.delete_loadbalancer(vnf_src_name
, vnf_src_interface
)
757 return Response(u
"Loadbalancer deleted at %s:%s" % (vnf_src_name
, vnf_src_interface
),
758 status
=200, mimetype
="application/json")
760 cookie
= vnf_src_name
761 self
.api
.manage
.delete_floating_lb(cookie
)
762 return Response(u
"Floating loadbalancer with cookie %s removed" % (cookie
),
763 status
=200, mimetype
="application/json")
764 except Exception as e
:
765 logging
.exception(u
"%s: Error deleting the loadbalancer at %s%s.\n %s" %
766 (__name__
, vnf_src_name
, vnf_src_interface
, e
))
767 return Response(u
"%s: Error deleting the loadbalancer at %s%s." %
768 (__name__
, vnf_src_name
, vnf_src_interface
), status
=500, mimetype
="application/json")
771 class QueryTopology(Resource
):
773 Handles requests at "/v1/topo/"
776 def __init__(self
, api
):
781 Answers GET requests for the current network topology at "/v1/topo".
782 This will only return switches and datacenters and ignore currently deployed VNFs.
784 :return: 200 if successful with the network graph as json dict, else 500
788 logging
.debug("Querying topology")
789 graph
= self
.api
.manage
.net
.DCNetwork_graph
790 net
= self
.api
.manage
.net
792 topology
= {"nodes": list()}
795 # remove root node as well as the floating switch fs1
796 if n
!= "root" and n
!= "fs1":
797 # we only want to return switches!
798 if not isinstance(net
[n
], OVSSwitch
):
802 # get real datacenter label
803 for dc
in self
.api
.manage
.net
.dcs
.values():
804 if str(dc
.switch
) == str(n
):
805 node
["name"] = str(n
)
806 node
["type"] = "Datacenter"
807 node
["label"] = str(dc
.label
)
810 # node is not a datacenter. It has to be a switch
811 if node
.get("type", "") != "Datacenter":
812 node
["name"] = str(n
)
813 node
["type"] = "Switch"
815 node
["links"] = list()
816 # add links to the topology
817 for graph_node
, data
in graph
[n
].items():
818 # only add links to the topology that connect switches
819 if isinstance(net
[graph_node
], OVSSwitch
):
820 # we allow multiple edges between switches, so add them all
821 # with their unique keys
822 link
= copy
.copy(data
)
824 # do not add any links to the floating switch to the topology!
825 if graph_node
== "fs1":
827 # the translator wants everything as a string!
828 for key
, value
in link
[edge
].items():
829 link
[edge
][key
] = str(value
)
830 # name of the destination
831 link
[edge
]["name"] = graph_node
832 node
["links"].append(link
)
834 topology
["nodes"].append(node
)
836 return Response(json
.dumps(topology
),
837 status
=200, mimetype
="application/json")
838 except Exception as e
:
839 logging
.exception(u
"%s: Error querying topology.\n %s" %
841 return Response(u
"%s: Error querying topology.\n %s" %
842 (__name__
, e
), status
=500, mimetype
="application/json")