mirror of
https://github.com/HuFlungDu/pylibmeshctrl.git
synced 2026-02-20 13:42:11 +00:00
Merge branch 'development' of github.com:HuFlungDu/pylibmeshctrl into development
This commit is contained in:
@@ -1496,7 +1496,6 @@ class Session(object):
|
||||
result[node]["result"].append(event["result"])
|
||||
if all(_["complete"] for key, _ in result.items()):
|
||||
return True
|
||||
|
||||
if start_data is not None:
|
||||
if _parse_event(start_data):
|
||||
return
|
||||
@@ -1542,6 +1541,80 @@ class Session(object):
|
||||
|
||||
return {n: v | {"result": "".join(v["result"])} for n,v in result.items()}
|
||||
|
||||
async def run_console_command(self, nodeids, command, powershell=False, runasuser=False, runasuseronly=False, ignore_output=False, timeout=None):
|
||||
'''
|
||||
Run a mesh console command on any number of nodes. WARNING: Non namespaced call. Calling this function again before it returns may cause unintended consequences.
|
||||
|
||||
Args:
|
||||
nodeids (str|list[str]): Unique ids of nodes on which to run the command
|
||||
command (str): Command to run
|
||||
ignore_output (bool): Don't bother trying to get the output. Every device will return an empty string for its result.
|
||||
timeout (int): duration in seconds to wait for a response before throwing an error
|
||||
|
||||
Returns:
|
||||
dict[str, ~meshctrl.types.RunCommandResponse]: Dict containing mapped output of the commands by device
|
||||
|
||||
Raises:
|
||||
:py:class:`~meshctrl.exceptions.ServerError`: Error text from server if there is a failure
|
||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||
ValueError: `Invalid device id` if device is not found
|
||||
asyncio.TimeoutError: Command timed out
|
||||
'''
|
||||
if isinstance(nodeids, str):
|
||||
nodeids = [nodeids]
|
||||
|
||||
def match_nodeid(id, ids):
|
||||
for nid in ids:
|
||||
if (nid == id):
|
||||
return nid
|
||||
if (nid[6:] == id):
|
||||
return nid
|
||||
if (f"node//{nid}" == id):
|
||||
return nid
|
||||
|
||||
result = {n: {"complete": False, "result": [], "command": command} for n in nodeids}
|
||||
async def _console():
|
||||
async for event in self.events({"action": "msg", "type": "console"}):
|
||||
node = match_nodeid(event["nodeid"], nodeids)
|
||||
if node:
|
||||
result[node]["result"].append(event["value"])
|
||||
result.setdefault(node, {})["complete"] = True
|
||||
if all(_["complete"] for key, _ in result.items()):
|
||||
break
|
||||
async def __(command, tg, tasks):
|
||||
data = await self._send_command(command, "run_console_command", timeout=timeout)
|
||||
|
||||
if data.get("type", None) != "runcommands" and data.get("result", "ok").lower() != "ok":
|
||||
raise exceptions.ServerError(data["result"])
|
||||
elif data.get("type", None) != "runcommands" and data.get("result", "ok").lower() == "ok":
|
||||
expect_response = False
|
||||
console_task = tg.create_task(asyncio.wait_for(_console(), timeout=timeout))
|
||||
if not ignore_output:
|
||||
userid = (await self.user_info())["_id"]
|
||||
for n in nodeids:
|
||||
device_info = await self.device_info(n, timeout=timeout)
|
||||
try:
|
||||
permissions = device_info.mesh.links.get(userid, {}).get("rights",constants.DeviceRights.norights)\
|
||||
# This should work for device rights, but it only seems to work for mesh rights. Not sure why, but I can't get the events to show up when the user only has individual device rights
|
||||
# |device_info.get("links", {}).get(userid, {}).get("rights", constants.DeviceRights.norights)
|
||||
# If we don't have agentconsole rights, we won't be able te read the output, so fill in blanks on this node
|
||||
if not permissions&constants.DeviceRights.agentconsole:
|
||||
result[n]["complete"] = True
|
||||
else:
|
||||
expect_response = True
|
||||
except AttributeError:
|
||||
result[n]["complete"] = True
|
||||
if expect_response:
|
||||
tasks.append(console_task)
|
||||
else:
|
||||
console_task.cancel()
|
||||
|
||||
tasks = []
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
tasks.append(tg.create_task(__({ "action": 'runcommands', "nodeids": nodeids, "type": 4, "cmds": command}, tg, tasks)))
|
||||
|
||||
return {n: v | {"result": "".join(v["result"])} for n,v in result.items()}
|
||||
|
||||
def shell(self, nodeid):
|
||||
'''
|
||||
Get a terminal shell on the given device
|
||||
|
||||
@@ -37,7 +37,9 @@ class Agent(object):
|
||||
self._clienturl = clienturl
|
||||
self._dockerurl = dockerurl
|
||||
r = requests.post(f"{self._clienturl}/add-agent", json={"url": f"{self._dockerurl}", "meshid": self.meshid})
|
||||
self.nodeid = r.json()["id"]
|
||||
agent_json = r.json()
|
||||
self.nodeid = agent_json["id"]
|
||||
self.nodehex = agent_json["hex"]
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
@@ -53,7 +53,7 @@ def add_agent():
|
||||
time.sleep(.1)
|
||||
else:
|
||||
raise Exception(f"Failed to start agent: {text}")
|
||||
return {"id": agent_id}
|
||||
return {"id": agent_id, "hex": agent_hex}
|
||||
|
||||
@api.route('/remove-agent/<agentid>', methods=['POST'])
|
||||
def remove_agent(agentid):
|
||||
|
||||
@@ -259,6 +259,19 @@ async def test_mesh_device(env):
|
||||
else:
|
||||
raise Exception("Run command on a device that doesn't exist did not raise an exception")
|
||||
|
||||
r = await admin_session.run_console_command([agent.nodeid, agent2.nodeid], "info", timeout=10)
|
||||
print("\ninfo run_console_command: {}\n".format(r))
|
||||
assert agent.nodeid in r[agent.nodeid]["result"], "Run console command gave bad response"
|
||||
assert agent2.nodeid in r[agent2.nodeid]["result"], "Run console command gave bad response"
|
||||
|
||||
# Test run_commands missing device
|
||||
try:
|
||||
await admin_session.run_console_command([agent.nodeid, "notanid"], "info", timeout=10)
|
||||
except* (meshctrl.exceptions.ServerError, ValueError):
|
||||
pass
|
||||
else:
|
||||
raise Exception("Run console command on a device that doesn't exist did not raise an exception")
|
||||
|
||||
# Test run commands with individual device permissions
|
||||
try:
|
||||
await unprivileged_session.run_command(agent.nodeid, "ls", timeout=10)
|
||||
|
||||
Reference in New Issue
Block a user