A set of Neutron drivers for the VMware NSX.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

245 lines
11 KiB

# Copyright 2018 VMware, Inc.
# All Rights Reserved
#
# 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 import exceptions as n_exc
from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_const
from vmware_nsx.services.lbaas.nsx_p.implementation import lb_utils as p_utils
from vmware_nsx.services.lbaas.nsx_v3.implementation import lb_utils
LOG = logging.getLogger(__name__)
def _translate_member_state(state):
return lb_const.ENABLED if state else lb_const.DISABLED
class EdgeMemberManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
def _get_fip_object(self, context, fip):
filters = {'floating_ip_address': [fip]}
floating_ips = self.core_plugin.get_floatingips(context,
filters=filters)
if floating_ips:
return floating_ips[0]
msg = (_('Member IP %(fip)s is expected to be an external IP with a '
'floating IP') % {'fip': fip})
raise n_exc.BadRequest(resource='lbaas-vip', msg=msg)
def _get_info_from_fip(self, context, fip):
return self._get_fip_object(context, fip)['fixed_ip_address']
def _get_router_from_fip(self, context, fip):
return self._get_fip_object(context, fip)['router_id']
def _validate_member_lb_connectivity(self, context, member, completor):
lb = member['pool'].get('loadbalancer')
if not lb:
msg = (_('Member %s loadbalancer object is missing') %
member['id'])
raise n_exc.BadRequest(resource='lbaas-vip', msg=msg)
subnet_id = lb.get('vip_subnet_id')
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, subnet_id)
if network and not network.get('router:external'):
return
# If VIP is attached to an external network, loadbalancer_mgr might not
# attach it to a router. If not, set the LB service connectivity path
# to the member subnet's router.
service_client = self.core_plugin.nsxpolicy.load_balancer.lb_service
service = p_utils.get_lb_nsx_lb_service(
self.core_plugin.nsxpolicy, lb['id'], try_old=True)
if not service:
completor(success=False)
msg = (_('Cannot find loadbalancer %(lb_id)s service') %
{'lb_id': lb['id']})
raise n_exc.BadRequest(resource='lbaas-router', msg=msg)
if not service.get('connectivity_path'):
# Find the router of the local subnet
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, member['subnet_id'])
if network and network.get('router:external'):
# member ip should be a fip
try:
router_id = self._get_router_from_fip(
context, member['address'])
except n_exc.BadRequest:
with excutils.save_and_reraise_exception():
completor(success=False)
else:
try:
router_id = lb_utils.get_router_from_network(
context, self.core_plugin, member['subnet_id'])
except Exception:
completor(success=False)
msg = (_('Cannot find router attached to member '
'%(mem_id)s') % {'mem_id': member['id']})
raise n_exc.BadRequest(resource='lbaas-router', msg=msg)
if not router_id:
completor(success=False)
msg = (_('Cannot find router with uplink attached to '
'member %(mem_id)s') % {'mem_id': member['id']})
raise n_exc.BadRequest(resource='lbaas-router', msg=msg)
if not self.core_plugin.service_router_has_services(context,
router_id):
self.core_plugin.create_service_router(context, router_id)
connectivity_path = self.core_plugin.nsxpolicy.tier1.get_path(
router_id)
tags = p_utils.get_tags(self.core_plugin,
router_id,
lb_const.LR_ROUTER_TYPE,
member.get('tenant_id'),
context.project_name)
tags.append(p_utils.get_service_lb_tag(lb['id']))
lb_name = p_utils.get_service_lb_name(lb, router_id)
# Validate that there is no other LB on this router
# as NSX does not allow it
with p_utils.get_lb_rtr_lock(router_id):
if self.core_plugin.service_router_has_loadbalancers(
context.elevated(), router_id):
completor(success=False)
msg = (_('Cannot attach a loadbalancer %(lb_id)s on '
'router %(router)s, as it already has a '
'loadbalancer') %
{'lb_id': lb['id'], 'router': router_id})
raise n_exc.BadRequest(resource='lbaas-router', msg=msg)
try:
service_client.update(service['id'],
name=lb_name,
tags=tags,
connectivity_path=connectivity_path)
p_utils.update_router_lb_vip_advertisement(
context, self.core_plugin, router_id)
# Update the LB gateway policy now that we have a router
p_utils.set_allowed_cidrs_fw(self.core_plugin,
context, lb, lb['listeners'])
except Exception as e:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to set connectivity for '
'loadbalancer %(lb)s on subnet %(sub)s '
'with error %(err)s',
{'lb': lb['id'],
'sub': member['subnet_id'],
'err': e})
def create(self, context, member, completor):
pool_client = self.core_plugin.nsxpolicy.load_balancer.lb_pool
self._validate_member_lb_connectivity(context, member, completor)
if member.get('subnet_id'):
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, member['subnet_id'])
else:
network = None
if network and network.get('router:external'):
try:
fixed_ip = self._get_info_from_fip(context, member['address'])
except n_exc.BadRequest:
with excutils.save_and_reraise_exception():
completor(success=False)
else:
fixed_ip = member['address']
pool_id = member['pool']['id']
try:
pool_client.create_pool_member_and_add_to_pool(
pool_id, fixed_ip,
port=member['protocol_port'],
display_name=member['name'][:218] + '_' + member['id'],
weight=member['weight'],
backup_member=member.get('backup', False),
admin_state=_translate_member_state(member['admin_state_up']))
except Exception as e:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to create member %(member)s on pool %(pool)s'
': %(err)s',
{'member': member['id'],
'pool': pool_id, 'err': e})
completor(success=True)
def update(self, context, old_member, new_member, completor):
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, new_member['subnet_id'])
if network and network.get('router:external'):
try:
fixed_ip = self._get_info_from_fip(
context, new_member['address'])
except n_exc.BadRequest:
with excutils.save_and_reraise_exception():
completor(success=False)
else:
fixed_ip = new_member['address']
pool_id = old_member['pool']['id']
pool_client = self.core_plugin.nsxpolicy.load_balancer.lb_pool
try:
pool_client.update_pool_member(
pool_id, fixed_ip, port=new_member['protocol_port'],
display_name=new_member['name'][:219] + '_' + new_member['id'],
weight=new_member['weight'],
backup_member=new_member.get('backup', False),
admin_state=_translate_member_state(
new_member['admin_state_up']))
except Exception as e:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to update member %(member)s on pool %(pool)s'
': %(err)s',
{'member': new_member['id'],
'pool': pool_id, 'err': e})
completor(success=True)
def delete(self, context, member, completor):
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, member['subnet_id'])
if network and network.get('router:external'):
try:
fixed_ip = self._get_info_from_fip(context, member['address'])
except n_exc.BadRequest:
with excutils.save_and_reraise_exception():
completor(success=False)
else:
fixed_ip = member['address']
pool_id = member['pool']['id']
pool_client = self.core_plugin.nsxpolicy.load_balancer.lb_pool
try:
pool_client.remove_pool_member(
pool_id, fixed_ip, port=member['protocol_port'])
except Exception as e:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to create member %(member)s on pool %(pool)s'
': %(err)s',
{'member': member['id'],
'pool': pool_id, 'err': e})
completor(success=True)
def delete_cascade(self, context, member, completor):
# No action should be taken on members delete cascade
pass