def test_add_acls_with_sec_group(self): expected_acls = [] expected_acls += ovn_acl.drop_all_ip_traffic_for_port( self.fake_port_sg) expected_acls += ovn_acl.add_acl_dhcp( self.fake_port_sg, self.fake_subnet) sg_rule_acl = ovn_acl.add_sg_rule_acl_for_port( self.fake_port_sg, self.fake_sg_rule, 'outport == "' + self.fake_port_sg['id'] + '" ' + '&& ip4 && ip4.src == 0.0.0.0/0 ' + '&& tcp && tcp.dst == 22') expected_acls.append(sg_rule_acl) # Test with caches acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), self.fake_port_sg, self.sg_cache, self.sg_ports_cache, self.subnet_cache) self.assertEqual(expected_acls, acls) # Test without caches with mock.patch('neutron.db.db_base_plugin_v2.' 'NeutronDbPluginV2.get_subnet', return_value=self.fake_subnet), \ mock.patch('neutron.db.securitygroups_db.' 'SecurityGroupDbMixin.get_security_group', return_value=self.fake_sg): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), self.fake_port_sg, {}, {}, {}) self.assertEqual(expected_acls, acls)
def _test_add_acls_with_sec_group_helper(self, native_dhcp=True): fake_port_sg = fakes.FakePort.create_one_port( attrs={ 'security_groups': [self.fake_sg['id']], 'fixed_ips': [{ 'subnet_id': self.fake_subnet['id'], 'ip_address': '10.10.10.20' }] }).info() expected_acls = [] expected_acls += ovn_acl.drop_all_ip_traffic_for_port(fake_port_sg) if not native_dhcp: expected_acls += ovn_acl.add_acl_dhcp(fake_port_sg, self.fake_subnet) sg_rule_acl = ovn_acl.add_sg_rule_acl_for_port( fake_port_sg, self.fake_sg_rule, 'outport == "' + fake_port_sg['id'] + '" ' + '&& ip4 && ip4.src == 0.0.0.0/0 ' + '&& tcp && tcp.dst == 22') expected_acls.append(sg_rule_acl) # Test with caches acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual(expected_acls, acls) # Test without caches with mock.patch('neutron.db.db_base_plugin_v2.' 'NeutronDbPluginV2.get_subnet', return_value=self.fake_subnet), \ mock.patch('neutron.db.securitygroups_db.' 'SecurityGroupDbMixin.get_security_group', return_value=self.fake_sg): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, {}, {}) self.assertEqual(expected_acls, acls) # Test with security groups disabled with mock.patch('networking_ovn.common.acl.is_sg_enabled', return_value=False): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual([], acls) # Test with multiple fixed IPs on the same subnet. fake_port_sg['fixed_ips'].append({ 'subnet_id': self.fake_subnet['id'], 'ip_address': '10.10.10.21' }) acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual(expected_acls, acls)
def _update_port_in_ovn(self, original_port, port, ovn_port_info): external_ids = { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} admin_context = n_context.get_admin_context() sg_cache = {} sg_ports_cache = {} subnet_cache = {} with self._ovn.transaction(check_error=True) as txn: txn.add(self._ovn.set_lport(lport_name=port['id'], addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, type=ovn_port_info.type, options=ovn_port_info.options, enabled=port['admin_state_up'], port_security=ovn_port_info.port_security)) # Note that the ovsdb IDL suppresses the transaction down to what # has actually changed. txn.add(self._ovn.delete_acl( utils.ovn_name(port['network_id']), port['id'])) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, sg_ports_cache, subnet_cache) for acl in acls_new: txn.add(self._ovn.add_acl(**acl)) # Refresh remote security groups for changed security groups old_sg_ids = set(original_port.get('security_groups', [])) new_sg_ids = set(port.get('security_groups', [])) detached_sg_ids = old_sg_ids - new_sg_ids attached_sg_ids = new_sg_ids - old_sg_ids if (len(port.get('fixed_ips')) == 0 and len(original_port.get('fixed_ips')) == 0): # No need to process remote security group if the port # didn't have any IP Addresses. return port for sg_id in (attached_sg_ids | detached_sg_ids): ovn_acl.refresh_remote_security_group( self._plugin, admin_context, self._ovn, sg_id, sg_cache, sg_ports_cache, subnet_cache, [port['id']]) # Refresh remote security groups if remote_group_match_ip is set if original_port.get('fixed_ips') != port.get('fixed_ips'): # We have refreshed attached and detached security groups, so # now we only need to take care of unchanged security groups. unchanged_sg_ids = new_sg_ids & old_sg_ids for sg_id in unchanged_sg_ids: ovn_acl.refresh_remote_security_group( self._plugin, admin_context, self._ovn, sg_id, sg_cache, sg_ports_cache, subnet_cache, [port['id']])
def _validate_acls(self, should_match=True): # Get the neutron DB ACLs. db_acls = [] sg_cache = {} subnet_cache = {} for db_port in self._list('ports')['ports']: acls = acl_utils.add_acls(self.plugin, context.get_admin_context(), db_port, sg_cache, subnet_cache) for acl in acls: acl.pop('lport') acl.pop('lswitch') db_acls.append(acl) # Get the list of ACLs stored in the OVN plugin IDL. _plugin_nb_ovn = self.mech_driver._nb_ovn plugin_acls = [] for row in _plugin_nb_ovn._tables['Logical_Switch'].rows.values(): for acl in getattr(row, 'acls', []): plugin_acls.append(self._build_acl_to_compare(acl)) # Get the list of ACLs stored in the OVN monitor IDL. monitor_nb_ovn = self.monitor_nb_db_idl monitor_acls = [] for row in monitor_nb_ovn.tables['Logical_Switch'].rows.values(): for acl in getattr(row, 'acls', []): monitor_acls.append(self._build_acl_to_compare(acl)) if should_match: self.assertItemsEqual(db_acls, plugin_acls) self.assertItemsEqual(db_acls, monitor_acls) else: self.assertRaises(AssertionError, self.assertItemsEqual, db_acls, plugin_acls) self.assertRaises(AssertionError, self.assertItemsEqual, db_acls, monitor_acls)
def create_port_in_ovn(self, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} lswitch_name = utils.ovn_name(port['network_id']) admin_context = n_context.get_admin_context() sg_cache = {} sg_ports_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: # The lport_name *must* be neutron port['id']. It must match the # iface-id set in the Interfaces table of the Open_vSwitch # database which nova sets to be the port ID. txn.add( self._nb_ovn.create_lswitch_port( lport_name=port['id'], lswitch_name=lswitch_name, addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, enabled=port.get('admin_state_up'), options=ovn_port_info.options, type=ovn_port_info.type, port_security=ovn_port_info.port_security)) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, sg_ports_cache, subnet_cache) for acl in acls_new: txn.add(self._nb_ovn.add_acl(**acl)) if len(port.get('fixed_ips')): for sg_id in port.get('security_groups', []): ovn_acl.refresh_remote_security_group( self._plugin, admin_context, self._nb_ovn, sg_id, sg_cache, sg_ports_cache, subnet_cache, [port['id']])
def create_port_in_ovn(self, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} lswitch_name = utils.ovn_name(port['network_id']) admin_context = n_context.get_admin_context() sg_cache = {} sg_ports_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: # The lport_name *must* be neutron port['id']. It must match the # iface-id set in the Interfaces table of the Open_vSwitch # database which nova sets to be the port ID. txn.add(self._nb_ovn.create_lswitch_port( lport_name=port['id'], lswitch_name=lswitch_name, addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, enabled=port.get('admin_state_up'), options=ovn_port_info.options, type=ovn_port_info.type, port_security=ovn_port_info.port_security)) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, sg_ports_cache, subnet_cache) for acl in acls_new: txn.add(self._nb_ovn.add_acl(**acl)) if len(port.get('fixed_ips')): for sg_id in port.get('security_groups', []): ovn_acl.refresh_remote_security_group( self._plugin, admin_context, self._nb_ovn, sg_id, sg_cache, sg_ports_cache, subnet_cache, [port['id']])
def sync_acls(self, ctx): """Sync ACLs between neutron and NB. @param ctx: neutron context @type ctx: object of type neutron.context.Context @var db_ports: List of ports from neutron DB @var neutron_acls: neutron dictionary of port vs list-of-acls @var nb_acls: NB dictionary of port vs list-of-acls @var subnet_cache: cache for subnets @return: Nothing """ LOG.debug('ACL-SYNC: started @ %s' % str(datetime.now())) db_ports = {} for port in self.core_plugin.get_ports(ctx): db_ports[port['id']] = port sg_cache = {} subnet_cache = {} neutron_acls = {} for port_id, port in six.iteritems(db_ports): if port['security_groups']: acl_list = acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, subnet_cache) if port_id in neutron_acls: neutron_acls[port_id].extend(acl_list) else: neutron_acls[port_id] = acl_list nb_acls = self.get_acls(ctx) self.remove_common_acls(neutron_acls, nb_acls) LOG.debug('ACLs-to-be-added %d ACLs-to-be-removed %d' % (len(list(itertools.chain(*six.itervalues(neutron_acls)))), len(list(itertools.chain(*six.itervalues(nb_acls)))))) if self.mode == SYNC_MODE_REPAIR: LOG.debug('ACL-SYNC: transaction started @ %s' % str(datetime.now())) with self.ovn_api.transaction(check_error=True) as txn: for acla in list( itertools.chain(*six.itervalues(neutron_acls))): txn.add(self.ovn_api.add_acl(**acla)) for aclr in list(itertools.chain(*six.itervalues(nb_acls))): # Both lswitch and lport aren't needed within the ACL. lswitchr = aclr.pop('lswitch').replace('neutron-', '') lportr = aclr.pop('lport') aclr_dict = {lportr: aclr} txn.add( self.ovn_api.update_acls([lswitchr], [lportr], aclr_dict, need_compare=False, is_add_acl=False)) LOG.debug('ACL-SYNC: transaction finished @ %s' % str(datetime.now()))
def _test_add_acls_with_sec_group_helper(self, native_dhcp=True): fake_port_sg = fakes.FakePort.create_one_port( attrs={ "security_groups": [self.fake_sg["id"]], "fixed_ips": [{"subnet_id": self.fake_subnet["id"], "ip_address": "10.10.10.20"}], } ).info() expected_acls = [] expected_acls += ovn_acl.drop_all_ip_traffic_for_port(fake_port_sg) if not native_dhcp: expected_acls += ovn_acl.add_acl_dhcp(fake_port_sg, self.fake_subnet) sg_rule_acl = ovn_acl.add_sg_rule_acl_for_port( fake_port_sg, self.fake_sg_rule, 'outport == "' + fake_port_sg["id"] + '" ' + "&& ip4 && ip4.src == 0.0.0.0/0 " + "&& tcp && tcp.dst == 22", ) expected_acls.append(sg_rule_acl) # Test with caches acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual(expected_acls, acls) # Test without caches with mock.patch( "neutron.db.db_base_plugin_v2." "NeutronDbPluginV2.get_subnet", return_value=self.fake_subnet ), mock.patch( "neutron.db.securitygroups_db." "SecurityGroupDbMixin.get_security_group", return_value=self.fake_sg ): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, {}, {}) self.assertEqual(expected_acls, acls) # Test with security groups disabled with mock.patch("networking_ovn.common.acl.is_sg_enabled", return_value=False): acls = ovn_acl.add_acls( self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache ) self.assertEqual([], acls) # Test with multiple fixed IPs on the same subnet. fake_port_sg["fixed_ips"].append({"subnet_id": self.fake_subnet["id"], "ip_address": "10.10.10.21"}) acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual(expected_acls, acls)
def _update_port_in_ovn(self, original_port, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} admin_context = n_context.get_admin_context() sg_cache = {} sg_ports_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: txn.add( self._nb_ovn.set_lswitch_port( lport_name=port['id'], addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, type=ovn_port_info.type, options=ovn_port_info.options, enabled=port['admin_state_up'], port_security=ovn_port_info.port_security)) # Note that the ovsdb IDL suppresses the transaction down to what # has actually changed. txn.add( self._nb_ovn.delete_acl(utils.ovn_name(port['network_id']), port['id'])) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, sg_ports_cache, subnet_cache) for acl in acls_new: txn.add(self._nb_ovn.add_acl(**acl)) # Refresh remote security groups for changed security groups old_sg_ids = set(original_port.get('security_groups', [])) new_sg_ids = set(port.get('security_groups', [])) detached_sg_ids = old_sg_ids - new_sg_ids attached_sg_ids = new_sg_ids - old_sg_ids if (len(port.get('fixed_ips')) == 0 and len(original_port.get('fixed_ips')) == 0): # No need to process remote security group if the port # didn't have any IP Addresses. return port for sg_id in (attached_sg_ids | detached_sg_ids): ovn_acl.refresh_remote_security_group(self._plugin, admin_context, self._nb_ovn, sg_id, sg_cache, sg_ports_cache, subnet_cache, [port['id']]) # Refresh remote security groups if remote_group_match_ip is set if original_port.get('fixed_ips') != port.get('fixed_ips'): # We have refreshed attached and detached security groups, so # now we only need to take care of unchanged security groups. unchanged_sg_ids = new_sg_ids & old_sg_ids for sg_id in unchanged_sg_ids: ovn_acl.refresh_remote_security_group( self._plugin, admin_context, self._nb_ovn, sg_id, sg_cache, sg_ports_cache, subnet_cache, [port['id']])
def create_port_in_ovn(self, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port["name"]} lswitch_name = utils.ovn_name(port["network_id"]) admin_context = n_context.get_admin_context() sg_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: # The lport_name *must* be neutron port['id']. It must match the # iface-id set in the Interfaces table of the Open_vSwitch # database which nova sets to be the port ID. txn.add( self._nb_ovn.create_lswitch_port( lport_name=port["id"], lswitch_name=lswitch_name, addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, enabled=port.get("admin_state_up"), options=ovn_port_info.options, type=ovn_port_info.type, port_security=ovn_port_info.port_security, dhcpv4_options=ovn_port_info.dhcpv4_options, ) ) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, subnet_cache) for acl in acls_new: txn.add(self._nb_ovn.add_acl(**acl)) sg_ids = port.get("security_groups", []) if port.get("fixed_ips") and sg_ids: addresses = ovn_acl.acl_port_ips(port) # NOTE(rtheis): Fail port creation if the address set doesn't # exist. This prevents ports from being created on any security # groups out-of-sync between neutron and OVN. for sg_id in sg_ids: for ip_version in addresses: if addresses[ip_version]: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=addresses[ip_version], addrs_remove=None, if_exists=False, ) )
def create_port_in_ovn(self, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} lswitch_name = utils.ovn_name(port['network_id']) admin_context = n_context.get_admin_context() sg_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: # The lport_name *must* be neutron port['id']. It must match the # iface-id set in the Interfaces table of the Open_vSwitch # database which nova sets to be the port ID. txn.add( self._nb_ovn.create_lswitch_port( lport_name=port['id'], lswitch_name=lswitch_name, addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, enabled=port.get('admin_state_up'), options=ovn_port_info.options, type=ovn_port_info.type, port_security=ovn_port_info.port_security, dhcpv4_options=ovn_port_info.dhcpv4_options)) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, subnet_cache) for acl in acls_new: txn.add(self._nb_ovn.add_acl(**acl)) sg_ids = port.get('security_groups', []) if port.get('fixed_ips') and sg_ids: addresses = ovn_acl.acl_port_ips(port) # NOTE(rtheis): Fail port creation if the address set doesn't # exist. This prevents ports from being created on any security # groups out-of-sync between neutron and OVN. for sg_id in sg_ids: for ip_version in addresses: if addresses[ip_version]: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name( sg_id, ip_version), addrs_add=addresses[ip_version], addrs_remove=None, if_exists=False))
def test_sg_disabled(self): sg = fakes.FakeSecurityGroup.create_one_security_group().info() port = fakes.FakePort.create_one_port({ 'security_groups': [sg['id']] }).info() with mock.patch('networking_ovn.common.acl.is_sg_enabled', return_value=False): acl_list = ovn_acl.add_acls(self.plugin, self.admin_context, port, {}, {}, self.driver._ovn) self.assertEqual([], acl_list) ovn_acl.update_acls_for_security_group(self.plugin, self.admin_context, self.driver._ovn, sg['id'], None) self.driver._ovn.update_acls.assert_not_called() addresses = ovn_acl.acl_port_ips(port) self.assertEqual({'ip4': [], 'ip6': []}, addresses)
def test_sg_disabled(self): sg = fakes.FakeSecurityGroup.create_one_security_group().info() port = fakes.FakePort.create_one_port({ 'security_groups': [sg['id']] }).info() with mock.patch('networking_ovn.common.acl.is_sg_enabled', return_value=False): acl_list = ovn_acl.add_acls(self.plugin, self.admin_context, port, {}, {}) self.assertEqual([], acl_list) ovn_acl.update_acls_for_security_group(self.plugin, self.admin_context, self.driver._ovn, sg['id']) self.driver._ovn.update_acls.assert_not_called() addresses = ovn_acl.acl_port_ips(port) self.assertEqual({'ip4': [], 'ip6': []}, addresses)
def _validate_acls(self, should_match=True): # Get the neutron DB ACLs. db_acls = [] sg_cache = {} subnet_cache = {} for db_port in self._list('ports')['ports']: acls = acl_utils.add_acls(self.plugin, context.get_admin_context(), db_port, sg_cache, subnet_cache) for acl in acls: acl.pop('lport') acl.pop('lswitch') db_acls.append(acl) # Get the list of ACLs stored in the OVN plugin IDL. _plugin_nb_ovn = self.mech_driver._nb_ovn plugin_acls = [] for row in _plugin_nb_ovn._tables['Logical_Switch'].rows.values(): for acl in getattr(row, 'acls', []): plugin_acls.append(self._build_acl_to_compare(acl)) # Get the list of ACLs stored in the OVN monitor IDL. monitor_nb_ovn = self.monitor_nb_db_idl monitor_acls = [] for row in monitor_nb_ovn.tables['Logical_Switch'].rows.values(): for acl in getattr(row, 'acls', []): monitor_acls.append(self._build_acl_to_compare(acl)) if should_match: self.assertItemsEqual(db_acls, plugin_acls) self.assertItemsEqual(db_acls, monitor_acls) else: self.assertRaises( AssertionError, self.assertItemsEqual, db_acls, plugin_acls) self.assertRaises( AssertionError, self.assertItemsEqual, db_acls, monitor_acls)
def _update_port_in_ovn(self, original_port, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port["name"]} admin_context = n_context.get_admin_context() sg_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: txn.add( self._nb_ovn.set_lswitch_port( lport_name=port["id"], addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, type=ovn_port_info.type, options=ovn_port_info.options, enabled=port["admin_state_up"], port_security=ovn_port_info.port_security, dhcpv4_options=ovn_port_info.dhcpv4_options, ) ) # Determine if security groups or fixed IPs are updated. old_sg_ids = set(original_port.get("security_groups", [])) new_sg_ids = set(port.get("security_groups", [])) detached_sg_ids = old_sg_ids - new_sg_ids attached_sg_ids = new_sg_ids - old_sg_ids is_fixed_ips_updated = original_port.get("fixed_ips") != port.get("fixed_ips") # Refresh ACLs for changed security groups or fixed IPs. if detached_sg_ids or attached_sg_ids or is_fixed_ips_updated: # Note that update_acls will compare the port's ACLs to # ensure only the necessary ACLs are added and deleted # on the transaction. acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, subnet_cache) txn.add( self._nb_ovn.update_acls([port["network_id"]], [port], {port["id"]: acls_new}, need_compare=True) ) # Refresh address sets for changed security groups or fixed IPs. if len(port.get("fixed_ips")) != 0 or len(original_port.get("fixed_ips")) != 0: addresses = ovn_acl.acl_port_ips(port) addresses_old = ovn_acl.acl_port_ips(original_port) # Add current addresses to attached security groups. for sg_id in attached_sg_ids: for ip_version in addresses: if addresses[ip_version]: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=addresses[ip_version], addrs_remove=None, ) ) # Remove old addresses from detached security groups. for sg_id in detached_sg_ids: for ip_version in addresses_old: if addresses_old[ip_version]: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=None, addrs_remove=addresses_old[ip_version], ) ) if is_fixed_ips_updated: # We have refreshed address sets for attached and detached # security groups, so now we only need to take care of # unchanged security groups. unchanged_sg_ids = new_sg_ids & old_sg_ids for sg_id in unchanged_sg_ids: for ip_version in addresses: addr_add = (set(addresses[ip_version]) - set(addresses_old[ip_version])) or None addr_remove = (set(addresses_old[ip_version]) - set(addresses[ip_version])) or None if addr_add or addr_remove: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=addr_add, addrs_remove=addr_remove, ) ) if not ovn_port_info.dhcpv4_options: # Check if the DHCP_Options row exist for this port. # We need to delete it as it is no longer referenced by this # port. cmd = self._get_delete_lsp_dhcpv4_options_cmd(port) if cmd: txn.add(cmd)
def sync_acls(self, ctx): """Sync ACLs between neutron and NB. @param ctx: neutron context @type ctx: object of type neutron.context.Context @var db_ports: List of ports from neutron DB @var neutron_acls: neutron dictionary of port vs list-of-acls @var nb_acls: NB dictionary of port vs list-of-acls @var subnet_cache: cache for subnets @return: Nothing """ LOG.debug('ACL-SYNC: started @ %s' % str(datetime.now())) db_ports = {} for port in self.core_plugin.get_ports(ctx): db_ports[port['id']] = port sg_cache = {} subnet_cache = {} neutron_acls = {} for port_id, port in six.iteritems(db_ports): if port['security_groups']: acl_list = acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, subnet_cache) if port_id in neutron_acls: neutron_acls[port_id].extend(acl_list) else: neutron_acls[port_id] = acl_list nb_acls = self.get_acls(ctx) self.remove_common_acls(neutron_acls, nb_acls) num_acls_to_add = \ len(list(itertools.chain(*six.itervalues(neutron_acls)))) num_acls_to_remove = \ len(list(itertools.chain(*six.itervalues(nb_acls)))) if 0 != num_acls_to_add or 0 != num_acls_to_remove: LOG.warning( _LW('ACLs-to-be-added %(add)d ' 'ACLs-to-be-removed %(remove)d'), { 'add': num_acls_to_add, 'remove': num_acls_to_remove }) if self.mode == SYNC_MODE_REPAIR: with self.ovn_api.transaction(check_error=True) as txn: for acla in list( itertools.chain(*six.itervalues(neutron_acls))): LOG.warning( _LW('ACL found in Neutron but not in ' 'OVN DB for port %s'), acla['lport']) txn.add(self.ovn_api.add_acl(**acla)) # TODO(rtheis): Each delete must be done in a separate # transaction until bug 1629099 is fixed. Otherwise, ACLs may # not be deleted because of previous row mutations on a logical # switch. for aclr in list(itertools.chain(*six.itervalues(nb_acls))): # Both lswitch and lport aren't needed within the ACL. lswitchr = aclr.pop('lswitch').replace('neutron-', '') lportr = aclr.pop('lport') aclr_dict = {lportr: aclr} LOG.warning( _LW('ACLs found in OVN DB but not in ' 'Neutron for port %s'), lportr) self.ovn_api.update_acls( [lswitchr], [lportr], aclr_dict, need_compare=False, is_add_acl=False).execute(check_error=True) LOG.debug('ACL-SYNC: finished @ %s' % str(datetime.now()))
def _update_port_in_ovn(self, original_port, port, ovn_port_info): external_ids = { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} admin_context = n_context.get_admin_context() sg_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: txn.add(self._nb_ovn.set_lswitch_port( lport_name=port['id'], addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, type=ovn_port_info.type, options=ovn_port_info.options, enabled=port['admin_state_up'], port_security=ovn_port_info.port_security)) # Determine if security groups or fixed IPs are updated. old_sg_ids = set(original_port.get('security_groups', [])) new_sg_ids = set(port.get('security_groups', [])) detached_sg_ids = old_sg_ids - new_sg_ids attached_sg_ids = new_sg_ids - old_sg_ids is_fixed_ips_updated = \ original_port.get('fixed_ips') != port.get('fixed_ips') # Refresh ACLs for changed security groups or fixed IPs. if detached_sg_ids or attached_sg_ids or is_fixed_ips_updated: # Note that update_acls will compare the port's ACLs to # ensure only the necessary ACLs are added and deleted # on the transaction. acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, subnet_cache) txn.add(self._nb_ovn.update_acls([port['network_id']], [port], {port['id']: acls_new}, need_compare=True)) # Refresh address sets for changed security groups or fixed IPs. if (len(port.get('fixed_ips')) != 0 or len(original_port.get('fixed_ips')) != 0): addresses = ovn_acl.acl_port_ips(port) addresses_old = ovn_acl.acl_port_ips(original_port) # Add current addresses to attached security groups. for sg_id in attached_sg_ids: for ip_version in addresses: if addresses[ip_version]: txn.add(self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=addresses[ip_version], addrs_remove=None)) # Remove old addresses from detached security groups. for sg_id in detached_sg_ids: for ip_version in addresses_old: if addresses_old[ip_version]: txn.add(self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=None, addrs_remove=addresses_old[ip_version])) if is_fixed_ips_updated: # We have refreshed address sets for attached and detached # security groups, so now we only need to take care of # unchanged security groups. unchanged_sg_ids = new_sg_ids & old_sg_ids for sg_id in unchanged_sg_ids: for ip_version in addresses: addr_add = (set(addresses[ip_version]) - set(addresses_old[ip_version])) or None addr_remove = (set(addresses_old[ip_version]) - set(addresses[ip_version])) or None if addr_add or addr_remove: txn.add(self._nb_ovn.update_address_set( name=utils.ovn_addrset_name( sg_id, ip_version), addrs_add=addr_add, addrs_remove=addr_remove))
def test_add_acls_no_sec_group(self): acls = acl_utils.add_acls(self.plugin, self.context, port={'security_groups': []}, sg_cache={}, sg_ports_cache={}, subnet_cache={}) self.assertEqual(acls, [])
def _update_port_in_ovn(self, original_port, port, ovn_port_info): external_ids = { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} admin_context = n_context.get_admin_context() sg_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: txn.add(self._nb_ovn.set_lswitch_port( lport_name=port['id'], addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, type=ovn_port_info.type, options=ovn_port_info.options, enabled=port['admin_state_up'], port_security=ovn_port_info.port_security)) # Note that the ovsdb IDL suppresses the transaction down to what # has actually changed. txn.add(self._nb_ovn.delete_acl( utils.ovn_name(port['network_id']), port['id'])) acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, subnet_cache) for acl in acls_new: txn.add(self._nb_ovn.add_acl(**acl)) # Refresh remote security groups for changed security groups old_sg_ids = set(original_port.get('security_groups', [])) new_sg_ids = set(port.get('security_groups', [])) detached_sg_ids = old_sg_ids - new_sg_ids attached_sg_ids = new_sg_ids - old_sg_ids if (len(port.get('fixed_ips')) != 0 or len(original_port.get('fixed_ips')) != 0): addresses = ovn_acl.acl_port_ips(port) addresses_old = ovn_acl.acl_port_ips(original_port) # Add current addresses to attached security groups. for sg_id in attached_sg_ids: for ip_version in addresses: if addresses[ip_version]: txn.add(self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=addresses[ip_version], addrs_remove=None)) # Remove old addresses from detached security groups. for sg_id in detached_sg_ids: for ip_version in addresses_old: if addresses_old[ip_version]: txn.add(self._nb_ovn.update_address_set( name=utils.ovn_addrset_name(sg_id, ip_version), addrs_add=None, addrs_remove=addresses_old[ip_version])) if original_port.get('fixed_ips') != port.get('fixed_ips'): # We have refreshed address sets for attached and detached # security groups, so now we only need to take care of # unchanged security groups. unchanged_sg_ids = new_sg_ids & old_sg_ids for sg_id in unchanged_sg_ids: for ip_version in addresses: addr_add = (set(addresses[ip_version]) - set(addresses_old[ip_version])) or None addr_remove = (set(addresses_old[ip_version]) - set(addresses[ip_version])) or None if addr_add or addr_remove: txn.add(self._nb_ovn.update_address_set( name=utils.ovn_addrset_name( sg_id, ip_version), addrs_add=addr_add, addrs_remove=addr_remove))
def sync_acls(self, ctx): """Sync ACLs between neutron and NB. @param ctx: neutron context @type ctx: object of type neutron.context.Context @var db_ports: List of ports from neutron DB @var neutron_acls: neutron dictionary of port vs list-of-acls @var nb_acls: NB dictionary of port vs list-of-acls @var subnet_cache: cache for subnets @return: Nothing """ LOG.debug('ACL-SYNC: started @ %s' % str(datetime.now())) db_ports = {} for port in self.core_plugin.get_ports(ctx): db_ports[port['id']] = port sg_cache = {} subnet_cache = {} neutron_acls = {} for port_id, port in six.iteritems(db_ports): if port['security_groups']: acl_list = acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, subnet_cache) if port_id in neutron_acls: neutron_acls[port_id].extend(acl_list) else: neutron_acls[port_id] = acl_list nb_acls = self.get_acls(ctx) self.remove_common_acls(neutron_acls, nb_acls) num_acls_to_add = \ len(list(itertools.chain(*six.itervalues(neutron_acls)))) num_acls_to_remove = \ len(list(itertools.chain(*six.itervalues(nb_acls)))) if 0 != num_acls_to_add or 0 != num_acls_to_remove: LOG.warning(_LW('ACLs-to-be-added %(add)d ' 'ACLs-to-be-removed %(remove)d'), {'add': num_acls_to_add, 'remove': num_acls_to_remove}) if self.mode == SYNC_MODE_REPAIR: with self.ovn_api.transaction(check_error=True) as txn: for acla in list(itertools.chain( *six.itervalues(neutron_acls))): LOG.warning(_LW('ACL found in Neutron but not in ' 'OVN DB for port %s'), acla['lport']) txn.add(self.ovn_api.add_acl(**acla)) with self.ovn_api.transaction(check_error=True) as txn: for aclr in list(itertools.chain(*six.itervalues(nb_acls))): # Both lswitch and lport aren't needed within the ACL. lswitchr = aclr.pop('lswitch').replace('neutron-', '') lportr = aclr.pop('lport') aclr_dict = {lportr: aclr} LOG.warning(_LW('ACLs found in OVN DB but not in ' 'Neutron for port %s'), lportr) txn.add(self.ovn_api.update_acls( [lswitchr], [lportr], aclr_dict, need_compare=False, is_add_acl=False )) LOG.debug('ACL-SYNC: finished @ %s' % str(datetime.now()))
def _test_add_acls_with_sec_group_helper(self, native_dhcp=True): fake_port_sg = fakes.FakePort.create_one_port( attrs={'security_groups': [self.fake_sg['id']], 'fixed_ips': [{'subnet_id': self.fake_subnet['id'], 'ip_address': '10.10.10.20'}]} ).info() expected_acls = [] expected_acls += ovn_acl.drop_all_ip_traffic_for_port( fake_port_sg) if not native_dhcp: expected_acls += ovn_acl.add_acl_dhcp( fake_port_sg, self.fake_subnet) sg_rule_acl = ovn_acl.add_sg_rule_acl_for_port( fake_port_sg, self.fake_sg_rule, 'outport == "' + fake_port_sg['id'] + '" ' + '&& ip4 && ip4.src == 0.0.0.0/0 ' + '&& tcp && tcp.dst == 22') expected_acls.append(sg_rule_acl) # Test with caches acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual(expected_acls, acls) # Test without caches with mock.patch('neutron.db.db_base_plugin_v2.' 'NeutronDbPluginV2.get_subnet', return_value=self.fake_subnet), \ mock.patch('neutron.db.securitygroups_db.' 'SecurityGroupDbMixin.get_security_group', return_value=self.fake_sg): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, {}, {}) self.assertEqual(expected_acls, acls) # Test with security groups disabled with mock.patch('networking_ovn.common.acl.is_sg_enabled', return_value=False): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual([], acls) # Test with multiple fixed IPs on the same subnet. fake_port_sg['fixed_ips'].append({'subnet_id': self.fake_subnet['id'], 'ip_address': '10.10.10.21'}) acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), fake_port_sg, self.sg_cache, self.subnet_cache) self.assertEqual(expected_acls, acls)
def sync_acls(self, ctx): """Sync ACLs between neutron and NB. @param ctx: neutron_lib.context @type ctx: object of type neutron_lib.context.Context @var db_ports: List of ports from neutron DB @var neutron_acls: neutron dictionary of port vs list-of-acls @var nb_acls: NB dictionary of port vs list-of-acls @var subnet_cache: cache for subnets @return: Nothing """ LOG.debug('ACL-SYNC: started @ %s' % str(datetime.now())) db_ports = {} for port in self.core_plugin.get_ports(ctx): db_ports[port['id']] = port sg_cache = {} subnet_cache = {} neutron_acls = {} for port_id, port in db_ports.items(): if utils.get_lsp_security_groups(port): acl_list = acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, subnet_cache, self.ovn_api) if port_id in neutron_acls: neutron_acls[port_id].extend(acl_list) else: neutron_acls[port_id] = acl_list nb_acls = self.get_acls(ctx) self.remove_common_acls(neutron_acls, nb_acls) num_acls_to_add = len(list(itertools.chain(*neutron_acls.values()))) num_acls_to_remove = len(list(itertools.chain(*nb_acls.values()))) if 0 != num_acls_to_add or 0 != num_acls_to_remove: LOG.warning( 'ACLs-to-be-added %(add)d ' 'ACLs-to-be-removed %(remove)d', { 'add': num_acls_to_add, 'remove': num_acls_to_remove }) if self.mode == SYNC_MODE_REPAIR: with self.ovn_api.transaction(check_error=True) as txn: for acla in list(itertools.chain(*neutron_acls.values())): LOG.warning( 'ACL found in Neutron but not in ' 'OVN DB for port %s', acla['lport']) txn.add(self.ovn_api.add_acl(**acla)) with self.ovn_api.transaction(check_error=True) as txn: for aclr in list(itertools.chain(*nb_acls.values())): # Both lswitch and lport aren't needed within the ACL. lswitchr = aclr.pop('lswitch').replace('neutron-', '') lportr = aclr.pop('lport') aclr_dict = {lportr: aclr} LOG.warning( 'ACLs found in OVN DB but not in ' 'Neutron for port %s', lportr) txn.add( self.ovn_api.update_acls([lswitchr], [lportr], aclr_dict, need_compare=False, is_add_acl=False)) LOG.debug('ACL-SYNC: finished @ %s' % str(datetime.now()))
def sync_acls(self, ctx): """Sync ACLs between neutron and NB. @param ctx: neutron context @type ctx: object of type neutron.context.Context @var db_secs: List of SGs from neutron DB @var db_ports: List of ports from neutron DB @var neutron_acls: neutron dictionary of port vs list-of-acls @var nb_acls: NB dictionary of port vs list-of-acls @var sg_ports_cache: cache for sg_ports @var subnet_cache: cache for subnets @return: Nothing """ LOG.debug('ACL-SYNC: started @ %s' % str(datetime.now())) db_secs = {} for sg in self.core_plugin.get_security_groups(ctx): db_secs[sg['id']] = sg db_ports = {} for port in self.core_plugin.get_ports(ctx): db_ports[port['id']] = port sg_cache = {} sg_ports_cache = {} subnet_cache = {} neutron_acls = {} for port_id, port in db_ports.items(): if port['security_groups']: if port_id in neutron_acls: neutron_acls[port_id].extend( acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, sg_ports_cache, subnet_cache)) else: neutron_acls[port_id] = \ acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, sg_ports_cache, subnet_cache) nb_acls = self.get_acls(ctx) self.remove_common_acls(neutron_acls, nb_acls) LOG.debug('ACLs-to-be-addded %d ACLs-to-be-removed %d' % (len(list(itertools.chain(*six.itervalues(neutron_acls)))), len(list(itertools.chain(*six.itervalues(nb_acls)))))) LOG.debug('ACL-SYNC: transaction started @ %s' % str(datetime.now())) with self.ovn_api.transaction(check_error=True) as txn: for acla in list(itertools.chain(*six.itervalues(neutron_acls))): txn.add(self.ovn_api.add_acl(**acla)) for aclr in list(itertools.chain(*six.itervalues(nb_acls))): txn.add(self.ovn_api.delete_acl(aclr['lswitch'], aclr['lport'])) LOG.debug('ACL-SYNC: transaction finished @ %s' % str(datetime.now()))
def test_add_acls_no_sec_group(self): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), self.fake_port_no_sg, {}, {}, {}) self.assertEqual([], acls)
def _update_port_in_ovn(self, original_port, port, ovn_port_info): external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']} admin_context = n_context.get_admin_context() sg_cache = {} subnet_cache = {} with self._nb_ovn.transaction(check_error=True) as txn: txn.add( self._nb_ovn.set_lswitch_port( lport_name=port['id'], addresses=ovn_port_info.addresses, external_ids=external_ids, parent_name=ovn_port_info.parent_name, tag=ovn_port_info.tag, type=ovn_port_info.type, options=ovn_port_info.options, enabled=port['admin_state_up'], port_security=ovn_port_info.port_security, dhcpv4_options=ovn_port_info.dhcpv4_options)) # Determine if security groups or fixed IPs are updated. old_sg_ids = set(original_port.get('security_groups', [])) new_sg_ids = set(port.get('security_groups', [])) detached_sg_ids = old_sg_ids - new_sg_ids attached_sg_ids = new_sg_ids - old_sg_ids is_fixed_ips_updated = \ original_port.get('fixed_ips') != port.get('fixed_ips') # Refresh ACLs for changed security groups or fixed IPs. if detached_sg_ids or attached_sg_ids or is_fixed_ips_updated: # Note that update_acls will compare the port's ACLs to # ensure only the necessary ACLs are added and deleted # on the transaction. acls_new = ovn_acl.add_acls(self._plugin, admin_context, port, sg_cache, subnet_cache) txn.add( self._nb_ovn.update_acls([port['network_id']], [port], {port['id']: acls_new}, need_compare=True)) # Refresh address sets for changed security groups or fixed IPs. if (len(port.get('fixed_ips')) != 0 or len(original_port.get('fixed_ips')) != 0): addresses = ovn_acl.acl_port_ips(port) addresses_old = ovn_acl.acl_port_ips(original_port) # Add current addresses to attached security groups. for sg_id in attached_sg_ids: for ip_version in addresses: if addresses[ip_version]: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name( sg_id, ip_version), addrs_add=addresses[ip_version], addrs_remove=None)) # Remove old addresses from detached security groups. for sg_id in detached_sg_ids: for ip_version in addresses_old: if addresses_old[ip_version]: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name( sg_id, ip_version), addrs_add=None, addrs_remove=addresses_old[ip_version])) if is_fixed_ips_updated: # We have refreshed address sets for attached and detached # security groups, so now we only need to take care of # unchanged security groups. unchanged_sg_ids = new_sg_ids & old_sg_ids for sg_id in unchanged_sg_ids: for ip_version in addresses: addr_add = (set(addresses[ip_version]) - set(addresses_old[ip_version])) or None addr_remove = (set(addresses_old[ip_version]) - set(addresses[ip_version])) or None if addr_add or addr_remove: txn.add( self._nb_ovn.update_address_set( name=utils.ovn_addrset_name( sg_id, ip_version), addrs_add=addr_add, addrs_remove=addr_remove)) if not ovn_port_info.dhcpv4_options: # Check if the DHCP_Options row exist for this port. # We need to delete it as it is no longer referenced by this # port. cmd = self._get_delete_lsp_dhcpv4_options_cmd(port) if cmd: txn.add(cmd)
def sync_acls(self, ctx): """Sync ACLs between neutron and NB. @param ctx: neutron context @type ctx: object of type neutron.context.Context @var db_ports: List of ports from neutron DB @var neutron_acls: neutron dictionary of port vs list-of-acls @var nb_acls: NB dictionary of port vs list-of-acls @var sg_ports_cache: cache for sg_ports @var subnet_cache: cache for subnets @return: Nothing """ LOG.debug('ACL-SYNC: started @ %s' % str(datetime.now())) db_ports = {} for port in self.core_plugin.get_ports(ctx): db_ports[port['id']] = port sg_cache = {} sg_ports_cache = {} subnet_cache = {} neutron_acls = {} for port_id, port in db_ports.items(): if port['security_groups']: if port_id in neutron_acls: neutron_acls[port_id].extend( acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, sg_ports_cache, subnet_cache)) else: neutron_acls[port_id] = \ acl_utils.add_acls(self.core_plugin, ctx, port, sg_cache, sg_ports_cache, subnet_cache) nb_acls = self.get_acls(ctx) self.remove_common_acls(neutron_acls, nb_acls) LOG.debug('ACLs-to-be-addded %d ACLs-to-be-removed %d' % (len(list(itertools.chain(*six.itervalues(neutron_acls)))), len(list(itertools.chain(*six.itervalues(nb_acls)))))) if self.mode == SYNC_MODE_REPAIR: LOG.debug('ACL-SYNC: transaction started @ %s' % str(datetime.now())) with self.ovn_api.transaction(check_error=True) as txn: for acla in list(itertools.chain( *six.itervalues(neutron_acls))): txn.add(self.ovn_api.add_acl(**acla)) for aclr in list(itertools.chain(*six.itervalues(nb_acls))): # Both lswitch and lport aren't needed within the ACL. lswitchr = aclr.pop('lswitch').replace('neutron-', '') lportr = aclr.pop('lport') aclr_dict = {lportr: aclr} txn.add(self.ovn_api.update_acls([lswitchr], [lportr], aclr_dict, need_compare=False, is_add_acl=False)) LOG.debug('ACL-SYNC: transaction finished @ %s' % str(datetime.now()))
def test_add_acls_no_sec_group(self): acls = ovn_acl.add_acls(self.mech_driver._plugin, mock.Mock(), self.fake_port_no_sg, {}, {}) self.assertEqual([], acls)