Added targeting of multiple groups and devices through a new 'keyword'

Through a new sorting method, all cool and logically formatted.
This commit is contained in:
dselen
2025-02-13 14:27:02 +01:00
committed by GitHub
8 changed files with 90 additions and 40 deletions

View File

@@ -20,37 +20,41 @@ To install, follow the following commands:<br>
### Linux setup:
```shell
```bash
git clone https://github.com/daanselen/meshbook
cd ./meshbook
python3 -m venv ./venv
source ./venv/bin/activate
pip3 install -r ./requirements.txt
cp ./templates/meshcentral.conf.template ./meshcentral.conf
```
### Windows setup:
### Windows setup (PowerShell, not cmd):
```shell
git clone https://github.com/daanselen/meshbook
cd ./meshbook
python3 -m venv ./venv
python -m venv ./venv # or python3 when done through the Microsoft Store.
.\venv\Scripts\activate # Make sure to check the terminal prefix.
pip3 install -r ./requirements.txt
cp .\templates\meshcentral.conf.template .\meshcentral.conf
```
Now copy the configuration template from ./templates and fill it in with the correct details. The url should start with `wss://`.<br>
Now copy the configuration template from ./templates and fill it in with the correct details (remove .template from the file) this is shown in the last step of the setup(s).<br>
The url should start with `wss://`.<br>
You can check pre-made examples in the examples directory, make sure the values are set to your situation.<br>
After this you can use meshbook, for example:
### Linux run:
```shell
```bash
python3 .\meshbook.py -pb .\examples\echo.yaml
```
### Windows run:
```shell
.\venv\Scripts\python.exe .\meshbook.py -pb .\examples\echo.yaml
.\venv\Scripts\python.exe .\meshbook.py -pb .\examples\echo_example.yaml
```
### How to check if everything is okay?
@@ -80,6 +84,7 @@ So to target for example a mesh/group in MeshCentral called: "Nerthus" do:
---
name: example configuration
group: "Nerthus"
#target_os: "Linux" # <--- according to os_categories.json
variables:
- name: var1
value: "This is the first variable"
@@ -118,6 +123,7 @@ You can expand the command chain as follows:<br>
---
name: Echo a string to the terminal through the meshbook example.
group: "Dev"
#target_os: "Linux" # <--- according to os_categories.json
variables:
- name: file
value: "/etc/os-release"
@@ -129,34 +135,30 @@ tasks:
The following response it received when executing the first yaml of the above files (without the `-s` parameters, which just outputs the below JSON).
```shell
python3 meshbook.py -pb examples/echo_example.yaml
~/meshbook$ python3 meshbook.py -pb examples/echo_example.yaml
----------------------------------------
Playbook: examples/echo_example.yaml
Operating System Categorisation file: ./os_categories.json
Congiguration file: ./meshcentral.conf
Target group: Development
Grace: True
Silent: False
----------------------------------------
Trying to load the MeshCentral account credential file...
Trying to load the Playbook yaml file and compile it into something workable...
Trying to load the Operating System categorisation JSON file...
Connecting to MeshCentral and establish a session using variables from previous credential file.
Generating group list with nodes and reference the targets from that.
----------------------------------------
Executing playbook on the targets.
Executing playbook on the target(s): Development.
Initiating grace-period...
1...
2...
3...
----------------------------------------
1. Running: Echo!
----------------------------------------
{
"Task 1": [
{
"complete": true,
"result": "PRETTY_NAME=\"Debian GNU/Linux 12 (bookworm)\" NAME=\"Debian GNU/Linux\" VERSION_ID=\"12\" VERSION=\"12 (bookworm)\" VERSION_CODENAME=bookworm ID=debian HOME_URL=\"https://www.debian.org/\" SUPPORT_URL=\"https://www.debian.org/support\" BUG_REPORT_URL=\"https://bugs.debian.org/\"\n",
"command": "echo $(cat /etc/os-release)",
"device_id": "<Node-Unique>",
"device_name": "raspberrypi5"
},
{
"complete": true,
"result": "PRETTY_NAME=\"Debian GNU/Linux 12 (bookworm)\" NAME=\"Debian GNU/Linux\" VERSION_ID=\"12\" VERSION=\"12 (bookworm)\" VERSION_CODENAME=bookworm ID=debian HOME_URL=\"https://www.debian.org/\" SUPPORT_URL=\"https://www.debian.org/support\" BUG_REPORT_URL=\"https://bugs.debian.org/\"\n",
"command": "echo $(cat /etc/os-release)",
"device_id": "<Node-Unique>",
"device_name": "Cubic"
}
]
}
{"Task 1": "ALL THE DATA"} # Not sharing due to PID
```
The above without `-s` is quite verbose. use `--help` to read about parameters and getting a minimal response for example.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -1,6 +1,7 @@
---
name: Refresh the apt cache
device: Cubic
device: "<Device-Name>"
#target_os: "Linux"
variables:
- name: package_manager
value: "apt"

View File

@@ -1,6 +1,7 @@
---
name: Refresh the apt cache
group: "Dev"
#target_os: "Linux"
variables:
- name: package_manager
value: "apt"

View File

@@ -1,7 +1,7 @@
---
name: Echo a string to the terminal through the meshbook example.
group: "Dev"
target_os: Debian GnU/Linux 12 (bookworm)
group: "Development"
target_os: "Linux"
variables:
- name: file
value: "/etc/os-release"

View File

@@ -1,6 +1,7 @@
---
name: Ping Multiple Points
group: "Kubernetes"
#target_os: "Debian"
variables:
- name: host1
value: "1.1.1.1"

View File

@@ -141,21 +141,53 @@ async def gather_targets(playbook: dict, group_list: dict[str, list[dict]], os_c
target_list = []
target_os = playbook.get("target_os")
if "device" in playbook:
pseudo_target = playbook["device"]
async def process_device_or_group(pseudo_target, group_list, os_categories, target_os) -> list[str]:
"""Helper function to process devices or groups."""
matched_devices = []
for group in group_list:
for device in group_list[group]:
if device["device_name"] == pseudo_target:
matched_devices = await filter_targets([device], os_categories, target_os)
matched_devices.append(device)
if matched_devices:
return await filter_targets(matched_devices, os_categories, target_os)
return []
match playbook:
case {"device": pseudo_target}: # Single device target
if isinstance(pseudo_target, str):
matched_devices = await process_device_or_group(pseudo_target, group_list, os_categories, target_os)
target_list.extend(matched_devices)
else:
console("\033[91mPlease use devices (Notice the 'S') for multiple devices.\x1B[0m", True)
case {"devices": pseudo_target}: # List of devices
if isinstance(pseudo_target, list):
for sub_pseudo_device in pseudo_target:
matched_devices = await process_device_or_group(sub_pseudo_device, group_list, os_categories, target_os)
target_list.extend(matched_devices)
elif "group" in playbook:
pseudo_target = playbook["group"]
else:
console("\033[91mThe 'devices' method is being used, but only one string is given. Did you mean 'device'?\x1B[0m", True)
if pseudo_target in group_list:
matched_devices = await filter_targets(group_list[pseudo_target], os_categories, target_os)
target_list.extend(matched_devices)
case {"group": pseudo_target}: # Single group target
if isinstance(pseudo_target, str) and pseudo_target in group_list:
matched_devices = await filter_targets(group_list[pseudo_target], os_categories, target_os)
target_list.extend(matched_devices)
else:
console("\033[91mPlease use groups (Notice the 'S') for multiple groups.\x1B[0m", True)
case {"groups": pseudo_target}: # List of groups
if isinstance(pseudo_target, list):
for sub_pseudo_target in pseudo_target:
if sub_pseudo_target in group_list:
matched_devices = await filter_targets(group_list[sub_pseudo_target], os_categories, target_os)
target_list.extend(matched_devices)
else:
console("\033[91mThe 'groups' method is being used, but only one string is given. Did you mean 'group'?\x1B[0m", True)
return target_list
@@ -220,10 +252,10 @@ async def main():
console("Operating System Categorisation file: " + args.oscategories)
console("Congiguration file: " + args.conf)
if "device" in playbook:
console("Target device: " + playbook["device"])
console("Target device: " + str(playbook["device"]))
elif "group" in playbook:
console("Target group: " + playbook["group"])
console("Target group: " + str(playbook["group"]))
console("Grace: " + str((not args.nograce))) # Negation of bool for correct explanation
console("Silent: False") # Can be pre-defined because if silent flag was passed then none of this would be printed.
@@ -249,7 +281,20 @@ async def main():
else:
console(("-" * 40))
target_name = playbook["group"] if "group" in playbook else playbook["device"] # Quickly get the name.
match playbook:
case {"group": candidate_target_name}:
target_name = candidate_target_name
case {"groups": candidate_target_name}:
target_name = str(candidate_target_name)
case {"device": candidate_target_name}:
target_name = candidate_target_name
case {"devices": candidate_target_name}:
target_name = str(candidate_target_name)
console(("\033[91mExecuting playbook on the target(s): " + target_name + ".\x1B[0m"))
if not args.nograce: