Skip to content

Commit

Permalink
feat(inventory): Allow board inventory output
Browse files Browse the repository at this point in the history
  • Loading branch information
MrKevinWeiss committed Aug 17, 2023
1 parent 5414df9 commit a3b4a84
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 56 deletions.
123 changes: 84 additions & 39 deletions docs/cli-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,56 @@ $ inet-nm-update-from-os . --board-info "echo board_1 board_2" --board-features
Getting features_provided for board_1
Getting features_provided for board_2

Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/board_info.json
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/board_info.json
```

1. Now we can add a board using the wizard. We can just add a mock device for now and for this example.
```bash
$ inet-nm-commission --mock-dev

Found 0 saved nodes in /tmp/pytest-of-weiss/pytest-14/test_cli_example0
Enter serial number [43190210341183772271]:
Found 0 saved nodes in /tmp/pytest-of-weiss/pytest-5/test_cli_example0
Enter serial number [68378266499947448987]:
board_1
Select board name for generic_vendor
> board_1
Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/nodes.json
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/nodes.json
```

2. Let's do it again so we have 2 `board_1` boards.
```bash
$ inet-nm-commission --mock-dev

Found 1 saved nodes in /tmp/pytest-of-weiss/pytest-5/test_cli_example0
Enter serial number [02292275306556290432]:
board_1
Found 1 saved nodes in /tmp/pytest-of-weiss/pytest-14/test_cli_example0
Enter serial number [02807064577242520563]:
Select board name for generic_vendor
> board_1
Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/nodes.json
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/nodes.json
```

3. We can also add the board with directly with the cli args.
```bash
$ inet-nm-commission --mock-dev --board board_2

Found 2 saved nodes in /tmp/pytest-of-weiss/pytest-14/test_cli_example0
Enter serial number [38177405136834385679]:
Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/nodes.json
Found 2 saved nodes in /tmp/pytest-of-weiss/pytest-5/test_cli_example0
Enter serial number [53501089861140397880]:
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/nodes.json
```

4. If we add a board that is not in our `board_info` list, we will need to confirm it is correct.
```bash
$ inet-nm-commission --mock-dev
Found 3 saved nodes in /tmp/pytest-of-weiss/pytest-14/test_cli_example0
Enter serial number [45405871127408525688]:
Found 3 saved nodes in /tmp/pytest-of-weiss/pytest-5/test_cli_example0
Enter serial number [38033817949869448782]:
Select board name for generic_vendor
> board_3
Board board_3 not in board list, continue? [y/N] Y
Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/nodes.json
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/nodes.json
```

5. If we have a device showing up on the list that should not be commissioned, we can ignore it. This will prevent accidentally commissioning it in the future and will not be selectable by this tool.
```bash
$ inet-nm-commission --mock-dev --ignore
Found 4 saved nodes in /tmp/pytest-of-weiss/pytest-14/test_cli_example0
Enter serial number [65026266254893701960]:
Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/nodes.json
Found 4 saved nodes in /tmp/pytest-of-weiss/pytest-5/test_cli_example0
Enter serial number [13638471407267323877]:
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/nodes.json
```

6. Let's see what we have, since these are mocked devices we the will never be connected, so we should always check missing boards.
Expand Down Expand Up @@ -142,9 +140,9 @@ NODE:2:BOARD:board_3: 2
```bash
$ inet-nm-exec "printenv | grep NM_" --missing --boards board_2
NODE:0:BOARD:board_2: NM_BOARD=board_2
NODE:0:BOARD:board_2: NM_CONFIG_DIR=/tmp/pytest-of-weiss/pytest-14/test_cli_example0
NODE:0:BOARD:board_2: NM_SERIAL=38177405136834385679
NODE:0:BOARD:board_2: NM_UID=783e6d149b761089aaef00431149715b
NODE:0:BOARD:board_2: NM_CONFIG_DIR=/tmp/pytest-of-weiss/pytest-5/test_cli_example0
NODE:0:BOARD:board_2: NM_SERIAL=53501089861140397880
NODE:0:BOARD:board_2: NM_UID=7c740cc754d112ea98d9ff8099d389e7
NODE:0:BOARD:board_2: NM_PORT=Unknown
NODE:0:BOARD:board_2: NM_IDX=0
```
Expand All @@ -157,7 +155,7 @@ $ inet-nm-exec "sleep 1" --missing --skip-dups --seq --boards board_1 board_2 bo

16. There is also some blocking of boards if they are being used, let's try it out by blocking `board_1` for some time.
```bash
$ inet-nm-exec "sleep 0.5" --missing --skip-dups --boards board_1
$ inet-nm-exec "sleep 1" --missing --skip-dups --boards board_1
```

17. Before the command finishes, in a different terminal, we can check to see what is available to us, notice we will be missing one of the `board_1` boards.
Expand Down Expand Up @@ -213,10 +211,10 @@ $ inet-nm-check --missing --only-used
```bash
$ inet-nm-free
Releasing all locks
Removing lock file /tmp/inet_nm/locks/8e4bce9da919575fc961402178766025.lock
Removing lock file /tmp/inet_nm/locks/783e6d149b761089aaef00431149715b.lock
Removing lock file /tmp/inet_nm/locks/d6329329ffa003dbffe88ac2663716cf.lock
Removing lock file /tmp/inet_nm/locks/7bd84abe9331a30a8fe1db0ef8695ef8.lock
Removing lock file /tmp/inet_nm/locks/7c740cc754d112ea98d9ff8099d389e7.lock
Removing lock file /tmp/inet_nm/locks/735aeda7849fcba12991f607f0ed84fd.lock
Removing lock file /tmp/inet_nm/locks/e41a9d16bf84cb7819726dbaa3e3e021.lock
Removing lock file /tmp/inet_nm/locks/ed808cc451d7642f13b06dc12b10f665.lock
All locks released
```

Expand All @@ -237,13 +235,13 @@ Getting features_provided for board_1
Getting features_provided for board_2
Getting features_provided for board_3

Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/board_info.json
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/board_info.json
```

26. Well we can updated the nodes cache in a separate step, no need to re-commission.
```bash
$ inet-nm-update-commissioned
Updated /tmp/pytest-of-weiss/pytest-14/test_cli_example0/nodes.json
Updated /tmp/pytest-of-weiss/pytest-5/test_cli_example0/nodes.json
```

27. Now we can see the new feature and board is available.
Expand All @@ -258,32 +256,79 @@ $ inet-nm-check --missing --feat-filter feature_board_3
```bash
$ inet-nm-export MY_CUSTOM_BOARD_ENV_VAR \\${NM_BOARD} --apply-to-shared
Added MY_CUSTOM_BOARD_ENV_VAR=\${NM_BOARD} to shared env vars
Written to /tmp/pytest-of-weiss/pytest-14/test_cli_example0/env.json
Written to /tmp/pytest-of-weiss/pytest-5/test_cli_example0/env.json
```

29. We can also do it only for specific board names, for example, if debugger settings or toolchain info is needed. In this case, we can filter as we wish and it will only take matching boards or features.
29. Apply a pattern to select only some boards or features.
```bash
$ inet-nm-export MY_CUSTOM_SETTING 1 --apply-to-boards --missing --feat-filter feature_board_3
Added MY_CUSTOM_SETTING=1 to env vars for boards {'board_3'}
Written to /tmp/pytest-of-weiss/pytest-14/test_cli_example0/env.json
$ inet-nm-export MY_CUSTOM_SETTING 1 --apply-pattern --missing --feat-filter feature_board_3
Added patterns: {'key': 'MY_CUSTOM_SETTING', 'val': '1', 'boards': None, 'feat_filter': ['feature_board_3'], 'feat_eval': None}
Written to /tmp/pytest-of-weiss/pytest-5/test_cli_example0/env.json
```

30. Finally we can apply env vars to specific nodes. This is based on the UID, therefor commissioning new nodes will not contain these env vars.
30. The pattern has higher priority than the shared env vars. Let's overwrite the shared variable for board 2
```bash
$ inet-nm-export MY_CUSTOM_BOARD_ENV_VAR board_2 --apply-pattern --missing --boards board_2
Added patterns: {'key': 'MY_CUSTOM_BOARD_ENV_VAR', 'val': 'board_2', 'boards': ['board_2'], 'feat_filter': None, 'feat_eval': None}
Written to /tmp/pytest-of-weiss/pytest-5/test_cli_example0/env.json
```

