ALL_CHANNELS = ['basic', 'ident', 'crash', 'device', 'perf']
+
+def _defaultdict_list() -> defaultdict:
+ return defaultdict(list)
+
+
+def _defaultdict_dict() -> defaultdict:
+ return defaultdict(dict)
+
+
+def _defaultdict_int() -> defaultdict:
+ return defaultdict(int)
+
+
+def _defaultdict_defaultdict_int() -> defaultdict:
+ return defaultdict(_defaultdict_int)
+
+
+def _defaultdict_defaultdict_defaultdict_int() -> defaultdict:
+ return defaultdict(_defaultdict_defaultdict_int)
+
+
+def _defaultdict_histogram() -> defaultdict:
+ """Six-level nested defaultdict(int) used by get_osd_histograms."""
+ return defaultdict(_defaultdict_defaultdict_defaultdict_int)
+
LICENSE = 'sharing-1-0'
LICENSE_NAME = 'Community Data License Agreement - Sharing - Version 1.0'
LICENSE_URL = 'https://cdla.io/sharing-1-0/'
return etype + '.' + m.hexdigest()
def get_heap_stats(self) -> Dict[str, dict]:
- result: Dict[str, dict] = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
+ result: Dict[str, dict] = defaultdict(_defaultdict_defaultdict_int)
anonymized_daemons = {}
osd_map = self.get('osd_map')
return parsed_output
def get_mempool(self, mode: str = 'separated') -> Dict[str, dict]:
- result: Dict[str, dict] = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
+ result: Dict[str, dict] = defaultdict(_defaultdict_defaultdict_int)
anonymized_daemons = {}
osd_map = self.get('osd_map')
def get_osd_histograms(self, mode: str = 'separated') -> List[Dict[str, dict]]:
# Initialize result dict
- result: Dict[str, dict] = defaultdict(lambda: defaultdict(
- lambda: defaultdict(
- lambda: defaultdict(
- lambda: defaultdict(
- lambda: defaultdict(int))))))
+ result: Dict[str, dict] = defaultdict(_defaultdict_histogram)
# Get list of osd ids from the metadata
osd_metadata = self.get('osd_metadata')
perf_counters = self.get_perf_counters()
# Initialize 'result' dict
- result: Dict[str, dict] = defaultdict(lambda: defaultdict(list))
+ result: Dict[str, dict] = defaultdict(_defaultdict_list)
# 'separated' mode
anonymized_daemon_dict = {}
result[daemon][collection] = []
for sub_collection in sub_collection_list:
- sub_collection_result: Dict[str, dict] = defaultdict(lambda: defaultdict(dict))
+ sub_collection_result: Dict[str, dict] = defaultdict(_defaultdict_dict)
sub_collection_result['labels'] = sub_collection['labels']
for sub_collection_counter_name, sub_collection_counter_info in sub_collection['counters'].items():
if mode == 'separated':
import json
+import pickle
import pytest
import unittest
+from collections import defaultdict
from unittest import mock
import telemetry
assert m.is_opted_in() == expected['is_opted_in']
assert m.is_enabled_collection(Collection.basic_base) == expected['is_enabled_collection']['basic_base']
assert m.is_enabled_collection(Collection.basic_mds_metadata) == expected['is_enabled_collection']['basic_mds_metadata']
+
+ def test_defaultdict_helpers_are_picklable(self) -> None:
+ """Regression test for pickle serialization of defaultdict factories.
+
+ The telemetry report returned through mgr.remote() must be pickle-serializable.
+ Verify that the module-level defaultdict factories can be pickled.
+ """
+ factories = [
+ telemetry.module._defaultdict_list,
+ telemetry.module._defaultdict_dict,
+ telemetry.module._defaultdict_defaultdict_int,
+ telemetry.module._defaultdict_histogram,
+ ]
+
+ for factory in factories:
+ d = defaultdict(factory)
+ restored = pickle.loads(pickle.dumps(d))
+ assert restored is not None, \
+ f"defaultdict({factory.__name__}) failed pickle round-trip"