def _apply_endpoint_update(self): pending_endpoint = self._pending_endpoint if pending_endpoint == self.endpoint: _log.debug("Endpoint hasn't changed, nothing to do") return if pending_endpoint: # Update/create. if pending_endpoint['mac'] != self._mac: # Either we have not seen this MAC before, or it has changed. _log.debug("Endpoint MAC changed to %s", pending_endpoint["mac"]) self._mac = pending_endpoint['mac'] self._mac_changed = True # MAC change requires refresh of iptables rules and ARP table. self._iptables_in_sync = False self._device_in_sync = False if self.endpoint is None: # This is the first time we have seen the endpoint, so extract # the interface name and endpoint ID. self._iface_name = pending_endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) _log.debug("Learned interface name/suffix: %s/%s", self._iface_name, self._suffix) # First time through, need to program everything. self._iptables_in_sync = False self._device_in_sync = False # Check if the profile ID or IP addresses have changed, requiring # a refresh of the dataplane. profile_ids = set(pending_endpoint.get("profile_ids", [])) if profile_ids != self.rules_ref_helper.required_refs: # Profile ID update required iptables update but not device # update. _log.debug("Profile IDs changed, need to update iptables") self._iptables_in_sync = False if (self.endpoint and (self.endpoint[self.nets_key] != pending_endpoint[self.nets_key])): # IP addresses have changed, need to update the routing table. _log.debug("IP addresses changed, need to update routing") self._device_in_sync = False else: # Delete of the endpoint. Need to resync everything. profile_ids = set() self._iptables_in_sync = False self._device_in_sync = False # Note: we don't actually need to wait for the activation to finish # due to the dependency management in the iptables layer. self.rules_ref_helper.replace_all(profile_ids) self.endpoint = pending_endpoint self._endpoint_update_pending = False self._pending_endpoint = None
def on_endpoint_update(self, endpoint): """ Called when this endpoint has received an update. :param dict[str] endpoint: endpoint parameter dictionary. """ _log.info("%s updated: %s", self, endpoint) mac_changed = False if not endpoint and not self.endpoint: # First time we have been called, but it's a delete! Maybe some # odd timing window, but we have nothing to tidy up. return if endpoint and endpoint['mac'] != self._mac: # Either we have not seen this MAC before, or it has changed. self._mac = endpoint['mac'] mac_changed = True if endpoint and not self.endpoint: # This is the first time we have seen the endpoint, so extract the # interface name and endpoint ID. self._iface_name = endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) was_ready = self._ready # Activate the required profile IDs (and deactivate any that we no # longer need). if endpoint: new_profile_ids = set(endpoint["profile_ids"]) else: new_profile_ids = set() # Note: we don't actually need to wait for the activation to finish # due to the dependency management in the iptables layer. self.rules_ref_helper.replace_all(new_profile_ids) if endpoint != self.endpoint: self._dirty = True # Store off the endpoint we were passed. self.endpoint = endpoint if endpoint: # Configure the network interface; may fail if not there yet (in # which case we'll just do it when the interface comes up). self._configure_interface(mac_changed) else: # Remove the network programming. self._deconfigure_interface() self._maybe_update(was_ready) _log.debug("%s finished processing update", self)
def on_endpoint_update(self, endpoint, force_reprogram=False): """ Called when this endpoint has received an update. :param dict[str] endpoint: endpoint parameter dictionary. """ _log.info("%s updated: %s", self, endpoint) mac_changed = False if not endpoint and not self.endpoint: # First time we have been called, but it's a delete! Maybe some # odd timing window, but we have nothing to tidy up. return if endpoint and endpoint['mac'] != self._mac: # Either we have not seen this MAC before, or it has changed. self._mac = endpoint['mac'] mac_changed = True if endpoint and not self.endpoint: # This is the first time we have seen the endpoint, so extract the # interface name and endpoint ID. self._iface_name = endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) was_ready = self._ready # Activate the required profile IDs (and deactivate any that we no # longer need). if endpoint: new_profile_ids = set(endpoint["profile_ids"]) else: new_profile_ids = set() # Note: we don't actually need to wait for the activation to finish # due to the dependency management in the iptables layer. self.rules_ref_helper.replace_all(new_profile_ids) if endpoint != self.endpoint or force_reprogram: self._dirty = True # Store off the endpoint we were passed. self.endpoint = endpoint if endpoint: # Configure the network interface; may fail if not there yet (in # which case we'll just do it when the interface comes up). self._configure_interface(mac_changed) else: # Remove the network programming. self._deconfigure_interface() self._maybe_update(was_ready) _log.debug("%s finished processing update", self)
def on_endpoint_update(self, endpoint): """ Called when this endpoint has received an update. :param dict[str] endpoint: endpoint parameter dictionary. """ _log.info("%s updated: %s", self, endpoint) if endpoint and not self.endpoint: # This is the first time we have seen the endpoint, so extract the # interface name and endpoint ID. self._iface_name = endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) was_ready = self._ready old_profile_id = self.endpoint and self.endpoint["profile_id"] new_profile_id = endpoint and endpoint["profile_id"] if old_profile_id != new_profile_id: if old_profile_id: # Clean up the old profile. _log.info("Profile changed, decreffing old profile %s", old_profile_id) self.rules_mgr.decref(old_profile_id, async=True) if new_profile_id is not None: _log.info("Acquiring new profile %s", new_profile_id) self.rules_mgr.get_and_incref(new_profile_id, async=True) if endpoint != self.endpoint: self._dirty = True # Store off the endpoint we were passed. self.endpoint = endpoint if endpoint: # Configure the network interface; may fail if not there yet (in # which case we'll just do it when the interface comes up). self._configure_interface() else: # Remove the network programming. self._deconfigure_interface() self._maybe_update(was_ready) _log.debug("%s finished processing update", self)
def _calculate_update(self, ifaces): """ Calculates the iptables update to rewrite our chains. To avoid traversing lots of dispatch rules to find the right one, we build a tree of chains. Currently, the tree can only be two layers deep: a root chain and a layer of leaves. Interface names look like this: "prefix1234abc". The "prefix" part is always the same so we ignore it. We call "1234abc", the "suffix". The root chain contains two sorts of rules: * where there are multiple interfaces whose suffixes start with the same character, it contains a rule that matches on that prefix of the "suffix"(!) and directs the packet to a leaf chain for that prefix. * as an optimization, if there is only one interface whose suffix starts with a given character, it contains a dispatch rule for that exact interface name. For example, if we have interface names "tapA1" "tapB1" "tapB2", we'll get (in pseudo code): Root chain: if interface=="tapA1" then goto chain for endpoint tapA1 if interface.startswith("tapB") then goto leaf chain for prefix "tapB" tapB leaf chain: if interface=="tapB1" then goto chain for endpoint tapB1 if interface=="tapB2" then goto chain for endpoint tapB2 :param set[str] ifaces: The list of interfaces to generate a dispatch chain for. :returns Tuple: to_delete, deps, updates, new_leaf_chains: * set of leaf chains that are no longer needed for deletion * chain dependency dict. * chain updates dict. * complete set of leaf chains that are now required. """ # iptables update fragments/dependencies for the root chains. updates = defaultdict(list) root_to_upds = updates[CHAIN_TO_ENDPOINT] root_from_upds = updates[CHAIN_FROM_ENDPOINT] dependencies = defaultdict(set) root_to_deps = dependencies[CHAIN_TO_ENDPOINT] root_from_deps = dependencies[CHAIN_FROM_ENDPOINT] # Special case: allow the metadata IP through from all interfaces. if self.config.METADATA_IP is not None and self.ip_version == 4: # Need to allow outgoing Metadata requests. root_from_upds.append("--append %s " "--protocol tcp " "--in-interface %s+ " "--destination %s " "--dport %s " "--jump RETURN" % (CHAIN_FROM_ENDPOINT, self.config.IFACE_PREFIX, self.config.METADATA_IP, self.config.METADATA_PORT)) # Separate the interface names by their prefixes so we can count them # and decide whether to program a leaf chain or not. interfaces_by_prefix = defaultdict(set) for iface in ifaces: ep_suffix = interface_to_suffix(self.config, iface) prefix = ep_suffix[:1] interfaces_by_prefix[prefix].add(iface) # Spin through the interfaces by prefix. Either add them directly # to the root chain or create a leaf and add them there. new_leaf_chains = set() for prefix, interfaces in interfaces_by_prefix.iteritems(): use_root_chain = len(interfaces) == 1 if use_root_chain: # Optimization: there's only one interface with this prefix, # don't program a leaf chain. disp_to_chain = CHAIN_TO_ENDPOINT disp_from_chain = CHAIN_FROM_ENDPOINT to_deps = root_to_deps from_deps = root_from_deps to_upds = root_to_upds from_upds = root_from_upds else: # There's more than one interface with this prefix, program # a leaf chain. disp_to_chain = CHAIN_TO_LEAF + "-" + prefix disp_from_chain = CHAIN_FROM_LEAF + "-" + prefix to_upds = updates[disp_to_chain] from_upds = updates[disp_from_chain] to_deps = dependencies[disp_to_chain] from_deps = dependencies[disp_from_chain] new_leaf_chains.add(disp_from_chain) new_leaf_chains.add(disp_to_chain) # Root chain depends on its leaves. root_from_deps.add(disp_to_chain) root_to_deps.add(disp_from_chain) # Point root chain at prefix chain. iface_match = self.config.IFACE_PREFIX + prefix + "+" root_from_upds.append( "--append %s --in-interface %s --goto %s" % (CHAIN_FROM_ENDPOINT, iface_match, disp_from_chain) ) root_to_upds.append( "--append %s --out-interface %s --goto %s" % (CHAIN_TO_ENDPOINT, iface_match, disp_to_chain) ) # Common processing, add the per-endpoint rules to whichever # chain we decided above. for iface in interfaces: # Add rule to leaf or global chain to direct traffic to the # endpoint-specific one. Note that we use --goto, which means # that the endpoint-specific chain will return to our parent # rather than to this chain. ep_suffix = interface_to_suffix(self.config, iface) to_chain_name, from_chain_name = chain_names(ep_suffix) from_upds.append("--append %s --in-interface %s --goto %s" % (disp_from_chain, iface, from_chain_name)) from_deps.add(from_chain_name) to_upds.append("--append %s --out-interface %s --goto %s" % (disp_to_chain, iface, to_chain_name)) to_deps.add(to_chain_name) if not use_root_chain: # Add a default drop to the end of the leaf chain. from_upds.append("--append %s --jump DROP" % disp_from_chain) to_upds.append("--append %s --jump DROP" % disp_to_chain) # Both TO and FROM chains end with a DROP so that interfaces that # we don't know about yet can't bypass our rules. root_from_upds.append("--append %s --jump DROP" % CHAIN_FROM_ENDPOINT) root_to_upds.append("--append %s --jump DROP" % CHAIN_TO_ENDPOINT) chains_to_delete = self.programmed_leaf_chains - new_leaf_chains return chains_to_delete, dependencies, updates, new_leaf_chains
def _calculate_update(self, ifaces): """ Calculates the iptables update to rewrite our chains. To avoid traversing lots of dispatch rules to find the right one, we build a tree of chains. Currently, the tree can only be two layers deep: a root chain and a layer of leaves. Interface names look like this: "prefix1234abc". The "prefix" part is always the same so we ignore it. We call "1234abc", the "suffix". The root chain contains two sorts of rules: * where there are multiple interfaces whose suffixes start with the same character, it contains a rule that matches on that prefix of the "suffix"(!) and directs the packet to a leaf chain for that prefix. * as an optimization, if there is only one interface whose suffix starts with a given character, it contains a dispatch rule for that exact interface name. For example, if we have interface names "tapA1" "tapB1" "tapB2", we'll get (in pseudo code): Root chain: if interface=="tapA1" then goto chain for endpoint tapA1 if interface.startswith("tapB") then goto leaf chain for prefix "tapB" tapB leaf chain: if interface=="tapB1" then goto chain for endpoint tapB1 if interface=="tapB2" then goto chain for endpoint tapB2 :param set[str] ifaces: The list of interfaces to generate a dispatch chain for. :returns Tuple: to_delete, deps, updates, new_leaf_chains: * set of leaf chains that are no longer needed for deletion * chain dependency dict. * chain updates dict. * complete set of leaf chains that are now required. """ # iptables update fragments/dependencies for the root chains. updates = defaultdict(list) root_to_upds = updates[CHAIN_TO_ENDPOINT] root_from_upds = updates[CHAIN_FROM_ENDPOINT] dependencies = defaultdict(set) root_to_deps = dependencies[CHAIN_TO_ENDPOINT] root_from_deps = dependencies[CHAIN_FROM_ENDPOINT] # Separate the interface names by their prefixes so we can count them # and decide whether to program a leaf chain or not. interfaces_by_prefix = defaultdict(set) for iface in ifaces: ep_suffix = interface_to_suffix(self.config, iface) prefix = ep_suffix[:1] interfaces_by_prefix[prefix].add(iface) # Spin through the interfaces by prefix. Either add them directly # to the root chain or create a leaf and add them there. new_leaf_chains = set() for prefix, interfaces in interfaces_by_prefix.iteritems(): use_root_chain = len(interfaces) == 1 if use_root_chain: # Optimization: there's only one interface with this prefix, # don't program a leaf chain. disp_to_chain = CHAIN_TO_ENDPOINT disp_from_chain = CHAIN_FROM_ENDPOINT to_deps = root_to_deps from_deps = root_from_deps to_upds = root_to_upds from_upds = root_from_upds else: # There's more than one interface with this prefix, program # a leaf chain. disp_to_chain = CHAIN_TO_LEAF + "-" + prefix disp_from_chain = CHAIN_FROM_LEAF + "-" + prefix to_upds = updates[disp_to_chain] from_upds = updates[disp_from_chain] to_deps = dependencies[disp_to_chain] from_deps = dependencies[disp_from_chain] new_leaf_chains.add(disp_from_chain) new_leaf_chains.add(disp_to_chain) # Root chain depends on its leaves. root_from_deps.add(disp_to_chain) root_to_deps.add(disp_from_chain) # Point root chain at prefix chain. iface_match = self.config.IFACE_PREFIX + prefix + "+" root_from_upds.append( "--append %s --in-interface %s --goto %s" % (CHAIN_FROM_ENDPOINT, iface_match, disp_from_chain)) root_to_upds.append( "--append %s --out-interface %s --goto %s" % (CHAIN_TO_ENDPOINT, iface_match, disp_to_chain)) # Common processing, add the per-endpoint rules to whichever # chain we decided above. for iface in interfaces: # Add rule to leaf or global chain to direct traffic to the # endpoint-specific one. Note that we use --goto, which means # that the endpoint-specific chain will return to our parent # rather than to this chain. ep_suffix = interface_to_suffix(self.config, iface) to_chain_name = CHAIN_TO_PREFIX + ep_suffix from_chain_name = CHAIN_FROM_PREFIX + ep_suffix from_upds.append("--append %s --in-interface %s --goto %s" % (disp_from_chain, iface, from_chain_name)) from_deps.add(from_chain_name) to_upds.append("--append %s --out-interface %s --goto %s" % (disp_to_chain, iface, to_chain_name)) to_deps.add(to_chain_name) if not use_root_chain: # Add a default drop to the end of the leaf chain. # Add a default drop to the end of the leaf chain. from_upds.extend( self.iptables_generator.drop_rules( self.ip_version, disp_from_chain, None, "From unknown endpoint")) to_upds.extend( self.iptables_generator.drop_rules(self.ip_version, disp_to_chain, None, "To unknown endpoint")) # Both TO and FROM chains end with a DROP so that interfaces that # we don't know about yet can't bypass our rules. root_from_upds.extend( self.iptables_generator.drop_rules(self.ip_version, CHAIN_FROM_ENDPOINT, None, "From unknown endpoint")) root_to_upds.extend( self.iptables_generator.drop_rules(self.ip_version, CHAIN_TO_ENDPOINT, None, "To unknown endpoint")) chains_to_delete = self.programmed_leaf_chains - new_leaf_chains return chains_to_delete, dependencies, updates, new_leaf_chains
def _apply_endpoint_update(self): pending_endpoint = self._pending_endpoint if pending_endpoint == self.endpoint: _log.debug("Endpoint hasn't changed, nothing to do") return # Calculate the set of IPs that we had before this update. Needed on # the update and delete code paths below. if self.endpoint: old_ips = set(futils.net_to_ip(n) for n in self.endpoint.get(self.nets_key, [])) old_nat_mappings = self.endpoint.get(self.nat_key, []) else: old_ips = set() old_nat_mappings = [] all_old_ips = old_ips | set([n["ext_ip"] for n in old_nat_mappings]) if pending_endpoint: # Update/create. if pending_endpoint['mac'] != self._mac: # Either we have not seen this MAC before, or it has changed. _log.debug("Endpoint MAC changed to %s", pending_endpoint["mac"]) self._mac = pending_endpoint['mac'] self._mac_changed = True # MAC change requires refresh of iptables rules and ARP table. self._iptables_in_sync = False self._device_in_sync = False if self.endpoint is None: # This is the first time we have seen the endpoint, so extract # the interface name and endpoint ID. self._iface_name = pending_endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) _log.debug("Learned interface name/suffix: %s/%s", self._iface_name, self._suffix) # First time through, need to program everything. self._iptables_in_sync = False self._device_in_sync = False if self._device_is_up is None: _log.debug("Learned interface name, checking if device " "is up.") self._device_is_up = ( devices.interface_exists(self._iface_name) and devices.interface_up(self._iface_name) ) # Check if the profile ID or IP addresses have changed, requiring # a refresh of the dataplane. profile_ids = set(pending_endpoint.get("profile_ids", [])) if profile_ids != self._explicit_profile_ids: # Profile ID update requires iptables update but not device # update. _log.debug("Profile IDs changed from %s to %s, need to update " "iptables", self._rules_ref_helper.required_refs, profile_ids) self._explicit_profile_ids = profile_ids self._iptables_in_sync = False self._profile_ids_dirty = True # Check for changes to values that require a device update. if self.endpoint: if self.endpoint.get("state") != pending_endpoint.get("state"): _log.debug("Desired interface state updated.") self._device_in_sync = False self._iptables_in_sync = False new_ips = set(futils.net_to_ip(n) for n in pending_endpoint.get(self.nets_key, [])) if old_ips != new_ips: # IP addresses have changed, need to update the routing # table. _log.debug("IP addresses changed, need to update routing") self._device_in_sync = False new_nat_mappings = pending_endpoint.get(self.nat_key, []) if old_nat_mappings != new_nat_mappings: _log.debug("NAT mappings have changed, refreshing.") self._device_in_sync = False self._iptables_in_sync = False all_new_ips = new_ips | set([n["ext_ip"] for n in new_nat_mappings]) if all_old_ips != all_new_ips: # Ensure we clean up any conntrack entries for IPs that # have been removed. _log.debug("Set of all IPs changed from %s to %s", all_old_ips, all_new_ips) self._removed_ips |= all_old_ips self._removed_ips -= all_new_ips else: # Delete of the endpoint. Need to resync everything. self._profile_ids_dirty = True self._iptables_in_sync = False self._device_in_sync = False self._removed_ips |= all_old_ips self.endpoint = pending_endpoint self._endpoint_update_pending = False self._pending_endpoint = None
def _apply_endpoint_update(self): pending_endpoint = self._pending_endpoint if pending_endpoint == self.endpoint: _log.debug("Endpoint hasn't changed, nothing to do") return if pending_endpoint: # Update/create. if pending_endpoint['mac'] != self._mac: # Either we have not seen this MAC before, or it has changed. _log.debug("Endpoint MAC changed to %s", pending_endpoint["mac"]) self._mac = pending_endpoint['mac'] self._mac_changed = True # MAC change requires refresh of iptables rules and ARP table. self._iptables_in_sync = False self._device_in_sync = False if self.endpoint is None: # This is the first time we have seen the endpoint, so extract # the interface name and endpoint ID. self._iface_name = pending_endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) _log.debug("Learned interface name/suffix: %s/%s", self._iface_name, self._suffix) # First time through, need to program everything. self._iptables_in_sync = False self._device_in_sync = False if self._device_is_up is None: _log.debug("Learned interface name, checking if device " "is up.") self._device_is_up = ( devices.interface_exists(self._iface_name) and devices.interface_up(self._iface_name)) # Check if the profile ID or IP addresses have changed, requiring # a refresh of the dataplane. profile_ids = set(pending_endpoint.get("profile_ids", [])) if profile_ids != self.rules_ref_helper.required_refs: # Profile ID update required iptables update but not device # update. _log.debug("Profile IDs changed, need to update iptables") self._iptables_in_sync = False # Check for changes to values that require a device update. if self.endpoint: if self.endpoint.get("state") != pending_endpoint.get("state"): _log.debug("Desired interface state updated.") self._device_in_sync = False self._iptables_in_sync = False if (self.endpoint[self.nets_key] != pending_endpoint[self.nets_key]): # IP addresses have changed, need to update the routing # table. _log.debug("IP addresses changed, need to update routing") self._device_in_sync = False for key in "ipv4_nat", "ipv6_nat": if (self.endpoint.get(key, None) != pending_endpoint.get( key, None)): _log.debug("NAT mappings have changed, refreshing.") self._device_in_sync = False self._iptables_in_sync = False else: # Delete of the endpoint. Need to resync everything. profile_ids = set() self._iptables_in_sync = False self._device_in_sync = False # Note: we don't actually need to wait for the activation to finish # due to the dependency management in the iptables layer. self.rules_ref_helper.replace_all(profile_ids) self.endpoint = pending_endpoint self._endpoint_update_pending = False self._pending_endpoint = None
def _apply_endpoint_update(self): pending_endpoint = self._pending_endpoint if pending_endpoint == self.endpoint: _log.debug("Endpoint hasn't changed, nothing to do") return # Calculate the set of IPs that we had before this update. Needed on # the update and delete code paths below. if self.endpoint: old_ips = set( futils.net_to_ip(n) for n in self.endpoint.get(self.nets_key, [])) old_nat_mappings = self.endpoint.get(self.nat_key, []) else: old_ips = set() old_nat_mappings = [] all_old_ips = old_ips | set([n["ext_ip"] for n in old_nat_mappings]) if pending_endpoint: # Update/create. if pending_endpoint['mac'] != self._mac: # Either we have not seen this MAC before, or it has changed. _log.debug("Endpoint MAC changed to %s", pending_endpoint["mac"]) self._mac = pending_endpoint['mac'] self._mac_changed = True # MAC change requires refresh of iptables rules and ARP table. self._iptables_in_sync = False self._device_in_sync = False if self.endpoint is None: # This is the first time we have seen the endpoint, so extract # the interface name and endpoint ID. self._iface_name = pending_endpoint["name"] self._suffix = interface_to_suffix(self.config, self._iface_name) _log.debug("Learned interface name/suffix: %s/%s", self._iface_name, self._suffix) # First time through, need to program everything. self._iptables_in_sync = False self._device_in_sync = False if self._device_is_up is None: _log.debug("Learned interface name, checking if device " "is up.") self._device_is_up = ( devices.interface_exists(self._iface_name) and devices.interface_up(self._iface_name)) # Check if the profile ID or IP addresses have changed, requiring # a refresh of the dataplane. profile_ids = set(pending_endpoint.get("profile_ids", [])) if profile_ids != self._explicit_profile_ids: # Profile ID update requires iptables update but not device # update. _log.debug( "Profile IDs changed from %s to %s, need to update " "iptables", self._rules_ref_helper.required_refs, profile_ids) self._explicit_profile_ids = profile_ids self._iptables_in_sync = False self._profile_ids_dirty = True # Check for changes to values that require a device update. if self.endpoint: if self.endpoint.get("state") != pending_endpoint.get("state"): _log.debug("Desired interface state updated.") self._device_in_sync = False self._iptables_in_sync = False new_ips = set( futils.net_to_ip(n) for n in pending_endpoint.get(self.nets_key, [])) if old_ips != new_ips: # IP addresses have changed, need to update the routing # table. _log.debug("IP addresses changed, need to update routing") self._device_in_sync = False new_nat_mappings = pending_endpoint.get(self.nat_key, []) if old_nat_mappings != new_nat_mappings: _log.debug("NAT mappings have changed, refreshing.") self._device_in_sync = False self._iptables_in_sync = False all_new_ips = new_ips | set( [n["ext_ip"] for n in new_nat_mappings]) if all_old_ips != all_new_ips: # Ensure we clean up any conntrack entries for IPs that # have been removed. _log.debug("Set of all IPs changed from %s to %s", all_old_ips, all_new_ips) self._removed_ips |= all_old_ips self._removed_ips -= all_new_ips else: # Delete of the endpoint. Need to resync everything. self._profile_ids_dirty = True self._iptables_in_sync = False self._device_in_sync = False self._removed_ips |= all_old_ips self.endpoint = pending_endpoint self._endpoint_update_pending = False self._pending_endpoint = None