diff --git a/devlib/bin/scripts/shutils.in b/devlib/bin/scripts/shutils.in index 6d78be7..004030d 100755 --- a/devlib/bin/scripts/shutils.in +++ b/devlib/bin/scripts/shutils.in @@ -195,6 +195,22 @@ cgroups_freezer_set_state() { exit 1 } +################################################################################ +# Misc +################################################################################ + +read_tree_values() { + PATH=$1 + MAXDEPTH=$2 + + PATHS=$($BUSYBOX find $PATH -follow -maxdepth $MAXDEPTH) + if [ ${#PATHS[@]} -eq 0 ]; then + echo "ERROR: '$1' does not exist" + else + $BUSYBOX grep -s '' $PATHS + fi +} + ################################################################################ # Main Function Dispatcher ################################################################################ @@ -236,6 +252,9 @@ cgroups_freezer_set_state) ftrace_get_function_stats) ftrace_get_function_stats ;; +read_tree_values) + read_tree_values $* + ;; *) echo "Command [$CMD] not supported" exit -1 diff --git a/devlib/target.py b/devlib/target.py index f84c6a1..8609fec 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -614,6 +614,20 @@ class Target(object): timeout = duration + 10 self.execute('sleep {}'.format(duration), timeout=timeout) + def read_tree_values_flat(self, path, depth=1, check_exit_code=True): + command = 'read_tree_values {} {}'.format(path, depth) + output = self._execute_util(command, as_root=self.is_rooted, + check_exit_code=check_exit_code) + result = {} + for entry in output.strip().split('\n'): + path, value = entry.strip().split(':', 1) + result[path] = value + return result + + def read_tree_values(self, path, depth=1, dictcls=dict, check_exit_code=True): + value_map = self.read_tree_values_flat(path, depth, check_exit_code) + return _build_path_tree(value_map, path, self.path.sep, dictcls) + # internal methods def _setup_shutils(self): @@ -1558,3 +1572,32 @@ def _get_part_name(section): if name is None: name = '{}/{}/{}'.format(implementer, part, variant) return name + + +def _build_path_tree(path_map, basepath, sep=os.path.sep, dictcls=dict): + """ + Convert a flat mapping of paths to values into a nested structure of + dict-line object (``dict``'s by default), mirroring the directory hierarchy + represented by the paths relative to ``basepath``. + + """ + def process_node(node, path, value): + parts = path.split(sep, 1) + if len(parts) == 1: # leaf + node[parts[0]] = value + else: # branch + if parts[0] not in node: + node[parts[0]] = dictcls() + process_node(node[parts[0]], parts[1], value) + + relpath_map = {os.path.relpath(p, basepath): v + for p, v in path_map.iteritems()} + + if len(relpath_map) == 1 and relpath_map.keys()[0] == '.': + result = relpath_map.values()[0] + else: + result = dictcls() + for path, value in relpath_map.iteritems(): + process_node(result, path, value) + + return result diff --git a/doc/target.rst b/doc/target.rst index 08472e2..ae6ddb0 100644 --- a/doc/target.rst +++ b/doc/target.rst @@ -327,6 +327,32 @@ Target some sysfs entries silently failing to set the written value without returning an error code. +.. method:: Target.read_tree_values(path, depth=1, dictcls=dict): + + Read values of all sysfs (or similar) file nodes under ``path``, traversing + up to the maximum depth ``depth``. + + Returns a nested structure of dict-like objects (``dict``\ s by default) that + follows the structure of the scanned sub-directory tree. The top-level entry + has a single item who's key is ``path``. If ``path`` points to a single file, + the value of the entry is the value ready from that file node. Otherwise, the + value is a dict-line object with a key for every entry under ``path`` + mapping onto its value or further dict-like objects as appropriate. + + :param path: sysfs path to scan + :param depth: maximum depth to descend + :param dictcls: a dict-like type to be used for each level of the hierarchy. + +.. method:: Target.read_tree_values_flat(path, depth=1): + + Read values of all sysfs (or similar) file nodes under ``path``, traversing + up to the maximum depth ``depth``. + + Returns a dict mapping paths of file nodes to corresponding values. + + :param path: sysfs path to scan + :param depth: maximum depth to descend + .. method:: Target.reset() Soft reset the target. Typically, this means executing ``reboot`` on the