7fa6bb9030
Currently, lbaas agent refresh loadbalancer based on the number of active listener, and will update the port with device_own='' if delete the last listener in loadbalancer. So the device_owner of the vip port of exist loadbalancer will never update to the correct one. This patch add port device_owner field to PUT to guarantee the field is 'neutron:LOADBALANCERV2'. Change-Id: I2de5800f4a6b06f98174ef42390f0e2c91bf0cfd Closes-Bug:#1692995
215 lines
8.7 KiB
Python
215 lines
8.7 KiB
Python
# Copyright 2015 Rackspace.
|
|
#
|
|
# 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.
|
|
|
|
from neutron_lib.api.definitions import portbindings
|
|
from neutron_lib import constants
|
|
from neutron_lib import exceptions as n_exc
|
|
from oslo_log import log as logging
|
|
import oslo_messaging as messaging
|
|
|
|
from neutron_lbaas._i18n import _
|
|
from neutron_lbaas.db.loadbalancer import loadbalancer_dbv2
|
|
from neutron_lbaas.db.loadbalancer import models as db_models
|
|
from neutron_lbaas.services.loadbalancer import data_models
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class LoadBalancerCallbacks(object):
|
|
|
|
# history
|
|
# 1.0 Initial version
|
|
target = messaging.Target(version='1.0')
|
|
|
|
def __init__(self, plugin):
|
|
super(LoadBalancerCallbacks, self).__init__()
|
|
self.plugin = plugin
|
|
|
|
def get_ready_devices(self, context, host=None):
|
|
with context.session.begin(subtransactions=True):
|
|
agents = self.plugin.db.get_lbaas_agents(
|
|
context, filters={'host': [host]})
|
|
if not agents:
|
|
return []
|
|
elif len(agents) > 1:
|
|
LOG.warning('Multiple lbaas agents found on host %s', host)
|
|
loadbalancers = self.plugin.db.list_loadbalancers_on_lbaas_agent(
|
|
context, agents[0].id)
|
|
loadbalancer_ids = [
|
|
l.id for l in loadbalancers]
|
|
|
|
qry = context.session.query(
|
|
loadbalancer_dbv2.models.LoadBalancer.id)
|
|
qry = qry.filter(
|
|
loadbalancer_dbv2.models.LoadBalancer.id.in_(
|
|
loadbalancer_ids))
|
|
qry = qry.filter(
|
|
loadbalancer_dbv2.models.LoadBalancer.provisioning_status.in_(
|
|
constants.ACTIVE_PENDING_STATUSES))
|
|
up = True # makes pep8 and sqlalchemy happy
|
|
qry = qry.filter(
|
|
loadbalancer_dbv2.models.LoadBalancer.admin_state_up == up)
|
|
return [id for id, in qry]
|
|
|
|
def get_loadbalancer(self, context, loadbalancer_id=None):
|
|
lb_model = self.plugin.db.get_loadbalancer(context, loadbalancer_id)
|
|
if lb_model.vip_port and lb_model.vip_port.fixed_ips:
|
|
for fixed_ip in lb_model.vip_port.fixed_ips:
|
|
subnet_dict = self.plugin.db._core_plugin.get_subnet(
|
|
context, fixed_ip.subnet_id
|
|
)
|
|
setattr(fixed_ip, 'subnet', data_models.Subnet.from_dict(
|
|
subnet_dict))
|
|
if lb_model.provider:
|
|
device_driver = self.plugin.drivers[
|
|
lb_model.provider.provider_name].device_driver
|
|
setattr(lb_model.provider, 'device_driver', device_driver)
|
|
if lb_model.vip_port:
|
|
network_dict = self.plugin.db._core_plugin.get_network(
|
|
context, lb_model.vip_port.network_id)
|
|
setattr(lb_model.vip_port, 'network',
|
|
data_models.Network.from_dict(network_dict))
|
|
lb_dict = lb_model.to_dict(stats=False)
|
|
|
|
return lb_dict
|
|
|
|
def loadbalancer_deployed(self, context, loadbalancer_id):
|
|
with context.session.begin(subtransactions=True):
|
|
qry = context.session.query(db_models.LoadBalancer)
|
|
qry = qry.filter_by(id=loadbalancer_id)
|
|
loadbalancer = qry.one()
|
|
|
|
# set all resources to active
|
|
if (loadbalancer.provisioning_status in
|
|
constants.ACTIVE_PENDING_STATUSES):
|
|
loadbalancer.provisioning_status = constants.ACTIVE
|
|
|
|
if loadbalancer.listeners:
|
|
for l in loadbalancer.listeners:
|
|
if (l.provisioning_status in
|
|
constants.ACTIVE_PENDING_STATUSES):
|
|
l.provisioning_status = constants.ACTIVE
|
|
if (l.default_pool
|
|
and l.default_pool.provisioning_status in
|
|
constants.ACTIVE_PENDING_STATUSES):
|
|
l.default_pool.provisioning_status = constants.ACTIVE
|
|
if l.default_pool.members:
|
|
for m in l.default_pool.members:
|
|
if (m.provisioning_status in
|
|
constants.ACTIVE_PENDING_STATUSES):
|
|
m.provisioning_status = constants.ACTIVE
|
|
if l.default_pool.healthmonitor:
|
|
hm = l.default_pool.healthmonitor
|
|
ps = hm.provisioning_status
|
|
if ps in constants.ACTIVE_PENDING_STATUSES:
|
|
(l.default_pool.healthmonitor
|
|
.provisioning_status) = constants.ACTIVE
|
|
|
|
def update_status(self, context, obj_type, obj_id,
|
|
provisioning_status=None, operating_status=None):
|
|
if not provisioning_status and not operating_status:
|
|
LOG.warning('update_status for %(obj_type)s %(obj_id)s called '
|
|
'without specifying provisioning_status or '
|
|
'operating_status' % {'obj_type': obj_type,
|
|
'obj_id': obj_id})
|
|
return
|
|
model_mapping = {
|
|
'loadbalancer': db_models.LoadBalancer,
|
|
'pool': db_models.PoolV2,
|
|
'listener': db_models.Listener,
|
|
'member': db_models.MemberV2,
|
|
'healthmonitor': db_models.HealthMonitorV2
|
|
}
|
|
if obj_type not in model_mapping:
|
|
raise n_exc.Invalid(_('Unknown object type: %s') % obj_type)
|
|
try:
|
|
self.plugin.db.update_status(
|
|
context, model_mapping[obj_type], obj_id,
|
|
provisioning_status=provisioning_status,
|
|
operating_status=operating_status)
|
|
except n_exc.NotFound:
|
|
# update_status may come from agent on an object which was
|
|
# already deleted from db with other request
|
|
LOG.warning('Cannot update status: %(obj_type)s %(obj_id)s '
|
|
'not found in the DB, it was probably deleted '
|
|
'concurrently',
|
|
{'obj_type': obj_type, 'obj_id': obj_id})
|
|
|
|
def loadbalancer_destroyed(self, context, loadbalancer_id=None):
|
|
"""Agent confirmation hook that a load balancer has been destroyed.
|
|
|
|
This method exists for subclasses to change the deletion
|
|
behavior.
|
|
"""
|
|
pass
|
|
|
|
def plug_vip_port(self, context, port_id=None, host=None):
|
|
if not port_id:
|
|
return
|
|
|
|
try:
|
|
port = self.plugin.db._core_plugin.get_port(
|
|
context,
|
|
port_id
|
|
)
|
|
except n_exc.PortNotFound:
|
|
LOG.debug('Unable to find port %s to plug.', port_id)
|
|
return
|
|
|
|
port['admin_state_up'] = True
|
|
port[portbindings.HOST_ID] = host
|
|
port['device_owner'] = constants.DEVICE_OWNER_LOADBALANCERV2
|
|
self.plugin.db._core_plugin.update_port(
|
|
context,
|
|
port_id,
|
|
{'port': port}
|
|
)
|
|
|
|
def unplug_vip_port(self, context, port_id=None, host=None):
|
|
if not port_id:
|
|
return
|
|
|
|
try:
|
|
port = self.plugin.db._core_plugin.get_port(
|
|
context,
|
|
port_id
|
|
)
|
|
except n_exc.PortNotFound:
|
|
LOG.debug('Unable to find port %s to unplug. This can occur when '
|
|
'the Vip has been deleted first.',
|
|
port_id)
|
|
return
|
|
|
|
port['admin_state_up'] = False
|
|
port['device_owner'] = ''
|
|
port['device_id'] = ''
|
|
|
|
try:
|
|
self.plugin.db._core_plugin.update_port(
|
|
context,
|
|
port_id,
|
|
{'port': port}
|
|
)
|
|
|
|
except n_exc.PortNotFound:
|
|
LOG.debug('Unable to find port %s to unplug. This can occur when '
|
|
'the Vip has been deleted first.',
|
|
port_id)
|
|
|
|
def update_loadbalancer_stats(self, context,
|
|
loadbalancer_id=None,
|
|
stats=None):
|
|
self.plugin.db.update_loadbalancer_stats(context, loadbalancer_id,
|
|
stats)
|