Lucas Palm af63722e3a Add the Angular LBaaS V2 'Create Listener' workflow
This change adds the Create Listener workflow action to
the listeners table on the load balancer detail page.  This wizard
allows you to crete a new listener, as well as any resource below
it in the hierarchy.

Partially-Implements: blueprint horizon-lbaas-v2-ui
Change-Id: I40488eac6c116e363071fb82ba3473a8b0430ed9
2016-03-07 12:18:24 -06:00

622 lines
21 KiB
Python

# Copyright 2015 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""API over the neutron LBaaS v2 service.
"""
from six.moves import _thread as thread
from time import sleep
from django.views import generic
from horizon import conf
from openstack_dashboard.api import network
from openstack_dashboard.api import neutron
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
neutronclient = neutron.neutronclient
def poll_loadbalancer_status(request, loadbalancer_id, callback,
from_state='PENDING_UPDATE', to_state='ACTIVE',
callback_kwargs=None):
"""Poll for the status of the load balancer.
Polls for the status of the load balancer and calls a function when the
status changes to a specified state.
:param request: django request object
:param loadbalancer_id: id of the load balancer to poll
:param callback: function to call when polling is complete
:param from_state: initial expected state of the load balancer
:param to_state: state to check for
:param callback_kwargs: kwargs to pass into the callback function
"""
interval = conf.HORIZON_CONFIG['ajax_poll_interval'] / 1000.0
status = from_state
while status == from_state:
sleep(interval)
lb = neutronclient(request).show_loadbalancer(
loadbalancer_id).get('loadbalancer')
status = lb['provisioning_status']
if status == to_state:
kwargs = {'loadbalancer_id': loadbalancer_id}
if callback_kwargs:
kwargs.update(callback_kwargs)
callback(request, **kwargs)
def create_loadbalancer(request):
data = request.DATA
spec = {
'vip_subnet_id': data['loadbalancer']['subnet']
}
if data['loadbalancer'].get('name'):
spec['name'] = data['loadbalancer']['name']
if data['loadbalancer'].get('description'):
spec['description'] = data['loadbalancer']['description']
if data['loadbalancer'].get('ip'):
spec['vip_address'] = data['loadbalancer']['ip']
loadbalancer = neutronclient(request).create_loadbalancer(
{'loadbalancer': spec}).get('loadbalancer')
if data.get('listener'):
# There is work underway to add a new API to LBaaS v2 that will
# allow us to pass in all information at once. Until that is
# available we use a separate thread to poll for the load
# balancer status and create the other resources when it becomes
# active.
args = (request, loadbalancer['id'], create_listener)
kwargs = {'from_state': 'PENDING_CREATE'}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
return loadbalancer
def create_listener(request, **kwargs):
"""Create a new listener.
"""
data = request.DATA
listenerSpec = {
'protocol': data['listener']['protocol'],
'protocol_port': data['listener']['port'],
'loadbalancer_id': kwargs['loadbalancer_id']
}
if data['listener'].get('name'):
listenerSpec['name'] = data['listener']['name']
if data['listener'].get('description'):
listenerSpec['description'] = data['listener']['description']
if data.get('certificates'):
listenerSpec['default_tls_container_ref'] = data['certificates'][0]
listenerSpec['sni_container_refs'] = data['certificates']
listener = neutronclient(request).create_listener(
{'listener': listenerSpec}).get('listener')
if data.get('pool'):
args = (request, kwargs['loadbalancer_id'], create_pool)
kwargs = {'callback_kwargs': {'listener_id': listener['id']}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
return listener
def create_pool(request, **kwargs):
"""Create a new pool.
"""
data = request.DATA
poolSpec = {
'protocol': data['pool']['protocol'],
'lb_algorithm': data['pool']['method'],
'listener_id': kwargs['listener_id']
}
if data['pool'].get('name'):
poolSpec['name'] = data['pool']['name']
if data['pool'].get('description'):
poolSpec['description'] = data['pool']['description']
pool = neutronclient(request).create_lbaas_pool(
{'pool': poolSpec}).get('pool')
if data.get('members'):
args = (request, kwargs['loadbalancer_id'], add_member)
kwargs = {'callback_kwargs': {'pool_id': pool['id'],
'index': 0}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
elif data.get('monitor'):
args = (request, kwargs['loadbalancer_id'], add_monitor)
kwargs = {'callback_kwargs': {'pool_id': pool['id']}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
return pool
def add_member(request, **kwargs):
"""Add a member to a pool.
"""
data = request.DATA
members = data.get('members')
if kwargs.get('members_to_add'):
members_to_add = kwargs['members_to_add']
index = [members.index(member) for member in members
if member['id'] == members_to_add[0]][0]
pool_id = data['pool'].get('id')
loadbalancer_id = data.get('loadbalancer_id')
else:
index = kwargs.get('index')
pool_id = kwargs.get('pool_id')
loadbalancer_id = kwargs.get('loadbalancer_id')
member = members[index]
memberSpec = {
'address': member['address'],
'protocol_port': member['port'],
'subnet_id': member['subnet']
}
if member.get('weight'):
memberSpec['weight'] = member['weight']
member = neutronclient(request).create_lbaas_member(
pool_id, {'member': memberSpec}).get('member')
index += 1
if kwargs.get('members_to_add'):
args = (request, loadbalancer_id, update_member_list)
members_to_add = kwargs['members_to_add']
members_to_add.pop(0)
kwargs = {'callback_kwargs': {
'existing_members': kwargs.get('existing_members'),
'members_to_add': members_to_add,
'members_to_delete': kwargs.get('members_to_delete')}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
elif len(members) > index:
args = (request, loadbalancer_id, add_member)
kwargs = {'callback_kwargs': {'pool_id': pool_id,
'index': index}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
elif data.get('monitor'):
args = (request, loadbalancer_id, add_monitor)
kwargs = {'callback_kwargs': {'pool_id': pool_id}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
return member
def remove_member(request, **kwargs):
"""Remove a member from the pool.
"""
data = request.DATA
loadbalancer_id = data.get('loadbalancer_id')
pool_id = data['pool']['id']
if kwargs.get('members_to_delete'):
members_to_delete = kwargs['members_to_delete']
member_id = members_to_delete.pop(0)
neutronclient(request).delete_lbaas_member(member_id, pool_id)
args = (request, loadbalancer_id, update_member_list)
kwargs = {'callback_kwargs': {
'existing_members': kwargs.get('existing_members'),
'members_to_add': kwargs.get('members_to_add'),
'members_to_delete': members_to_delete}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
def add_monitor(request, **kwargs):
"""Create a new health monitor for a pool.
"""
data = request.DATA
monitorSpec = {
'type': data['monitor']['type'],
'delay': data['monitor']['interval'],
'timeout': data['monitor']['timeout'],
'max_retries': data['monitor']['retry'],
'pool_id': kwargs['pool_id']
}
if data['monitor'].get('method'):
monitorSpec['http_method'] = data['monitor']['method']
if data['monitor'].get('path'):
monitorSpec['url_path'] = data['monitor']['path']
if data['monitor'].get('status'):
monitorSpec['expected_codes'] = data['monitor']['status']
return neutronclient(request).create_lbaas_healthmonitor(
{'healthmonitor': monitorSpec}).get('healthmonitor')
def update_loadbalancer(request, **kwargs):
"""Update a load balancer.
"""
data = request.DATA
spec = {}
loadbalancer_id = kwargs.get('loadbalancer_id')
if data['loadbalancer'].get('name'):
spec['name'] = data['loadbalancer']['name']
if data['loadbalancer'].get('description'):
spec['description'] = data['loadbalancer']['description']
return neutronclient(request).update_loadbalancer(
loadbalancer_id, {'loadbalancer': spec}).get('loadbalancer')
def update_listener(request, **kwargs):
"""Update a listener.
"""
data = request.DATA
listener_spec = {}
listener_id = data['listener'].get('id')
loadbalancer_id = data.get('loadbalancer_id')
if data['listener'].get('name'):
listener_spec['name'] = data['listener']['name']
if data['listener'].get('description'):
listener_spec['description'] = data['listener']['description']
listener = neutronclient(request).update_listener(
listener_id, {'listener': listener_spec}).get('listener')
if data.get('pool'):
args = (request, loadbalancer_id, update_pool)
thread.start_new_thread(poll_loadbalancer_status, args)
return listener
def update_pool(request, **kwargs):
"""Update a pool.
"""
data = request.DATA
pool_spec = {}
pool_id = data['pool'].get('id')
loadbalancer_id = data.get('loadbalancer_id')
if data['pool'].get('name'):
pool_spec['name'] = data['pool']['name']
if data['pool'].get('description'):
pool_spec['description'] = data['pool']['description']
pools = neutronclient(request).update_lbaas_pool(
pool_id, {'pool': pool_spec}).get('pools')
# Assemble the lists of member id's to add and remove, if any exist
tenant_id = request.user.project_id
new_members = data.get('members', [])
existing_members = neutronclient(request).list_lbaas_members(
pool_id, tenant_id=tenant_id).get('members')
new_member_ids = [member['id'] for member in new_members]
existing_member_ids = [member['id'] for member in existing_members]
members_to_add = [member_id for member_id in new_member_ids
if member_id not in existing_member_ids]
members_to_delete = [member_id for member_id in existing_member_ids
if member_id not in new_member_ids]
if members_to_add or members_to_delete:
args = (request, loadbalancer_id, update_member_list)
kwargs = {'callback_kwargs': {'existing_members': existing_members,
'members_to_add': members_to_add,
'members_to_delete': members_to_delete}}
thread.start_new_thread(poll_loadbalancer_status, args, kwargs)
elif data.get('monitor'):
args = (request, loadbalancer_id, update_monitor)
thread.start_new_thread(poll_loadbalancer_status, args)
return pools
def update_monitor(request, **kwargs):
"""Update a health monitor.
"""
data = request.DATA
monitor_spec = {}
monitor_id = data['monitor']['id']
if data['monitor'].get('interval'):
monitor_spec['delay'] = data['monitor']['interval']
if data['monitor'].get('timeout'):
monitor_spec['timeout'] = data['monitor']['timeout']
if data['monitor'].get('retry'):
monitor_spec['max_retries'] = data['monitor']['retry']
if data['monitor'].get('method'):
monitor_spec['http_method'] = data['monitor']['method']
if data['monitor'].get('path'):
monitor_spec['url_path'] = data['monitor']['path']
if data['monitor'].get('status'):
monitor_spec['expected_codes'] = data['monitor']['status']
healthmonitor = neutronclient(request).update_lbaas_healthmonitor(
monitor_id, {'healthmonitor': monitor_spec}).get('healthmonitor')
return healthmonitor
def update_member_list(request, **kwargs):
"""Update the list of members by adding or removing the necessary members.
"""
data = request.DATA
loadbalancer_id = data.get('loadbalancer_id')
existing_members = kwargs.get('existing_members')
members_to_add = kwargs.get('members_to_add')
members_to_delete = kwargs.get('members_to_delete')
if members_to_add:
kwargs = {'existing_members': existing_members,
'members_to_add': members_to_add,
'members_to_delete': members_to_delete}
add_member(request, **kwargs)
elif members_to_delete:
kwargs = {'existing_members': existing_members,
'members_to_add': members_to_add,
'members_to_delete': members_to_delete}
remove_member(request, **kwargs)
elif data.get('monitor'):
args = (request, loadbalancer_id, update_monitor)
thread.start_new_thread(poll_loadbalancer_status, args)
def add_floating_ip_info(request, loadbalancers):
"""Add floating IP address info to each load balancer.
"""
floating_ips = network.tenant_floating_ip_list(request)
for lb in loadbalancers:
floating_ip = {}
associated_ip = next((fip for fip in floating_ips
if fip['fixed_ip'] == lb['vip_address']), None)
if associated_ip is not None:
floating_ip['id'] = associated_ip['id']
floating_ip['ip'] = associated_ip['ip']
lb['floating_ip'] = floating_ip
@urls.register
class LoadBalancers(generic.View):
"""API for load balancers.
"""
url_regex = r'lbaas/loadbalancers/$'
@rest_utils.ajax()
def get(self, request):
"""List load balancers for current project.
The listing result is an object with property "items".
"""
tenant_id = request.user.project_id
loadbalancers = neutronclient(request).list_loadbalancers(
tenant_id=tenant_id).get('loadbalancers')
if request.GET.get('full') and network.floating_ip_supported(request):
add_floating_ip_info(request, loadbalancers)
return {'items': loadbalancers}
@rest_utils.ajax()
def post(self, request):
"""Create a new load balancer.
Creates a new load balancer as well as other optional resources such as
a listener, pool, monitor, etc.
"""
return create_loadbalancer(request)
@urls.register
class LoadBalancer(generic.View):
"""API for retrieving, updating, and deleting a single load balancer.
"""
url_regex = r'lbaas/loadbalancers/(?P<loadbalancer_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, loadbalancer_id):
"""Get a specific load balancer.
http://localhost/api/lbaas/loadbalancers/cc758c90-3d98-4ea1-af44-aab405c9c915
"""
loadbalancer = neutronclient(request).show_loadbalancer(
loadbalancer_id).get('loadbalancer')
if request.GET.get('full') and network.floating_ip_supported(request):
add_floating_ip_info(request, [loadbalancer])
return loadbalancer
@rest_utils.ajax()
def put(self, request, loadbalancer_id):
"""Edit a load balancer.
"""
kwargs = {'loadbalancer_id': loadbalancer_id}
update_loadbalancer(request, **kwargs)
@rest_utils.ajax()
def delete(self, request, loadbalancer_id):
"""Delete a specific load balancer.
http://localhost/api/lbaas/loadbalancers/cc758c90-3d98-4ea1-af44-aab405c9c915
"""
neutronclient(request).delete_loadbalancer(loadbalancer_id)
@urls.register
class Listeners(generic.View):
"""API for load balancer listeners.
"""
url_regex = r'lbaas/listeners/$'
@rest_utils.ajax()
def get(self, request):
"""List of listeners for the current project.
The listing result is an object with property "items".
"""
loadbalancer_id = request.GET.get('loadbalancerId')
tenant_id = request.user.project_id
result = neutronclient(request).list_listeners(tenant_id=tenant_id)
listener_list = result.get('listeners')
if loadbalancer_id:
listener_list = self._filter_listeners(listener_list,
loadbalancer_id)
return {'items': listener_list}
@rest_utils.ajax()
def post(self, request):
"""Create a new listener.
Creates a new listener as well as other optional resources such as
a pool, members, and health monitor.
"""
kwargs = {'loadbalancer_id': request.DATA.get('loadbalancer_id')}
return create_listener(request, **kwargs)
def _filter_listeners(self, listener_list, loadbalancer_id):
filtered_listeners = []
for listener in listener_list:
if listener['loadbalancers'][0]['id'] == loadbalancer_id:
filtered_listeners.append(listener)
return filtered_listeners
@urls.register
class Listener(generic.View):
"""API for retrieving, updating, and deleting a single listener.
"""
url_regex = r'lbaas/listeners/(?P<listener_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, listener_id):
"""Get a specific listener.
If the param 'includeChildResources' is passed in as true, the details
of all resources that exist under the listener will be returned along
with the listener details.
http://localhost/api/lbaas/listeners/cc758c90-3d98-4ea1-af44-aab405c9c915
"""
listener = neutronclient(request).show_listener(
listener_id).get('listener')
if request.GET.get('includeChildResources'):
resources = {}
resources['listener'] = listener
if listener.get('default_pool_id'):
pool_id = listener['default_pool_id']
pool = neutronclient(request).show_lbaas_pool(
pool_id).get('pool')
resources['pool'] = pool
if pool.get('members'):
tenant_id = request.user.project_id
members = neutronclient(request).list_lbaas_members(
pool_id, tenant_id=tenant_id).get('members')
resources['members'] = members
if pool.get('healthmonitor_id'):
monitor_id = pool['healthmonitor_id']
monitor = neutronclient(request).show_lbaas_healthmonitor(
monitor_id).get('healthmonitor')
resources['monitor'] = monitor
return resources
else:
return listener
@rest_utils.ajax()
def put(self, request, listener_id):
"""Edit a listener as well as any resources below it.
"""
kwargs = {'listener_id': listener_id}
update_listener(request, **kwargs)
@urls.register
class Pool(generic.View):
"""API for retrieving a single pool.
"""
url_regex = r'lbaas/pools/(?P<pool_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, pool_id):
"""Get a specific pool.
http://localhost/api/lbaas/pools/cc758c90-3d98-4ea1-af44-aab405c9c915
"""
lb = neutronclient(request).show_lbaas_pool(pool_id)
return lb.get('pool')
@urls.register
class Members(generic.View):
"""API for load balancer members.
"""
url_regex = r'lbaas/pools/(?P<pool_id>[^/]+)/members/$'
@rest_utils.ajax()
def get(self, request, pool_id):
"""List of members for the current project.
The listing result is an object with property "items".
"""
tenant_id = request.user.project_id
result = neutronclient(request).list_lbaas_members(pool_id,
tenant_id=tenant_id)
return {'items': result.get('members')}
@urls.register
class Member(generic.View):
"""API for retrieving a single member.
"""
url_regex = r'lbaas/pools/(?P<pool_id>[^/]+)' + \
'/members/(?P<member_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, member_id, pool_id):
"""Get a specific member belonging to a specific pool.
"""
lb = neutronclient(request).show_lbaas_member(member_id, pool_id)
return lb.get('member')
@urls.register
class HealthMonitor(generic.View):
"""API for retrieving a single health monitor.
"""
url_regex = r'lbaas/healthmonitors/(?P<healthmonitor_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, healthmonitor_id):
"""Get a specific health monitor.
"""
lb = neutronclient(request).show_lbaas_healthmonitor(healthmonitor_id)
return lb.get('healthmonitor')