def clean_up_endpoint_statuses(self, our_endpoints_ids): """ Mark any endpoint status reports for non-existent endpoints for cleanup. :param set our_endpoints_ids: Set of endpoint IDs for endpoints on this host. """ if not self._config.REPORT_ENDPOINT_STATUS: _log.debug("Endpoint status reporting disabled, ignoring.") return our_host_dir = "/".join([FELIX_STATUS_DIR, self._config.HOSTNAME, "workload"]) try: # Grab all the existing status reports. response = self.client.read(our_host_dir, recursive=True) except EtcdKeyNotFound: _log.info("No endpoint statuses found, nothing to clean up") else: for node in response.leaves: combined_id = get_endpoint_id_from_key(node.key) if combined_id and combined_id not in our_endpoints_ids: # We found an endpoint in our status reporting tree that # wasn't in the main tree. Mark it as dirty so the status # reporting thread will clean it up. _log.debug("Endpoint %s removed by resync, marking " "status key for cleanup", combined_id) self._status_reporter.mark_endpoint_dirty(combined_id, async=True) elif node.dir: # This leaf is an empty directory, try to clean it up. # This is safe even if another thread is adding keys back # into the directory. _log.debug("Found empty directory %s, cleaning up", node.key) delete_empty_parents(self.client, node.key, our_host_dir)
def _attempt_cleanup(self): our_host_dir = "/".join( [FELIX_STATUS_DIR, self._config.HOSTNAME, "workload"]) try: # Grab all the existing status reports. response = self.client.read(our_host_dir, recursive=True) except EtcdKeyNotFound: _log.info("No endpoint statuses found, nothing to clean up") else: # Mark all statuses we find as dirty. This will result in any # unknown endpoints being cleaned up. for node in response.leaves: combined_id = get_endpoint_id_from_key(node.key) if combined_id: _log.debug( "Endpoint %s removed by resync, marking " "status key for cleanup", combined_id) self._mark_endpoint_dirty(combined_id) elif node.dir: # This leaf is an empty directory, try to clean it up. # This is safe even if another thread is adding keys back # into the directory. _log.debug("Found empty directory %s, cleaning up", node.key) delete_empty_parents(self.client, node.key, our_host_dir)
def _attempt_cleanup(self): our_host_dir = "/".join([FELIX_STATUS_DIR, self._config.HOSTNAME, "workload"]) try: # Grab all the existing status reports. response = self.client.read(our_host_dir, recursive=True) except EtcdKeyNotFound: _log.info("No endpoint statuses found, nothing to clean up") else: # Mark all statuses we find as dirty. This will result in any # unknown endpoints being cleaned up. for node in response.leaves: combined_id = get_endpoint_id_from_key(node.key) if combined_id: _log.debug("Endpoint %s removed by resync, marking " "status key for cleanup", combined_id) self._mark_endpoint_dirty(combined_id) elif node.dir: # This leaf is an empty directory, try to clean it up. # This is safe even if another thread is adding keys back # into the directory. _log.debug("Found empty directory %s, cleaning up", node.key) delete_empty_parents(self.client, node.key, our_host_dir)
def test_delete_empty_parents_other_exception(self): m_client = Mock() m_client.delete = Mock() m_client.delete.side_effect = etcd.EtcdValueError() delete_empty_parents(m_client, "/foo/bar/baz/biff", "/foo") self.assertEqual(m_client.delete.mock_calls, [ call("foo/bar/baz/biff", dir=True, timeout=5), ])
def test_delete_empty_parents_mainline(self): m_client = Mock() m_client.delete = Mock() delete_empty_parents(m_client, "/foo/bar/baz/biff", "/foo") self.assertEqual(m_client.delete.mock_calls, [ call("foo/bar/baz/biff", dir=True, timeout=5), call("foo/bar/baz", dir=True, timeout=5), call("foo/bar", dir=True, timeout=5), ])
def test_delete_empty_parents_not_found(self): m_client = Mock() m_client.delete = Mock() m_client.delete.side_effect = [None, etcd.EtcdKeyNotFound(), None] delete_empty_parents(m_client, "/foo/bar/baz/biff", "/foo") self.assertEqual(m_client.delete.mock_calls, [ call("foo/bar/baz/biff", dir=True, timeout=5), call("foo/bar/baz", dir=True, timeout=5), call("foo/bar", dir=True, timeout=5), ])
def test_delete_empty_parents_other_exception(self): m_client = Mock() m_client.delete = Mock() m_client.delete.side_effect = etcd.EtcdValueError() delete_empty_parents(m_client, "/foo/bar/baz/biff", "/foo") self.assertEqual( m_client.delete.mock_calls, [ call("foo/bar/baz/biff", dir=True, timeout=5), ] )
def test_delete_empty_parents_mainline(self): m_client = Mock() m_client.delete = Mock() delete_empty_parents(m_client, "/foo/bar/baz/biff", "/foo") self.assertEqual( m_client.delete.mock_calls, [ call("foo/bar/baz/biff", dir=True, timeout=5), call("foo/bar/baz", dir=True, timeout=5), call("foo/bar", dir=True, timeout=5), ] )
def test_delete_empty_parents_not_empty(self): m_client = Mock() m_client.delete = Mock() m_client.delete.side_effect = [ None, etcd.EtcdDirNotEmpty(), ] delete_empty_parents(m_client, "/foo/bar/baz/biff", "/foo") self.assertEqual( m_client.delete.mock_calls, [ call("foo/bar/baz/biff", dir=True, timeout=5), call("foo/bar/baz", dir=True, timeout=5), ] )
def _write_endpoint_status_to_etcd(self, ep_id, status): """ Try to actually write the status dict into etcd or delete the key if it is no longer needed. """ status_key = ep_id.path_for_status if status: _log.debug("Writing endpoint status %s = %s", ep_id, status) self.client.set(status_key, json.dumps(status)) else: _log.debug("Removing endpoint status %s", ep_id) try: self.client.delete(status_key) except EtcdKeyNotFound: _log.debug("Tried to delete %s but it was already gone", status_key) # Clean up any now-empty parent directories. delete_empty_parents( self.client, status_key.rsplit("/", 1)[0], # Snip off final path segment. dir_for_felix_status(self._config.HOSTNAME), )
def _write_endpoint_status_to_etcd(self, ep_id, status): """ Try to actually write the status dict into etcd or delete the key if it is no longer needed. """ status_key = ep_id.path_for_status if status: _log.debug("Writing endpoint status %s = %s", ep_id, status) self.client.set(status_key, json.dumps(status)) else: _log.debug("Removing endpoint status %s", ep_id) try: self.client.delete(status_key) except EtcdKeyNotFound: _log.debug("Tried to delete %s but it was already gone", status_key) # Clean up any now-empty parent directories. delete_empty_parents( self.client, status_key.rsplit("/", 1)[0], # Snip off final path segment. dir_for_felix_status(self._config.HOSTNAME))
def clean_up_endpoint_statuses(self, our_endpoints_ids): """ Mark any endpoint status reports for non-existent endpoints for cleanup. :param set our_endpoints_ids: Set of endpoint IDs for endpoints on this host. """ if not self._config.REPORT_ENDPOINT_STATUS: _log.debug("Endpoint status reporting disabled, ignoring.") return our_host_dir = "/".join( [FELIX_STATUS_DIR, self._config.HOSTNAME, "workload"]) try: # Grab all the existing status reports. response = self.client.read(our_host_dir, recursive=True) except EtcdKeyNotFound: _log.info("No endpoint statuses found, nothing to clean up") else: for node in response.leaves: combined_id = get_endpoint_id_from_key(node.key) if combined_id and combined_id not in our_endpoints_ids: # We found an endpoint in our status reporting tree that # wasn't in the main tree. Mark it as dirty so the status # reporting thread will clean it up. _log.debug( "Endpoint %s removed by resync, marking " "status key for cleanup", combined_id) self._status_reporter.mark_endpoint_dirty(combined_id, async=True) elif node.dir: # This leaf is an empty directory, try to clean it up. # This is safe even if another thread is adding keys back # into the directory. _log.debug("Found empty directory %s, cleaning up", node.key) delete_empty_parents(self.client, node.key, our_host_dir)