diff --git a/vmware_nsx/services/lbaas/nsx_v/lbaas_common.py b/vmware_nsx/services/lbaas/nsx_v/lbaas_common.py index 6b71c63907..bd5e1d7551 100644 --- a/vmware_nsx/services/lbaas/nsx_v/lbaas_common.py +++ b/vmware_nsx/services/lbaas/nsx_v/lbaas_common.py @@ -33,15 +33,20 @@ def get_lb_resource_id(lb_id): return ('lbaas-' + lb_id)[:36] -def get_lbaas_edge_id(context, plugin, lb_id, vip_addr, subnet_id, tenant_id): - subnet = plugin.get_subnet(context, subnet_id) +def get_lb_interface(context, plugin, lb_id, subnet_id): + filters = {'fixed_ips': {'subnet_id': [subnet_id]}, + 'device_id': [lb_id], + 'device_owner': [constants.DEVICE_OWNER_NEUTRON_PREFIX + 'LB']} + + lb_ports = plugin.get_ports(context.elevated(), filters=filters) + return lb_ports + + +def create_lb_interface(context, plugin, lb_id, subnet_id, tenant_id, + vip_addr=None, subnet=None): + if not subnet: + subnet = plugin.get_subnet(context, subnet_id) network_id = subnet.get('network_id') - availability_zone = plugin.get_network_az(context, network_id) - - resource_id = get_lb_resource_id(lb_id) - - edge_id = plugin.edge_manager.allocate_lb_edge_appliance( - context, resource_id, availability_zone=availability_zone) port_dict = {'name': 'lb_if-' + lb_id, 'admin_state_up': True, @@ -55,18 +60,46 @@ def get_lbaas_edge_id(context, plugin, lb_id, vip_addr, subnet_id, tenant_id): port = plugin.base_create_port(context, {'port': port_dict}) ip_addr = port['fixed_ips'][0]['ip_address'] net = netaddr.IPNetwork(subnet['cidr']) + resource_id = get_lb_resource_id(lb_id) address_groups = [{'primaryAddress': ip_addr, 'subnetPrefixLength': str(net.prefixlen), - 'subnetMask': str(net.netmask), - 'secondaryAddresses': { - 'type': 'secondary_addresses', - 'ipAddress': [vip_addr]} - }] + 'subnetMask': str(net.netmask)}] + + if vip_addr: + address_groups[0]['secondaryAddresses'] = { + 'type': 'secondary_addresses', 'ipAddress': [vip_addr]} + edge_utils.update_internal_interface( plugin.nsx_v, context, resource_id, network_id, address_groups) + +def delete_lb_interface(context, plugin, lb_id, subnet_id): + resource_id = get_lb_resource_id(lb_id) + subnet = plugin.get_subnet(context, subnet_id) + network_id = subnet.get('network_id') + lb_ports = get_lb_interface(context, plugin, lb_id, subnet_id) + for lb_port in lb_ports: + plugin.delete_port(context, lb_port['id']) + + edge_utils.delete_interface(plugin.nsx_v, context, resource_id, network_id, + dist=False) + + +def get_lbaas_edge_id(context, plugin, lb_id, vip_addr, subnet_id, tenant_id): + subnet = plugin.get_subnet(context, subnet_id) + network_id = subnet.get('network_id') + availability_zone = plugin.get_network_az(context, network_id) + + resource_id = get_lb_resource_id(lb_id) + + edge_id = plugin.edge_manager.allocate_lb_edge_appliance( + context, resource_id, availability_zone=availability_zone) + + create_lb_interface(context, plugin, lb_id, subnet_id, tenant_id, + vip_addr=vip_addr, subnet=subnet) + gw_ip = subnet.get('gateway_ip') if gw_ip: plugin.nsx_v.update_routes(edge_id, gw_ip, []) diff --git a/vmware_nsx/services/lbaas/nsx_v/v2/member_mgr.py b/vmware_nsx/services/lbaas/nsx_v/v2/member_mgr.py index 98bd7547a1..ca1f7ff95d 100644 --- a/vmware_nsx/services/lbaas/nsx_v/v2/member_mgr.py +++ b/vmware_nsx/services/lbaas/nsx_v/v2/member_mgr.py @@ -52,6 +52,13 @@ class EdgeMemberManager(base_mgr.EdgeLoadbalancerBaseManager): edge_id = lb_binding['edge_id'] edge_pool_id = pool_binding['edge_pool_id'] with locking.LockManager.get_lock(edge_id): + # Verify that Edge appliance is connected to the member's subnet + if not lb_common.get_lb_interface( + context, self.core_plugin, lb_id, member.subnet_id): + lb_common.create_lb_interface( + context, self.core_plugin, lb_id, member.subnet_id, + member.tenant_id) + edge_pool = self.vcns.get_pool(edge_id, edge_pool_id)[1] edge_member = { 'ipAddress': member.address, @@ -137,6 +144,19 @@ class EdgeMemberManager(base_mgr.EdgeLoadbalancerBaseManager): edge_pool_id = pool_binding['edge_pool_id'] with locking.LockManager.get_lock(edge_id): + # we should remove LB subnet interface if no members are attached + # and this is not the LB's VIP interface + remove_interface = True + if member.subnet_id == member.pool.loadbalancer.vip_subnet_id: + remove_interface = False + else: + for m in member.pool.members: + if m.subnet_id == member.subnet_id and m.id != member.id: + remove_interface = False + if remove_interface: + lb_common.delete_lb_interface(context, self.core_plugin, lb_id, + member.subnet_id) + edge_pool = self.vcns.get_pool(edge_id, edge_pool_id)[1] for i, m in enumerate(edge_pool['member']): diff --git a/vmware_nsx/tests/unit/nsx_v/test_edge_loadbalancer_driver_v2.py b/vmware_nsx/tests/unit/nsx_v/test_edge_loadbalancer_driver_v2.py index 05b776f779..3cb5eb6a92 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_edge_loadbalancer_driver_v2.py +++ b/vmware_nsx/tests/unit/nsx_v/test_edge_loadbalancer_driver_v2.py @@ -112,7 +112,8 @@ class BaseTestEdgeLbaasV2(base.BaseTestCase): None, 'HTTP', 'ROUND_ROBIN', loadbalancer_id=LB_ID, listener=self.listener, - listeners=[self.listener]) + listeners=[self.listener], + loadbalancer=self.lb) self.member = lb_models.Member(MEMBER_ID, LB_TENANT_ID, POOL_ID, MEMBER_ADDRESS, 80, 1, pool=self.pool) self.hm = lb_models.HealthMonitor(HM_ID, LB_TENANT_ID, 'PING', 3, 3, @@ -510,6 +511,10 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2): ) as mock_get_pool_binding, \ mock.patch.object(self.edge_driver.vcns, 'get_pool' ) as mock_get_pool, \ + mock.patch.object(self.core_plugin, 'get_ports' + ) as mock_get_ports, \ + mock.patch.object(lb_common, 'delete_lb_interface' + ) as mock_del_lb_iface, \ mock.patch.object(self.edge_driver.vcns, 'update_pool' ) as mock_update_pool: mock_get_lb_binding.return_value = LB_BINDING @@ -517,12 +522,14 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2): edge_pool_def = EDGE_POOL_DEF.copy() edge_pool_def['member'] = [EDGE_MEMBER_DEF] mock_get_pool.return_value = (None, edge_pool_def) - + mock_get_ports.return_value = [] self.edge_driver.member.delete(self.context, self.member) edge_pool_def['member'] = [] mock_update_pool.assert_called_with( LB_EDGE_ID, EDGE_POOL_ID, edge_pool_def) + mock_del_lb_iface.assert_called_with( + self.context, self.core_plugin, LB_ID, None) mock_successful_completion = ( self.lbv2_driver.member.successful_completion) mock_successful_completion.assert_called_with(self.context,