From f0bc7ba45d0495760ddb5efd3ec176ef3da2c13f Mon Sep 17 00:00:00 2001 From: sgherdao <104869962+sgherdao@users.noreply.github.com> Date: Sat, 6 Jul 2024 13:00:43 +0100 Subject: [PATCH] Fixes user/group management and documentation - Fix KeyError due to show_all=True missing from client.all_labs - Move the formatting logic to the view, pass list of dict to the view after minimum processing (mappings) - Add placeholders for the user dict when the logged-in user is non-admin - Update documentation, including doc plugin and README.md dsada --- README.md | 14 +-- examples/plugins/README.md | 152 +++++++++++++++++++++++++++ virl/cli/groups/ls/commands.py | 9 +- virl/cli/users/ls/commands.py | 28 +++-- virl/cli/views/groups/group_views.py | 4 +- virl/cli/views/users/user_views.py | 6 +- 6 files changed, 189 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e1f10e9..c8fbd27 100644 --- a/README.md +++ b/README.md @@ -817,13 +817,13 @@ Groups on Server ╞════════╪═══════════════╪══════════╪════════════════════════════════════╡ │ users │ All Users │ admin │ nso-ha (read_only) │ │ │ │ john │ netbox (read_only) │ -│ │ │ sgherdao │ Multi Platform Network (read_only) │ -│ │ │ saladoo │ BFD (read_only) │ -│ │ │ kmilo │ Vodafone-PT (read_only) │ -│ │ │ cisco1 │ fastapi-ubuntu (read_only) │ -│ │ │ alice │ upm-quick-test (read_only) │ -│ │ │ bob │ Quantum IPsec (read_only) │ -│ │ │ mike │ Multi-SA HSRP (read_only) │ +│ │ │ bob │ Multi Platform Network (read_only) │ +│ │ │ │ BFD (read_only) │ +│ │ │ │ Vodafone-PT (read_only) │ +│ │ │ │ fastapi-ubuntu (read_only) │ +│ │ │ │ upm-quick-test (read_only) │ +│ │ │ │ Quantum IPsec (read_only) │ +│ │ │ │ Multi-SA HSRP (read_only) │ ╘════════╧═══════════════╧══════════╧════════════════════════════════════╛ ``` diff --git a/examples/plugins/README.md b/examples/plugins/README.md index e920024..b318fee 100644 --- a/examples/plugins/README.md +++ b/examples/plugins/README.md @@ -139,6 +139,9 @@ support. The list of viewers are: - **image_def** : Render the output of `cml definitions image ls` - **node_def** : Render the output of `cml definitions node ls` - **search** : Render the output of `cml search` +- **cluster** : Render the output of `cml cluster info` +- **user** : Render the output of `cml users ls` +- **group** : Render the output of `cml groups ls` Each one of these viewers is discussed in more details below. @@ -230,6 +233,155 @@ The value of `computes` is a dictionary of compute nodes, keyed on the node ID. } ``` +#### _user_ Viewer + +The _user_ viewer renders the output of `cml users ls` (i.e., the list of user on the server). Your viewer will be passed a dict, `users` in the `kwargs` dictionary. +> _Note_: Non-admin users receive limited user info from the CML API (only user UUID and username). `groups` and `lab` keys will be filled with an empty list. The other missing keys will be filled with the string `"N/A"`. + +The value of `users` is a list of dictionary of users. For example: + +```json +[ + { + "id": "00000000-0000-4000-a000-000000000000", + "created": "2022-09-30T10:03:53+00:00", + "modified": "2024-06-30T23:39:08+00:00", + "username": "admin", + "fullname": "", + "email": "", + "description": "", + "admin": true, + "directory_dn": "", + "groups": [ + "users" + ], + "labs": [ + "Multi Platform Network", + "fastapi-ubuntu", + "Multi-SA HSRP" + ], + "opt_in": true, + "resource_pool": null, + "tour_version": "2.6.1+build.11", + "pubkey_info": "" + }, + { + { + "id": "336e0bb9-5b4d-418d-ae48-92544ef5c590", + "created": "2024-06-21T22:15:13+00:00", + "modified": "2024-06-30T23:37:22+00:00", + "username": "alice", + "fullname": "", + "email": "", + "description": "", + "admin": false, + "directory_dn": "", + "groups": [], + "labs": [ + "WONDERLAB" + ], + "opt_in": true, + "resource_pool": null, + "tour_version": "2.7.0+build.4", + "pubkey_info": "" + }, +] +``` + +A non-admin user will get: + +```json +[ + { + "id": "00000000-0000-4000-a000-000000000000", + "username": "admin", + "groups": [], + "labs": [], + "created": "N/A", + "modified": "N/A", + "fullname": "N/A", + "email": "N/A", + "description": "N/A", + "admin": "N/A", + "directory_dn": "N/A", + "opt_in": "N/A", + "resource_pool": "N/A", + "tour_version": "N/A", + "pubkey_info": "N/A" + }, + { + "id": "336e0bb9-5b4d-418d-ae48-92544ef5c590", + "username": "alice", + "groups": [], + "labs": [], + "created": "N/A", + "modified": "N/A", + "fullname": "N/A", + "email": "N/A", + "description": "N/A", + "admin": "N/A", + "directory_dn": "N/A", + "opt_in": "N/A", + "resource_pool": "N/A", + "tour_version": "N/A", + "pubkey_info": "N/A" + }, +] +``` + + + +#### _group_ Viewer + +The _group_ viewer renders the output of `cml groups ls` (i.e., the list of groups on the server). Your viewer will be passed a dict, `groups` in the `kwargs` dictionary. +Note: non-admin users are only allowed to get group information for the groups they belong to. + +The value of `groups` is a list of dictionaries. For example: + +```json +[ + { + "id": "48c9c605-552f-4666-bd23-5b68cf4de665", + "created": "2024-06-20T02:54:38+00:00", + "modified": "2024-06-26T16:10:34+00:00", + "name": "users", + "description": "All Users", + "members": [ + "admin", + "bob", + "mike" + ], + "labs": [ + { + "title": "Multi Platform Network", + "permission": "read_only" + }, + { + "title": "BFD", + "permission": "read_only" + }, + ] + }, + { + "id": "a7ae7b7a-8e59-42e9-aa6c-a193a8463c77", + "created": "2024-06-25T22:30:31+00:00", + "modified": "2024-07-06T12:44:58+00:00", + "name": "cryptopals", + "description": "", + "members": [ + "bob", + "alice" + ], + "labs": [ + { + "title": "Multi-SA HSRP", + "permission": "read_only" + } + ] + }, +] +``` + ### Common Functions In general, your plugins have access to all of the power in the `virl2_client` Python library. However, in practice, diff --git a/virl/cli/groups/ls/commands.py b/virl/cli/groups/ls/commands.py index 74cf8de..5ebf18e 100644 --- a/virl/cli/groups/ls/commands.py +++ b/virl/cli/groups/ls/commands.py @@ -14,14 +14,13 @@ def list_groups(verbose): server = VIRLServer() client = get_cml_client(server) user_mapping = {u["id"]: u["username"] for u in client.user_management.users()} - labs_mapping = {lab.id: lab.title for lab in client.all_labs()} + labs_mapping = {lab.id: lab.title for lab in client.all_labs(show_all=True)} groups = client.group_management.groups() for group in groups: - group["members"] = "\n".join(user_mapping[uid] for uid in group["members"]) - group["labs"] = "\n".join(f"{labs_mapping[lab['id']]} ({lab['permission']})" for lab in group["labs"]) - + group["members"] = [user_mapping[uid] for uid in group["members"]] + group["labs"] = [{"title": labs_mapping[lab["id"]], "permission": lab["permission"]} for lab in group["labs"]] try: pl = ViewerPlugin(viewer="group") pl.visualize(groups=groups) except NoPluginError: - group_list_table(groups, verbose) + group_list_table(groups, verbose=verbose) diff --git a/virl/cli/users/ls/commands.py b/virl/cli/users/ls/commands.py index 9551d51..757749e 100644 --- a/virl/cli/users/ls/commands.py +++ b/virl/cli/users/ls/commands.py @@ -15,16 +15,30 @@ def list_users(verbose): client = get_cml_client(server) users = client.user_management.users() - - labs_mapping = {lab.id: lab.title for lab in client.all_labs()} + user_keys = ( + "id", + "created", + "modified", + "username", + "fullname", + "email", + "description", + "admin", + "directory_dn", + "opt_in", + "resource_pool", + "tour_version", + "pubkey_info", + ) + labs_mapping = {lab.id: lab.title for lab in client.all_labs(show_all=True)} group_mapping = {g["id"]: g["name"] for g in client.group_management.groups()} - for user in users: - user["groups"] = "\n".join(group_mapping[group_id] for group_id in user["groups"]) - user["labs"] = "\n".join(str(labs_mapping[lab_id]) for lab_id in user["labs"]) - + user["groups"] = [group_mapping[group_id] for group_id in user.get("groups", [])] + user["labs"] = [labs_mapping[lab_id] for lab_id in user.get("labs", [])] + for k in user_keys: + user.setdefault(k, "N/A") try: pl = ViewerPlugin(viewer="user") pl.visualize(users=users) except NoPluginError: - user_list_table(users, verbose) + user_list_table(users, verbose=verbose) diff --git a/virl/cli/views/groups/group_views.py b/virl/cli/views/groups/group_views.py index 3b0d479..5ce79c8 100644 --- a/virl/cli/views/groups/group_views.py +++ b/virl/cli/views/groups/group_views.py @@ -19,8 +19,8 @@ def group_list_table(groups, verbose=False): wrapped_description = textwrap.fill(group["description"], width=20) tr.append(wrapped_description) - tr.append(group["members"]) - tr.append(group["labs"]) + tr.append("\n".join(group["members"])) + tr.append("\n".join(f"{lab['title']} ({lab['permission']})" for lab in group["labs"])) table.append(tr) # wrap the output in this try/except block as some terminals # may have problem with the 'fancy_grid' diff --git a/virl/cli/views/users/user_views.py b/virl/cli/views/users/user_views.py index a2f38ed..7062001 100644 --- a/virl/cli/views/users/user_views.py +++ b/virl/cli/views/users/user_views.py @@ -5,7 +5,7 @@ import tabulate -def user_list_table(users, verbose): +def user_list_table(users, verbose=False): click.secho("Users on Server", fg="green") table = [] headers = ["ID"] if verbose else [] @@ -19,8 +19,8 @@ def user_list_table(users, verbose): wrapped_fullname = textwrap.fill(user["fullname"], width=20) tr.append(wrapped_fullname) tr.append(user["email"]) - tr.append(user["groups"]) - tr.append(user["labs"]) + tr.append("\n".join(user["groups"])) + tr.append("\n".join(user["labs"])) table.append(tr) # wrap the output in this try/except block as some terminals # may have problem with the 'fancy_grid'