31. Finally we can apply env vars to specific nodes. This is based on the UID, therefor commissioning new nodes will not contain these env vars. This has the highest priority.
```bash
$ inet-nm-export MY_CUSTOM_NODE_HAS_SPECIAL_HARDWARE_FLAG 1 --missing --boards board_1 --skip-dups
Added MY_CUSTOM_NODE_HAS_SPECIAL_HARDWARE_FLAG=1 to env vars for nodes {'8e4bce9da919575fc961402178766025'}
Written to /tmp/pytest-of-weiss/pytest-14/test_cli_example0/env.json
Added MY_CUSTOM_NODE_HAS_SPECIAL_HARDWARE_FLAG=1 to env vars for nodes {'e41a9d16bf84cb7819726dbaa3e3e021'}
Written to /tmp/pytest-of-weiss/pytest-5/test_cli_example0/env.json
```

31. Now we can check each environment.
32. Now we can check each environment.
```bash
$ inet-nm-exec "printenv | grep MY_CUSTOM" --missing
NODE:0:BOARD:board_1: MY_CUSTOM_BOARD_ENV_VAR=\${NM_BOARD}
NODE:0:BOARD:board_1: MY_CUSTOM_NODE_HAS_SPECIAL_HARDWARE_FLAG=1
NODE:1:BOARD:board_1: MY_CUSTOM_BOARD_ENV_VAR=\${NM_BOARD}
NODE:2:BOARD:board_2: MY_CUSTOM_BOARD_ENV_VAR=\${NM_BOARD}
NODE:2:BOARD:board_2: MY_CUSTOM_BOARD_ENV_VAR=board_2
NODE:3:BOARD:board_3: MY_CUSTOM_SETTING=1
NODE:3:BOARD:board_3: MY_CUSTOM_BOARD_ENV_VAR=\${NM_BOARD}
```

33. Let's say I want to check the state of my boards. I can see a nice table of what is available, used, and missing.
```bash
$ inet-nm-inventory
-----------------------------------------------------------
| Board | Available | Used | Missing | Total |
-----------------------------------------------------------
| board_1 | 0 | 0 | 2 | 2 |
| board_2 | 0 | 0 | 1 | 1 |
| board_3 | 0 | 0 | 1 | 1 |
-----------------------------------------------------------
```

34. We can also get that in a machine readable way.
```bash
$ inet-nm-inventory --json
[
{
"available": 0,
"board": "board_1",
"missing": 2,
"total": 2,
"used": 0
},
{
"available": 0,
"board": "board_2",
"missing": 1,
"total": 1,
"used": 0
},
{
"available": 0,
"board": "board_3",
"missing": 1,
"total": 1,
"used": 0
}
]
```

That was the example to show off and test most of the features.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ console_scripts =
inet-nm-export = inet_nm.cli_export:main
inet-nm-check = inet_nm.cli_check:main
inet-nm-commission = inet_nm.cli_commission:main
inet-nm-inventory = inet_nm.cli_inventory:main
inet-nm-tty-from-uid = inet_nm.cli_tty_from_uid:main
inet-nm-update-commissioned = inet_nm.cli_update_commissioned:main
inet-nm-free = inet_nm.cli_free:main
Expand Down
67 changes: 51 additions & 16 deletions src/inet_nm/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
This is meant for evaluating inventory.
"""
import argparse
from typing import Dict, List
from typing import Dict, List, Tuple

import pyudev

Expand Down Expand Up @@ -234,19 +234,32 @@ def check_nodes(
return nodes


def check_filter_args(parser: argparse.ArgumentParser):
parser.add_argument(
"-f",
"--feat-filter",
nargs="+",
help="Filter all boards that don't provide these features",
)
parser.add_argument(
"-e", "--feat-eval", type=str, help="Evaluate features with this function"
)
parser.add_argument(
"-b",
"--boards",
nargs="+",
help="Use only the list of boards that match these names",
)


def check_args(parser: argparse.ArgumentParser):
"""
Define the arguments for the argparse parser.
Args:
parser: The argparse parser.
"""
parser.add_argument(
"-f",
"--feat-filter",
nargs="+",
help="Filter all boards that don't provide these features",
)

parser.add_argument(
"-a",
"--all-nodes",
Expand All @@ -256,9 +269,6 @@ def check_args(parser: argparse.ArgumentParser):
parser.add_argument(
"-m", "--missing", action="store_true", help="Show all missing boards"
)
parser.add_argument(
"-e", "--feat-eval", type=str, help="Evaluate features with this function"
)
parser.add_argument(
"-u", "--used", action="store_true", help="Show used boards as well"
)
Expand All @@ -268,12 +278,37 @@ def check_args(parser: argparse.ArgumentParser):
parser.add_argument(
"-s", "--skip-dups", action="store_true", help="Skip duplicate boards"
)
parser.add_argument(
"-b",
"--boards",
nargs="+",
help="Use only the list of boards that match these names",
)
check_filter_args(parser)


def get_inventory_nodes(
config: str,
feat_filter: List[str] = None,
feat_eval: str = None,
boards: List[str] = None,
) -> Tuple[List[NmNode], List[NmNode], List[NmNode]]:
"""
Get a list of nodes based on the provided parameters.
Args:
config: The configuration path for the nodes.
feat_filter: A list of features. Only the nodes that contain all of
these features will be returned.
feat_eval: The function to use to evaluate the features.
boards: A list of boards to use.
Returns:
A tuple of lists of nodes. The first list is the available nodes, the
second list is the used nodes, and the third list is the missing
nodes.
"""

kwargs = locals()
available = get_filtered_nodes(**kwargs)
used = get_filtered_nodes(only_used=True, **kwargs)
missing = get_filtered_nodes(missing=True, **kwargs)
total = get_filtered_nodes(all_nodes=True, **kwargs)
return available, used, missing, total


def get_filtered_nodes(
Expand Down
65 changes: 65 additions & 0 deletions src/inet_nm/cli_inventory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import argparse
import json

import inet_nm.check as chk
import inet_nm.config as cfg


def main():
"""CLI entrypoint for counting the inventory of boards."""
parser = argparse.ArgumentParser(description="Check the state of the boards")
cfg.config_arg(parser)
chk.check_filter_args(parser)
parser.add_argument("--json", action="store_true", help="Output in JSON format")

args = parser.parse_args()
kwargs = vars(args)
print_json = kwargs.pop("json")

(available, used, missing, total) = chk.get_inventory_nodes(**kwargs)

a_boards = chk.nodes_to_boards(available)
u_boards = chk.nodes_to_boards(used)
m_boards = chk.nodes_to_boards(missing)
t_boards = chk.nodes_to_boards(total)

boards = sorted(list(set(t_boards.keys())))

info = [
{
"board": key,
"available": a_boards.get(key, 0),
"used": u_boards.get(key, 0),
"missing": m_boards.get(key, 0),
"total": t_boards.get(key, 0),
}
for key in boards
]
if print_json:
out = json.dumps(info, indent=2, sort_keys=True)
print(out)
elif len(info) > 0:
# Get the longest str len of boards
max_board_len = max([len(i["board"]) for i in info])
brd_str = f"| {'Board':<{max_board_len + 1}}"
avl_str = f"| {'Available':<10}"
use_str = f"| {'Used':<10}"
mis_str = f"| {'Missing':<10}"
tot_str = f"| {'Total':<10}|"
full_str = brd_str + avl_str + use_str + mis_str + tot_str
print("-" * len(full_str))
print(full_str)
print("-" * len(full_str))
for i in info:
brd_str = f"| {i['board']:<{max_board_len + 1}}"
avl_str = f"| {i['available']:>9} "
use_str = f"| {i['used']:>9} "
mis_str = f"| {i['missing']:>9} "
tot_str = f"| {i['total']:>9} |"
full_str = brd_str + avl_str + use_str + mis_str + tot_str
print(full_str)
print("-" * len(full_str))


if __name__ == "__main__":
main()
Loading

0 comments on commit a3b4a84

Please sign in to comment.