senlin/senlin/drivers/openstack/lbaas.py

239 lines
8.8 KiB
Python

# 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.
import eventlet
import six
from oslo_log import log as logging
from senlin.common.i18n import _
from senlin.common import trust
from senlin.drivers import base
from senlin.drivers.openstack import neutron_v2 as neutronclient
LOG = logging.getLogger(__name__)
class LoadBalancerDriver(base.DriverBase):
"""Common driver for LoadBalancer management"""
def __init__(self, context):
super(LoadBalancerDriver, self).__init__(context)
self.context = context
params = trust.get_connection_params(context)
self.nc = neutronclient.NeutronClient(params)
def _wait_for_lb_ready(self, lb_data, ignore_not_found=False):
"""Keep waiting until loadbalancer is in ready status
This method will keep waiting until loadbalancer resource listed in
lb_data becoming ready which means its provisioning_status is ACTIVE
its operating_status is ONLINE. If ignore_not_found is set to True,
unexisting of loadbalancer resource is also an acceptable result.
"""
loadbalancer_id = lb_data.get('loadbalancer')
while True:
loadbalancer_ready = True
if loadbalancer_id:
lb = self.nc.loadbalancer_get(loadbalancer_id)
if lb is None:
loadbalancer_ready = True if ignore_not_found else False
elif (lb.provisioning_status == 'ACTIVE') and (
lb.operating_status == 'ONLINE'):
loadbalancer_ready = True
else:
loadbalancer_ready = False
if loadbalancer_ready:
return True, lb_data
else:
LOG.debug(_('Waiting for loadbalancer %(lb)s becoming ready'
) % {'lb': loadbalancer_id})
eventlet.sleep(2)
# TODO(Yanyan Hu): Add timeout check.
def lb_create(self, vip, pool):
"""Create a Neutron lbaas instance"""
# Catch all exceptions that could happen in each step and gracefully
# removing all lbaas related resources that have been created before
# returning.
try:
# Create loadblancer
lb_data = {}
subnet = vip.get('subnet', None)
subnet_id = (self.nc.subnet_get(subnet)).id
address = vip.get('address', None)
admin_state_up = vip.get('admin_state_up', None)
lb = self.nc.loadbalancer_create(subnet_id, address,
admin_state_up)
lb_data['loadbalancer'] = lb.id
res, reason = self._wait_for_lb_ready(lb_data)
if res is not True:
return res, reason
# Create listener
protocol = vip.get('protocol')
protocol_port = vip.get('protocol_port')
connection_limit = vip.get('connection_limit', None)
listener = self.nc.listener_create(lb.id, protocol, protocol_port,
connection_limit,
admin_state_up)
lb_data['listener'] = listener.id
res, reason = self._wait_for_lb_ready(lb_data)
if res is not True:
return res, reason
# Create pool
lb_algorithm = pool.get('lb_method')
protocol = pool.get('protocol')
admin_state_up = pool.get('admin_state_up')
pool = self.nc.pool_create(lb_algorithm, listener.id, protocol,
admin_state_up)
lb_data['pool'] = pool.id
res, reason = self._wait_for_lb_ready(lb_data)
if res is not True:
return res, reason
except Exception as ex:
self.lb_delete(**lb_data)
msg = _('Failed in creating lb resources: %(ex)s '
) % {'ex': six.text_type(ex)}
LOG.error(msg)
return False, msg
return True, lb_data
def lb_delete(self, **kwargs):
"""Delete a Neutron lbaas instance
The following Neutron lbaas resources will be deleted in order:
1)healthmonitor; 2)pool; 3)listener; 4)loadbalancer.
"""
loadbalancer_id = kwargs.get('loadbalancer')
listener_id = kwargs.get('listener')
pool_id = kwargs.get('pool')
healthmonitor_id = kwargs.get('healthmonitor', None)
lb_data = kwargs
try:
if healthmonitor_id is not None:
self.nc.healthmonitor_delete(healthmonitor_id)
del lb_data['healthmonitor']
self._wait_for_lb_ready(lb_data, ignore_not_found=True)
self.nc.pool_delete(pool_id)
del lb_data['pool']
self._wait_for_lb_ready(lb_data, ignore_not_found=True)
self.nc.listener_delete(listener_id)
del lb_data['listener']
self._wait_for_lb_ready(lb_data, ignore_not_found=True)
self.nc.loadbalancer_delete(loadbalancer_id)
except Exception as ex:
msg = _('Failed in deleting lb resources %(data)s: %(ex)s'
) % {'data': lb_data, 'ex': six.text_type(ex)}
LOG.error(msg)
return False, msg
return True, 'lb resource deleting succeeded'
def member_add(self, **kwargs):
"""Add a member to Neutron lbaas pool"""
node = kwargs.get('node')
pool_id = kwargs.get('pool_id')
port = kwargs.get('port')
subnet = kwargs.get('subnet')
try:
addresses = self._get_node_address(node, version=4)
if not addresses:
msg = _('Node does not have valid IPv%(version)s address'
) % {'version': 4}
raise Exception(msg)
else:
network_id = (self.nc.subnet_get(subnet))['network_id']
network_name = (self.nc.network_get(network_id))['name']
if network_name in addresses:
address = addresses[network_name]
else:
msg = _('Node is not in subnet %(subnet)s'
) % {'subnet': subnet}
raise Exception(msg)
subnet_id = (self.nc.subnet_get(subnet)).id
pool_member = self.nc.pool_member_create(pool_id, address, port,
subnet_id)
pool = self.nc.pool_get(pool_id)
listener = self.nc.listener_get(pool.listeners[0]['id'])
lb_data = {
'loadbalancer': listener.loadbalancers[0]['id'],
'member': pool_member.id
}
self._wait_for_lb_ready(lb_data)
except Exception as ex:
msg = _('Failed in adding node %(node)s into pool %(pool)s as '
'a member: %(ex)s') % {'node': node.id, 'pool': pool_id,
'ex': six.text_type(ex)}
LOG.error(msg)
return False
return pool_member.id
def member_remove(self, **kwargs):
"""Delete a member from Neutron lbaas pool"""
pool_id = kwargs.get('pool_id')
member_id = kwargs.get('member_id')
try:
self.nc.pool_member_delete(pool_id, member_id)
pool = self.nc.pool_get(pool_id)
listener = self.nc.listener_get(pool.listeners[0]['id'])
lb_data = {
'loadbalancer': listener.loadbalancers[0]['id'],
}
self._wait_for_lb_ready(lb_data)
except Exception as ex:
msg = _('Failed in removing member %(member)s from pool %(pool): '
'%(ex)s') % {'member': member_id, 'pool': pool_id,
'ex': six.test_type(ex)}
LOG.error(msg)
return False
return True
def _get_node_address(self, node, version=4):
"""Get IP address of node with specific version"""
node_detail = node.get_details(self.context)
node_addresses = node_detail.get('addresses')
address = {}
for network in node_addresses:
for addr in node_addresses[network]:
if addr['version'] == version:
address[network] = addr['addr']
return address