# 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