Brought up to date.
This commit is contained in:
33
README.md
33
README.md
@@ -1,33 +0,0 @@
|
||||
# GhostRunner
|
||||
|
||||
This project aims to create a way to schedule commands to be run as soon as possible when they offline initialy.<br>
|
||||
The way to accomplish this is to create a tracked task list, and keep track of it has been successfully done.
|
||||
|
||||
# Technical details.
|
||||
|
||||
Go(lang) backend server which exposes an HTTP API which can be used to add tasks to the process.<br>
|
||||
Python executor/runner which actually executes the commands, Python was chosen because of the availability of: [LibMeshCtrl Python Library](https://pypi.org/project/libmeshctrl/).<br>
|
||||
|
||||
Create a python virtual environment inside the `runner` folder.
|
||||
|
||||
# JSON Templates:
|
||||
|
||||
TokenBody:
|
||||
```json
|
||||
{
|
||||
"tokenname": "NewUsableToken",
|
||||
"authtoken": "admintokengoeshereofcourse",
|
||||
"selectedtoken": "thetokenyouwanttoremove"
|
||||
}
|
||||
```
|
||||
TaskBody:
|
||||
```json
|
||||
{
|
||||
"name": "DeployTask",
|
||||
"authtoken": "abc123securetoken",
|
||||
"data": {
|
||||
"command": "deploy-app",
|
||||
"nodeids": "node1,node2,node3"
|
||||
}
|
||||
}
|
||||
```
|
||||
128
docs/README.md
Normal file
128
docs/README.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# GhostRunner
|
||||
|
||||
This project aims to create a way to schedule commands to be run as soon as possible when they offline initialy.<br>
|
||||
The way to accomplish this is to create a tracked task list, and keep track of it has been successfully done.
|
||||
|
||||
# Technical details.
|
||||
|
||||
Go(lang) backend server which exposes an HTTP API which can be used to add tasks to the process.<br>
|
||||
Python executor/runner which actually executes the commands, Python was chosen because of the availability of: [LibMeshCtrl Python Library](https://pypi.org/project/libmeshctrl/).<br>
|
||||
|
||||
Create a python virtual environment inside the `runner` folder.
|
||||
|
||||
# JSON Templates:
|
||||
|
||||
Following is mock data.
|
||||
|
||||
### `InfoResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"message": "Request successful",
|
||||
"data": {
|
||||
"example": "This is some mock data"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `TokenCreateDetails`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "DeploymentToken123"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `TokenCreateBody`
|
||||
|
||||
```json
|
||||
{
|
||||
"authtoken": "abc123securetoken",
|
||||
"details": {
|
||||
"name": "DeploymentToken123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `TokenListBody`
|
||||
|
||||
```json
|
||||
{
|
||||
"authtoken": "abc123securetoken"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `TaskData`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "UpdateScript",
|
||||
"command": "sudo apt update && sudo apt upgrade -y",
|
||||
"nodeids": ["node-001", "node-002", "node-003"],
|
||||
"creation": "2025-05-27T10:15:30Z",
|
||||
"status": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `TaskBody`
|
||||
|
||||
```json
|
||||
{
|
||||
"authtoken": "abc123securetoken",
|
||||
"details": {
|
||||
"name": "UpdateScript",
|
||||
"command": "sudo apt update && sudo apt upgrade -y",
|
||||
"nodeids": ["node-001", "node-002", "node-003"],
|
||||
"creation": "2025-05-27T10:15:30Z",
|
||||
"status": "pending"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Device`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "RaspberryPi-01",
|
||||
"nodeid": "node-001"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `PyOnlineDevices`
|
||||
|
||||
```json
|
||||
{
|
||||
"online_devices": [
|
||||
{
|
||||
"name": "RaspberryPi-01",
|
||||
"nodeid": "node-001"
|
||||
},
|
||||
{
|
||||
"name": "Server-02",
|
||||
"nodeid": "node-005"
|
||||
}
|
||||
],
|
||||
"offline_devices": [
|
||||
{
|
||||
"name": "IoT-Gateway",
|
||||
"nodeid": "node-003"
|
||||
}
|
||||
],
|
||||
"total_devices": 3
|
||||
}
|
||||
```
|
||||
1
docs/RUNNER.md
Normal file
1
docs/RUNNER.md
Normal file
@@ -0,0 +1 @@
|
||||
# GhostRunner Python Runner module.
|
||||
@@ -2,6 +2,10 @@ import meshctrl
|
||||
from json import dumps
|
||||
|
||||
class connect:
|
||||
@staticmethod
|
||||
async def quit(session: meshctrl.Session) -> None: # Function to use when quitting, but gracefully.
|
||||
await session.close()
|
||||
|
||||
@staticmethod
|
||||
async def connect(hostname: str, username: str, password: str) -> meshctrl.Session:
|
||||
session = meshctrl.Session(
|
||||
@@ -13,8 +17,21 @@ class connect:
|
||||
return session
|
||||
|
||||
@staticmethod
|
||||
async def list_online(session: meshctrl.Session,
|
||||
mode: str = "online") -> dict: # Default is return online devices, but function can also return the offline devices if specified.
|
||||
async def run(session: meshctrl.Session, command: str, nodeids: list[str]) -> None:
|
||||
try:
|
||||
response = await session.run_command(nodeids=nodeids,
|
||||
command=command,
|
||||
ignore_output=False,
|
||||
timeout=900)
|
||||
except Exception as error:
|
||||
print("Run Command failed.", error)
|
||||
return
|
||||
|
||||
for device in response:
|
||||
print(dumps(response[device]["result"]))
|
||||
|
||||
@staticmethod
|
||||
async def list_online(session: meshctrl.Session) -> dict: # Default is return online devices, but function can also return the offline devices if specified.
|
||||
|
||||
raw_device_list = await session.list_devices()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/python3
|
||||
|
||||
import argparse
|
||||
from ast import literal_eval
|
||||
import asyncio
|
||||
from json import dumps
|
||||
|
||||
@@ -13,18 +14,25 @@ def cmd_flags() -> argparse.Namespace:
|
||||
parser.add_argument("-lo", "--list-online", action='store_true', help="Specify if the program needs to list online devices.")
|
||||
parser.add_argument("-rc", "--run", action='store_true', help="Make the program run a command.")
|
||||
parser.add_argument("--command", type=str, help="Specify the actual command that is going to run.")
|
||||
parser.add_argument("--nodeids", type=str, help="Specify which nodes the command is going to be run on.")
|
||||
parser.add_argument('--nodeids', nargs='+', help='List of node IDs')
|
||||
|
||||
parser.add_argument("-i", "--indent", action='store_true', help="Specify whether the output needs to be indented.")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
async def main():
|
||||
async def prepare_command(command: str, nodeids: list[str]) -> list[str]: # Have some checks so it happens correctly.
|
||||
if len(nodeids) < 1 or len(command) < 1:
|
||||
print("No nodeids or command passed... quiting.")
|
||||
return []
|
||||
|
||||
return nodeids
|
||||
|
||||
async def main() -> None:
|
||||
args = cmd_flags()
|
||||
credentials = utilities.load_config()
|
||||
session = await connect.connect(credentials["hostname"],
|
||||
credentials["username"],
|
||||
credentials["password"])
|
||||
credentials["username"],
|
||||
credentials["password"])
|
||||
|
||||
if args.list_online:
|
||||
online_devices = await connect.list_online(session)
|
||||
@@ -32,12 +40,18 @@ async def main():
|
||||
print(dumps(online_devices,indent=4))
|
||||
else:
|
||||
print(dumps(online_devices))
|
||||
return await connect.quit(session) # Exit gracefully. Because python.
|
||||
|
||||
if args.run:
|
||||
if not args.command or not args.nodeids:
|
||||
return
|
||||
print("When using run, also use --comand and --nodeids")
|
||||
return await connect.quit(session) # Exit gracefully. Because python.
|
||||
|
||||
print(args.nodeids)
|
||||
command = args.command
|
||||
nodeids = args.nodeids
|
||||
nodeids = await prepare_command(command, nodeids)
|
||||
|
||||
await connect.run(session, command, nodeids)
|
||||
|
||||
await session.close()
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
./../runner/venv/bin/python3 ./../runner/runner.py
|
||||
./../runner/venv/bin/python3 ./../runner/runner.py "$@"
|
||||
@@ -46,6 +46,7 @@ func ReadConf(configPath string) ConfigStruct {
|
||||
|
||||
section = inidata.Section(runnerSection)
|
||||
|
||||
// MeshCentral
|
||||
config.MeshHostname = section.Key("hostname").String()
|
||||
config.MeshUsername = section.Key("username").String()
|
||||
config.MeshPassword = section.Key("password").String()
|
||||
|
||||
Reference in New Issue
Block a user