Loading src/device/service/drivers/gnmi_openconfig/GnmiSessionHandler.py +30 −38 Original line number Diff line number Diff line Loading @@ -101,12 +101,14 @@ class GnmiSessionHandler: if resource_type not in grouped_updates: grouped_updates[resource_type] = [] grouped_updates[resource_type].append(update) self._logger.info('Grouped update path %s under resource type %s', path_str, resource_type) self._logger.debug('Grouped updates by resource type: %s', {k: len(v) for k, v in grouped_updates.items()}) # Create aggregated notifications aggregated_notifications = [] for resource_type, updates in grouped_updates.items(): if len(updates) > 1: # Create a new notification with all updates for this resource type # Always create a new notification, regardless of the number of updates aggregated_notification = type(notifications[0])() # Copy the first notification's structure aggregated_notification.CopyFrom(notifications[0]) Loading @@ -117,11 +119,9 @@ class GnmiSessionHandler: if hasattr(updates[0], 'timestamp') and updates[0].timestamp: aggregated_notification.timestamp = max(u.timestamp for u in updates if u.timestamp) aggregated_notifications.append(aggregated_notification) self._logger.debug('Aggregated %d updates for resource type: %s', len(updates), resource_type) else: aggregated_notifications.append(notifications[0]) self._logger.info('Aggregated %d updates for resource type: %s', len(updates), resource_type) self._logger.debug('Aggregated %d field responses into %d resource-level notifications', self._logger.info('Aggregated %d field responses into %d resource-level notifications', len(notifications), len(aggregated_notifications)) return aggregated_notifications Loading @@ -129,7 +129,8 @@ class GnmiSessionHandler: def _find_common_resource_path(self, updates): """ Finds the common resource path among a list of updates. This is a heuristic and might not be accurate for all models. For Stratum compatibility, we always return the top-level resource path (e.g., /interfaces, /components) since that's what the handlers expect. """ if not updates: return None Loading @@ -140,26 +141,15 @@ class GnmiSessionHandler: self._logger.debug('Finding common path. First path: %s, parts: %s', first_path_str, first_path_parts) # Iterate through the rest of the updates to find common segments for update in updates[1:]: update_path_str = path_to_string(update.path) update_path_parts = update_path_str.strip('/').split('/') self._logger.debug('Comparing with path: %s, parts: %s', update_path_str, update_path_parts) # Find the minimum length of the two paths min_len = min(len(first_path_parts), len(update_path_parts)) # Compare segments up to the minimum length for i in range(min_len): if first_path_parts[i] != update_path_parts[i]: # Return the common path up to the point of divergence common_path = '/'.join(first_path_parts[:i]) self._logger.debug('Found common path: %s (diverged at index %d)', common_path, i) return common_path # For Stratum compatibility, always return the top-level resource path # This ensures we get paths like /interfaces, /components that match PATH_TO_HANDLER if len(first_path_parts) >= 1: top_level_path = '/' + first_path_parts[0] self._logger.debug('Returning top-level resource path: %s', top_level_path) return top_level_path # If all updates have the same path, return the full path self._logger.debug('All paths are identical, returning: %s', first_path_str) # Fallback: return the original path if we can't extract top-level self._logger.debug('Could not extract top-level path, returning original: %s', first_path_str) return first_path_str def _reconstruct_object_from_updates(self, updates): Loading Loading @@ -305,10 +295,12 @@ class GnmiSessionHandler: if common_path: self._logger.debug('Processing notification for resource: %s with %d updates', common_path, len(notification.update)) self._logger.debug('Common path: %s', common_path) try: # Reconstruct the complete object from all updates reconstructed_data = self._reconstruct_object_from_updates(notification.update) self._logger.debug('Reconstructed data keys: %s', list(reconstructed_data.keys())) self._logger.debug('Calling parse with path: %s and data: %s', common_path, reconstructed_data) # Parse the reconstructed object at the resource level results.extend(parse(common_path, reconstructed_data, self._yang_handler)) except Exception as e: Loading Loading
src/device/service/drivers/gnmi_openconfig/GnmiSessionHandler.py +30 −38 Original line number Diff line number Diff line Loading @@ -101,12 +101,14 @@ class GnmiSessionHandler: if resource_type not in grouped_updates: grouped_updates[resource_type] = [] grouped_updates[resource_type].append(update) self._logger.info('Grouped update path %s under resource type %s', path_str, resource_type) self._logger.debug('Grouped updates by resource type: %s', {k: len(v) for k, v in grouped_updates.items()}) # Create aggregated notifications aggregated_notifications = [] for resource_type, updates in grouped_updates.items(): if len(updates) > 1: # Create a new notification with all updates for this resource type # Always create a new notification, regardless of the number of updates aggregated_notification = type(notifications[0])() # Copy the first notification's structure aggregated_notification.CopyFrom(notifications[0]) Loading @@ -117,11 +119,9 @@ class GnmiSessionHandler: if hasattr(updates[0], 'timestamp') and updates[0].timestamp: aggregated_notification.timestamp = max(u.timestamp for u in updates if u.timestamp) aggregated_notifications.append(aggregated_notification) self._logger.debug('Aggregated %d updates for resource type: %s', len(updates), resource_type) else: aggregated_notifications.append(notifications[0]) self._logger.info('Aggregated %d updates for resource type: %s', len(updates), resource_type) self._logger.debug('Aggregated %d field responses into %d resource-level notifications', self._logger.info('Aggregated %d field responses into %d resource-level notifications', len(notifications), len(aggregated_notifications)) return aggregated_notifications Loading @@ -129,7 +129,8 @@ class GnmiSessionHandler: def _find_common_resource_path(self, updates): """ Finds the common resource path among a list of updates. This is a heuristic and might not be accurate for all models. For Stratum compatibility, we always return the top-level resource path (e.g., /interfaces, /components) since that's what the handlers expect. """ if not updates: return None Loading @@ -140,26 +141,15 @@ class GnmiSessionHandler: self._logger.debug('Finding common path. First path: %s, parts: %s', first_path_str, first_path_parts) # Iterate through the rest of the updates to find common segments for update in updates[1:]: update_path_str = path_to_string(update.path) update_path_parts = update_path_str.strip('/').split('/') self._logger.debug('Comparing with path: %s, parts: %s', update_path_str, update_path_parts) # Find the minimum length of the two paths min_len = min(len(first_path_parts), len(update_path_parts)) # Compare segments up to the minimum length for i in range(min_len): if first_path_parts[i] != update_path_parts[i]: # Return the common path up to the point of divergence common_path = '/'.join(first_path_parts[:i]) self._logger.debug('Found common path: %s (diverged at index %d)', common_path, i) return common_path # For Stratum compatibility, always return the top-level resource path # This ensures we get paths like /interfaces, /components that match PATH_TO_HANDLER if len(first_path_parts) >= 1: top_level_path = '/' + first_path_parts[0] self._logger.debug('Returning top-level resource path: %s', top_level_path) return top_level_path # If all updates have the same path, return the full path self._logger.debug('All paths are identical, returning: %s', first_path_str) # Fallback: return the original path if we can't extract top-level self._logger.debug('Could not extract top-level path, returning original: %s', first_path_str) return first_path_str def _reconstruct_object_from_updates(self, updates): Loading Loading @@ -305,10 +295,12 @@ class GnmiSessionHandler: if common_path: self._logger.debug('Processing notification for resource: %s with %d updates', common_path, len(notification.update)) self._logger.debug('Common path: %s', common_path) try: # Reconstruct the complete object from all updates reconstructed_data = self._reconstruct_object_from_updates(notification.update) self._logger.debug('Reconstructed data keys: %s', list(reconstructed_data.keys())) self._logger.debug('Calling parse with path: %s and data: %s', common_path, reconstructed_data) # Parse the reconstructed object at the resource level results.extend(parse(common_path, reconstructed_data, self._yang_handler)) except Exception as e: Loading