def get_history_deltas(hostname, search_timestamp=None): if '/' in hostname: return None # just for security reasons inventory_path = "%s/inventory/%s" % (cmk.utils.paths.var_dir, hostname) if not os.path.exists(inventory_path): return [] latest_timestamp = str(int(os.stat(inventory_path).st_mtime)) inventory_archive_dir = "%s/inventory_archive/%s" % ( cmk.utils.paths.var_dir, hostname) try: archived_timestamps = sorted(os.listdir(inventory_archive_dir)) except OSError: return [] all_timestamps = archived_timestamps + [latest_timestamp] previous_timestamp = None if not search_timestamp: required_timestamps = all_timestamps else: new_timestamp_idx = all_timestamps.index(search_timestamp) if new_timestamp_idx == 0: required_timestamps = [search_timestamp] else: previous_timestamp = all_timestamps[new_timestamp_idx - 1] required_timestamps = [search_timestamp] tree_lookup = {} def get_tree(timestamp): if timestamp is None: return StructuredDataTree() if timestamp in tree_lookup: return tree_lookup[timestamp] if timestamp == latest_timestamp: inventory_tree = load_filtered_inventory_tree(hostname) if inventory_tree is None: return tree_lookup[timestamp] = inventory_tree else: inventory_archive_path = "%s/%s" % (inventory_archive_dir, timestamp) tree_lookup[timestamp] = _filter_tree( StructuredDataTree().load_from(inventory_archive_path)) return tree_lookup[timestamp] delta_history = [] for _idx, timestamp in enumerate(required_timestamps): cached_delta_path = os.path.join( cmk.utils.paths.var_dir, "inventory_delta_cache", hostname, "%s_%s" % (previous_timestamp, timestamp)) cached_data = None try: cached_data = cmk.utils.store.load_data_from_file( cached_delta_path) except MKGeneralException: pass if cached_data: new, changed, removed, delta_tree_data = cached_data delta_tree = StructuredDataTree() delta_tree.create_tree_from_raw_tree(delta_tree_data) delta_history.append( (timestamp, (new, changed, removed, delta_tree))) previous_timestamp = timestamp continue try: previous_tree = get_tree(previous_timestamp) current_tree = get_tree(timestamp) delta_data = current_tree.compare_with(previous_tree) new, changed, removed, delta_tree = delta_data cmk.utils.store.save_file( cached_delta_path, repr((new, changed, removed, delta_tree.get_raw_tree()))) delta_history.append((timestamp, delta_data)) except RequestTimeout: raise except Exception: return [] # No inventory for this host previous_timestamp = timestamp return delta_history
def get_history_deltas( hostname: HostName, search_timestamp: Optional[str] = None ) -> Tuple[List[Tuple[str, InventoryDeltaData]], List[str]]: if '/' in hostname: return [], [] # just for security reasons inventory_path = "%s/%s" % (cmk.utils.paths.inventory_output_dir, hostname) if not os.path.exists(inventory_path): return [], [] latest_timestamp = str(int(os.stat(inventory_path).st_mtime)) inventory_archive_dir = "%s/%s" % (cmk.utils.paths.inventory_archive_dir, hostname) try: archived_timestamps = sorted(os.listdir(inventory_archive_dir)) except OSError: return [], [] all_timestamps: List[str] = archived_timestamps + [latest_timestamp] previous_timestamp: Optional[str] = None if not search_timestamp: required_timestamps = all_timestamps else: new_timestamp_idx = all_timestamps.index(search_timestamp) if new_timestamp_idx == 0: required_timestamps = [search_timestamp] else: previous_timestamp = all_timestamps[new_timestamp_idx - 1] required_timestamps = [search_timestamp] tree_lookup: Dict[str, Any] = {} def get_tree(timestamp: Optional[str]) -> StructuredDataTree: if timestamp is None: return StructuredDataTree() if timestamp in tree_lookup: return tree_lookup[timestamp] if timestamp == latest_timestamp: inventory_tree = load_filtered_inventory_tree(hostname) if inventory_tree is None: raise LoadStructuredDataError() tree_lookup[timestamp] = inventory_tree else: inventory_archive_path = "%s/%s" % (inventory_archive_dir, timestamp) tree_lookup[timestamp] = _filter_tree( StructuredDataTree().load_from(inventory_archive_path)) return tree_lookup[timestamp] corrupted_history_files = [] delta_history: List[Tuple[str, InventoryDeltaData]] = [] for _idx, timestamp in enumerate(required_timestamps): cached_delta_path = os.path.join( cmk.utils.paths.var_dir, "inventory_delta_cache", hostname, "%s_%s" % (previous_timestamp, timestamp)) cached_data = None try: cached_data = store.load_object_from_file(cached_delta_path) except MKGeneralException: pass if cached_data: new, changed, removed, delta_tree_data = cached_data delta_tree = StructuredDataTree() delta_tree.create_tree_from_raw_tree(delta_tree_data) delta_history.append( (timestamp, (new, changed, removed, delta_tree))) previous_timestamp = timestamp continue try: previous_tree = get_tree(previous_timestamp) current_tree = get_tree(timestamp) delta_data: InventoryDeltaData = current_tree.compare_with( previous_tree) new, changed, removed, delta_tree = delta_data if new or changed or removed: store.save_file( cached_delta_path, repr((new, changed, removed, delta_tree.get_raw_tree())), ) delta_history.append((timestamp, delta_data)) except LoadStructuredDataError: corrupted_history_files.append( str(get_short_inventory_history_filepath(hostname, timestamp))) previous_timestamp = timestamp return delta_history, corrupted_history_files