mirror of
https://github.com/HuFlungDu/pylibmeshctrl.git
synced 2026-02-20 05:32:12 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b870aa25bd | ||
|
|
c63604f624 | ||
|
|
f0e09c0082 | ||
|
|
184ce3ef3e | ||
|
|
33680dab5d | ||
|
|
05f1bae04d | ||
|
|
b0b89b89e6 | ||
|
|
fdc2b11afd | ||
|
|
4ed332ca4c | ||
|
|
5f0f6a0ff9 | ||
|
|
c576eae48b |
52
README.rst
52
README.rst
@@ -29,15 +29,59 @@
|
|||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
=============
|
|
||||||
meshctrl
|
meshctrl
|
||||||
=============
|
========
|
||||||
|
|
||||||
|
Library for remotely interacting with a
|
||||||
|
`MeshCentral <https://meshcentral.com/>`__ server instance
|
||||||
|
|
||||||
Libmeshctrl implementation in python
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
pip install meshctrl
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
This module is implemented as a primarily asynchronous library
|
||||||
|
(asyncio), mostly through the `Session <https://pylibmeshctrl.readthedocs.io/en/latest/api/meshctrl.html#meshctrl.session.Session>`__ class. Because the library is asynchronous, you must wait for it to be
|
||||||
|
initialized before interacting with the server. The preferred way to do
|
||||||
|
this is to use the async context manager pattern:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
import meshctrl
|
||||||
|
|
||||||
|
async with meshctrl.Session(url, **options):
|
||||||
|
print(await session.list_users())
|
||||||
|
...
|
||||||
|
|
||||||
|
However, if you prefer to instantiate the object yourself, you can
|
||||||
|
simply use the `initialized <https://pylibmeshctrl.readthedocs.io/en/latest/api/meshctrl.html#meshctrl.session.Session.initialized>`__ property:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
session = meshctrl.Session(url, **options)
|
||||||
|
await session.initialized.wait()
|
||||||
|
|
||||||
|
Note that, in this case, you will be rquired to clean up tho session
|
||||||
|
using its `close <https://pylibmeshctrl.readthedocs.io/en/latest/api/meshctrl.html#meshctrl.session.Session.close>`__ method.
|
||||||
|
|
||||||
|
Session Parameters
|
||||||
|
------------------
|
||||||
|
|
||||||
|
``url``: URL of meshcentral server to connect to. Should start with
|
||||||
|
either "ws://" or "wss://".
|
||||||
|
|
||||||
|
``options``: optional parameters. Described at `Read the
|
||||||
|
Docs <https://pylibmeshctrl.readthedocs.io/en/latest/api/meshctrl.html#module-meshctrl.session>`__
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
|
||||||
|
API is documented in the `API
|
||||||
|
Docs <https://pylibmeshctrl.readthedocs.io/en/latest/api/meshctrl.html>`__
|
||||||
|
|
||||||
This is a library for interacting with a Mesh Central instance programatically. Written in python.
|
|
||||||
|
|
||||||
|
|
||||||
.. _pyscaffold-notes:
|
.. _pyscaffold-notes:
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
21
setup.cfg
21
setup.cfg
@@ -4,24 +4,19 @@
|
|||||||
# https://setuptools.pypa.io/en/latest/references/keywords.html
|
# https://setuptools.pypa.io/en/latest/references/keywords.html
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
name = meshctrl
|
name = libmeshctrl
|
||||||
description = Add a short description here!
|
description = Python package for interacting with a Meshcentral server instance
|
||||||
author = Josiah Baldwin
|
author = Josiah Baldwin
|
||||||
author_email = jbaldwin8889@gmail.com
|
author_email = jbaldwin8889@gmail.com
|
||||||
license = MIT
|
license = MIT
|
||||||
license_files = LICENSE.txt
|
license_files = LICENSE.txt
|
||||||
long_description = file: README.rst
|
long_description = file: README.rst
|
||||||
long_description_content_type = text/x-rst; charset=UTF-8
|
long_description_content_type = text/x-rst; charset=UTF-8
|
||||||
url = https://github.com/pyscaffold/pyscaffold/
|
url = https://github.com/HuFlungDu/pylibmeshctrl/
|
||||||
# Add here related links, for example:
|
# Add here related links, for example:
|
||||||
project_urls =
|
project_urls =
|
||||||
Documentation = https://pyscaffold.org/
|
Documentation = https://pylibmeshctrl.readthedocs.io/
|
||||||
# Source = https://github.com/pyscaffold/pyscaffold/
|
Source = https://github.com/HuFlungDu/pylibmeshctrl/
|
||||||
# Changelog = https://pyscaffold.org/en/latest/changelog.html
|
|
||||||
# Tracker = https://github.com/pyscaffold/pyscaffold/issues
|
|
||||||
# Conda-Forge = https://anaconda.org/conda-forge/pyscaffold
|
|
||||||
# Download = https://pypi.org/project/PyScaffold/#files
|
|
||||||
# Twitter = https://twitter.com/PyScaffold
|
|
||||||
|
|
||||||
# Change if running only on Windows, Mac or Linux (comma-separated)
|
# Change if running only on Windows, Mac or Linux (comma-separated)
|
||||||
platforms = any
|
platforms = any
|
||||||
@@ -41,14 +36,16 @@ package_dir =
|
|||||||
=src
|
=src
|
||||||
|
|
||||||
# Require a min/specific Python version (comma-separated conditions)
|
# Require a min/specific Python version (comma-separated conditions)
|
||||||
# python_requires = >=3.8
|
python_requires = >=3.8
|
||||||
|
|
||||||
# Add here dependencies of your project (line-separated), e.g. requests>=2.2,<3.0.
|
# Add here dependencies of your project (line-separated), e.g. requests>=2.2,<3.0.
|
||||||
# Version specifiers like >=2.2,<3.0 avoid problems due to API changes in
|
# Version specifiers like >=2.2,<3.0 avoid problems due to API changes in
|
||||||
# new major versions. This works if the required packages follow Semantic Versioning.
|
# new major versions. This works if the required packages follow Semantic Versioning.
|
||||||
# For more information, check out https://semver.org/.
|
# For more information, check out https://semver.org/.
|
||||||
install_requires =
|
install_requires =
|
||||||
importlib-metadata; python_version<"3.8"
|
importlib-metadata
|
||||||
|
cryptography>=43.0.3
|
||||||
|
websockets>=13.1
|
||||||
|
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
|
|||||||
@@ -15,10 +15,13 @@ except PackageNotFoundError: # pragma: no cover
|
|||||||
finally:
|
finally:
|
||||||
del version, PackageNotFoundError
|
del version, PackageNotFoundError
|
||||||
|
|
||||||
from . import session
|
from .session import Session
|
||||||
from . import constants
|
from . import constants
|
||||||
from . import shell
|
from . import shell
|
||||||
from . import tunnel
|
from . import tunnel
|
||||||
from . import util
|
from . import util
|
||||||
from . import files
|
from . import files
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
|
from . import device
|
||||||
|
from . import mesh
|
||||||
|
from . import user_group
|
||||||
@@ -22,9 +22,6 @@ class FileTransferError(MeshCtrlError):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
stats (dict): {"result" (str): Human readable result, "size" (int): number of bytes successfully transferred}
|
stats (dict): {"result" (str): Human readable result, "size" (int): number of bytes successfully transferred}
|
||||||
initialized (asyncio.Event): Event marking if the Session initialization has finished. Wait on this to wait for a connection.
|
|
||||||
alive (bool): Whether the session connection is currently alive
|
|
||||||
closed (asyncio.Event): Event that occurs when the session closes permanently
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, message, stats):
|
def __init__(self, message, stats):
|
||||||
self.stats = stats
|
self.stats = stats
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class Session(object):
|
|||||||
domain (str): Domain to connect to
|
domain (str): Domain to connect to
|
||||||
password (str): Password with which to connect. Can also be password generated from token.
|
password (str): Password with which to connect. Can also be password generated from token.
|
||||||
loginkey (str|bytes): Key from already handled login. Overrides username/password.
|
loginkey (str|bytes): Key from already handled login. Overrides username/password.
|
||||||
proxy (str): "url:port" to use for proxy server
|
proxy (str): "url:port" to use for proxy server NOTE: This is currently not implemented due to a limitation of the undersying websocket library. Upvote the issue if you find this important.
|
||||||
token (str): Login token. This appears to be superfluous
|
token (str): Login token. This appears to be superfluous
|
||||||
ignore_ssl (bool): Ignore SSL errors
|
ignore_ssl (bool): Ignore SSL errors
|
||||||
|
|
||||||
@@ -159,13 +159,20 @@ class Session(object):
|
|||||||
async def _send_data_task(self, websocket):
|
async def _send_data_task(self, websocket):
|
||||||
while True:
|
while True:
|
||||||
message = await self._message_queue.get()
|
message = await self._message_queue.get()
|
||||||
print(f"{self._user} send: {message}\n")
|
|
||||||
await websocket.send(message)
|
await websocket.send(message)
|
||||||
|
|
||||||
async def _listen_data_task(self, websocket):
|
async def _listen_data_task(self, websocket):
|
||||||
async for message in websocket:
|
async for message in websocket:
|
||||||
print(f"{self._user} recv: {message}\n")
|
await self._eventer.emit("raw", message)
|
||||||
data = json.loads(message)
|
# Meshcentral does pong wrong and breaks our parsing, so fix it here.
|
||||||
|
if message == '{action:"pong"}':
|
||||||
|
message = '{"action":"pong"}'
|
||||||
|
|
||||||
|
# Can't process non-json data, don't even try
|
||||||
|
try:
|
||||||
|
data = json.loads(message)
|
||||||
|
except SyntaxError:
|
||||||
|
continue
|
||||||
action = data.get("action", None)
|
action = data.get("action", None)
|
||||||
await self._eventer.emit("server_event", data)
|
await self._eventer.emit("server_event", data)
|
||||||
if action == "close":
|
if action == "close":
|
||||||
@@ -234,14 +241,14 @@ class Session(object):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
@util._check_socket
|
@util._check_socket
|
||||||
async def _send_command_no_response_id(self, data, timeout=None):
|
async def _send_command_no_response_id(self, data, action_override=None, timeout=None):
|
||||||
responded = asyncio.Event()
|
responded = asyncio.Event()
|
||||||
response = None
|
response = None
|
||||||
async def _(data):
|
async def _(data):
|
||||||
nonlocal response
|
nonlocal response
|
||||||
response = data
|
response = data
|
||||||
responded.set()
|
responded.set()
|
||||||
self._eventer.once(data["action"], _)
|
self._eventer.once(action_override if action_override is not None else data["action"], _)
|
||||||
await self._message_queue.put(json.dumps(data))
|
await self._message_queue.put(json.dumps(data))
|
||||||
await asyncio.wait_for(responded.wait(), timeout=timeout)
|
await asyncio.wait_for(responded.wait(), timeout=timeout)
|
||||||
if isinstance(response, Exception):
|
if isinstance(response, Exception):
|
||||||
@@ -268,6 +275,23 @@ class Session(object):
|
|||||||
"""
|
"""
|
||||||
return self._user_info
|
return self._user_info
|
||||||
|
|
||||||
|
async def ping(self, timeout=None):
|
||||||
|
'''
|
||||||
|
Ping the server. WARNING: Non namespaced call. Calling this function again before it returns may cause unintended consequences.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
timeout (int): duration in seconds to wait for a response before throwing an error
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {"action": "pong"}
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`~meshctrl.exceptions.ServerError`: Error from server
|
||||||
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
|
asyncio.TimeoutError: Command timed out
|
||||||
|
'''
|
||||||
|
data = await self._send_command_no_response_id({"action": "ping"}, action_override="pong", timeout=timeout)
|
||||||
|
return data
|
||||||
|
|
||||||
async def list_device_groups(self, timeout=None):
|
async def list_device_groups(self, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -284,7 +308,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
data = await self._send_command({"action": "meshes"}, "list_device_groups", timeout)
|
data = await self._send_command({"action": "meshes"}, "list_device_groups", timeout=timeout)
|
||||||
return [mesh.Mesh(m["_id"], self, **m) for m in data["meshes"]]
|
return [mesh.Mesh(m["_id"], self, **m) for m in data["meshes"]]
|
||||||
|
|
||||||
|
|
||||||
@@ -323,7 +347,7 @@ class Session(object):
|
|||||||
op["name"] = name
|
op["name"] = name
|
||||||
if message:
|
if message:
|
||||||
op["msg"] = message
|
op["msg"] = message
|
||||||
data = await self._send_command(op, "send_invite_email", timeout)
|
data = await self._send_command(op, "send_invite_email", timeout=timeout)
|
||||||
if ("result" in data and data["result"].lower() != "ok"):
|
if ("result" in data and data["result"].lower() != "ok"):
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
return True
|
return True
|
||||||
@@ -359,7 +383,7 @@ class Session(object):
|
|||||||
op["meshname"] = group
|
op["meshname"] = group
|
||||||
if flags != None:
|
if flags != None:
|
||||||
op["flags"] = flags
|
op["flags"] = flags
|
||||||
data = await self._send_command(op, "generate_invite_link", timeout)
|
data = await self._send_command(op, "generate_invite_link", timeout=timeout)
|
||||||
if ("result" in data and data["result"].lower() != "ok"):
|
if ("result" in data and data["result"].lower() != "ok"):
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
del data["tag"]
|
del data["tag"]
|
||||||
@@ -382,7 +406,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
data = await self._send_command({"action": "users"}, "list_users", timeout)
|
data = await self._send_command({"action": "users"}, "list_users", timeout=timeout)
|
||||||
if ("result" in data and data["result"].lower() != "ok"):
|
if ("result" in data and data["result"].lower() != "ok"):
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
return data["users"]
|
return data["users"]
|
||||||
@@ -401,7 +425,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
return (await self._send_command({"action": "wssessioncount"}, "list_user_sessions", timeout))["wssessions"]
|
return (await self._send_command({"action": "wssessioncount"}, "list_user_sessions", timeout=timeout))["wssessions"]
|
||||||
|
|
||||||
|
|
||||||
async def list_devices(self, details=False, group=None, meshid=None, timeout=None):
|
async def list_devices(self, details=False, group=None, meshid=None, timeout=None):
|
||||||
@@ -426,14 +450,14 @@ class Session(object):
|
|||||||
tasks = []
|
tasks = []
|
||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
if details:
|
if details:
|
||||||
tasks.append(tg.create_task(self._send_command_no_response_id({"action": "getDeviceDetails", "type":"json"}, timeout)))
|
tasks.append(tg.create_task(self._send_command_no_response_id({"action": "getDeviceDetails", "type":"json"}, timeout=timeout)))
|
||||||
elif group:
|
elif group:
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'nodes', "meshname": group}, "list_devices", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'nodes', "meshname": group}, "list_devices", timeout=timeout)))
|
||||||
elif meshid:
|
elif meshid:
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'nodes', "meshid": meshid}, "list_devices", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'nodes', "meshid": meshid}, "list_devices", timeout=timeout)))
|
||||||
else:
|
else:
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'meshes' }, "list_devices", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'meshes' }, "list_devices", timeout=timeout)))
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'nodes' }, "list_devices", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'nodes' }, "list_devices", timeout=timeout)))
|
||||||
|
|
||||||
res0 = tasks[0].result()
|
res0 = tasks[0].result()
|
||||||
if "result" in res0:
|
if "result" in res0:
|
||||||
@@ -478,6 +502,24 @@ class Session(object):
|
|||||||
node["mesh"] = mesh.Mesh(node.get("meshid"), self)
|
node["mesh"] = mesh.Mesh(node.get("meshid"), self)
|
||||||
return [device.Device(n["_id"], self, **n) for n in nodes]
|
return [device.Device(n["_id"], self, **n) for n in nodes]
|
||||||
|
|
||||||
|
async def raw_messages(self):
|
||||||
|
'''
|
||||||
|
Listen to raw messages from the server. These will be strings that have not been parsed at all. Consider this an emergency fallback if meshcentral sends something odd. You will get every message from the websocket.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
generator(data): A generator which will generate every message the server sends
|
||||||
|
'''
|
||||||
|
event_queue = asyncio.Queue()
|
||||||
|
async def _(data):
|
||||||
|
await event_queue.put(data)
|
||||||
|
self._eventer.on("raw", _)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
data = await event_queue.get()
|
||||||
|
yield data
|
||||||
|
finally:
|
||||||
|
self._eventer.off("server_event", _)
|
||||||
|
|
||||||
async def events(self, filter=None):
|
async def events(self, filter=None):
|
||||||
'''
|
'''
|
||||||
Listen to events from the server
|
Listen to events from the server
|
||||||
@@ -535,7 +577,7 @@ class Session(object):
|
|||||||
if limit:
|
if limit:
|
||||||
cmd["limit"] = limit
|
cmd["limit"] = limit
|
||||||
|
|
||||||
data = await self._send_command(cmd, "list_events", timeout)
|
data = await self._send_command(cmd, "list_events", timeout=timeout)
|
||||||
return data["events"]
|
return data["events"]
|
||||||
|
|
||||||
async def list_login_tokens(self, timeout=None):
|
async def list_login_tokens(self, timeout=None):
|
||||||
@@ -552,7 +594,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
return (await self._send_command_no_response_id({"action": "loginTokens"}, timeout))["loginTokens"]
|
return (await self._send_command_no_response_id({"action": "loginTokens"}, timeout=timeout))["loginTokens"]
|
||||||
|
|
||||||
async def add_login_token(self, name, expire=None, timeout=None):
|
async def add_login_token(self, name, expire=None, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -571,7 +613,7 @@ class Session(object):
|
|||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
cmd = { "action": 'createLoginToken', "name": name, "expire": 0 if not expire else expire }
|
cmd = { "action": 'createLoginToken', "name": name, "expire": 0 if not expire else expire }
|
||||||
data = await self._send_command_no_response_id(cmd, timeout)
|
data = await self._send_command_no_response_id(cmd, timeout=timeout)
|
||||||
del data["action"]
|
del data["action"]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -603,7 +645,7 @@ class Session(object):
|
|||||||
name = token["tokenUser"]
|
name = token["tokenUser"]
|
||||||
break
|
break
|
||||||
realnames.append(name)
|
realnames.append(name)
|
||||||
return (await self._send_command_no_response_id({ "action": 'loginTokens', "remove": realnames }, timeout))["loginTokens"]
|
return (await self._send_command_no_response_id({ "action": 'loginTokens', "remove": realnames }, timeout=timeout))["loginTokens"]
|
||||||
|
|
||||||
async def add_user(self, name, password=None, randompass=False, domain=None, email=None, emailverified=False, resetpass=False, realname=None, phone=None, rights=None, timeout=None):
|
async def add_user(self, name, password=None, randompass=False, domain=None, email=None, emailverified=False, resetpass=False, realname=None, phone=None, rights=None, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -651,7 +693,7 @@ class Session(object):
|
|||||||
if isinstance(realname, str):
|
if isinstance(realname, str):
|
||||||
op["realname"] = realname
|
op["realname"] = realname
|
||||||
|
|
||||||
data = await self._send_command(op, "add_user", timeout)
|
data = await self._send_command(op, "add_user", timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
return True
|
return True
|
||||||
@@ -706,7 +748,7 @@ class Session(object):
|
|||||||
op["realname"] = realname
|
op["realname"] = realname
|
||||||
if realname is True:
|
if realname is True:
|
||||||
op["realname"] = ''
|
op["realname"] = ''
|
||||||
data = await self._send_command(op, "edit_user", timeout)
|
data = await self._send_command(op, "edit_user", timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
return True
|
return True
|
||||||
@@ -732,7 +774,7 @@ class Session(object):
|
|||||||
elif (self._domain is not None) and ("/" not in userid):
|
elif (self._domain is not None) and ("/" not in userid):
|
||||||
userid = f"user/{self._domain}/{userid}"
|
userid = f"user/{self._domain}/{userid}"
|
||||||
|
|
||||||
data = await self._send_command({ "action": 'deleteuser', "userid": userid }, "remove_user", timeout)
|
data = await self._send_command({ "action": 'deleteuser', "userid": userid }, "remove_user", timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
return True
|
return True
|
||||||
@@ -760,7 +802,7 @@ class Session(object):
|
|||||||
op["domain"] = self._domain
|
op["domain"] = self._domain
|
||||||
elif self._domain is not None:
|
elif self._domain is not None:
|
||||||
op["domain"] = self._domain
|
op["domain"] = self._domain
|
||||||
data = await self._send_command(op, "add_user_group", timeout)
|
data = await self._send_command(op, "add_user_group", timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
|
|
||||||
@@ -795,7 +837,7 @@ class Session(object):
|
|||||||
|
|
||||||
if (not groupid.startswith("ugrp/")):
|
if (not groupid.startswith("ugrp/")):
|
||||||
groupid = f"ugrp//{groupid}"
|
groupid = f"ugrp//{groupid}"
|
||||||
data = await self._send_command({ "action": 'deleteusergroup', "ugrpid": groupid }, "remove_user_group", timeout)
|
data = await self._send_command({ "action": 'deleteusergroup', "ugrpid": groupid }, "remove_user_group", timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
return True
|
return True
|
||||||
@@ -814,7 +856,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
r = await self._send_command({"action": "usergroups"}, "list_user_groups", timeout)
|
r = await self._send_command({"action": "usergroups"}, "list_user_groups", timeout=timeout)
|
||||||
groups = []
|
groups = []
|
||||||
for key, val in r["ugroups"].items():
|
for key, val in r["ugroups"].items():
|
||||||
val["_id"] = key
|
val["_id"] = key
|
||||||
@@ -888,7 +930,7 @@ class Session(object):
|
|||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
tasks.append(tg.create_task(asyncio.wait_for(_(tg), timeout=timeout)))
|
tasks.append(tg.create_task(asyncio.wait_for(_(tg), timeout=timeout)))
|
||||||
tasks.append(tg.create_task(asyncio.wait_for(__(tg), timeout=timeout)))
|
tasks.append(tg.create_task(asyncio.wait_for(__(tg), timeout=timeout)))
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'addusertousergroup', "ugrpid": groupid, "usernames": usernames}, "add_users_to_user_group", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'addusertousergroup', "ugrpid": groupid, "usernames": usernames}, "add_users_to_user_group", timeout=timeout)))
|
||||||
|
|
||||||
|
|
||||||
res = tasks[2].result()
|
res = tasks[2].result()
|
||||||
@@ -922,7 +964,7 @@ class Session(object):
|
|||||||
if (not groupid.startswith("ugrp/")):
|
if (not groupid.startswith("ugrp/")):
|
||||||
groupid = f"ugrp//{groupid}"
|
groupid = f"ugrp//{groupid}"
|
||||||
|
|
||||||
data = await self._send_command({ "action": 'removeuserfromusergroup', "ugrpid": groupid, "userid": userid }, "remove_from_user_group", timeout)
|
data = await self._send_command({ "action": 'removeuserfromusergroup', "ugrpid": groupid, "userid": userid }, "remove_from_user_group", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -953,7 +995,7 @@ class Session(object):
|
|||||||
if rights is None:
|
if rights is None:
|
||||||
rights = 0
|
rights = 0
|
||||||
|
|
||||||
data = await self._send_command({ "action": 'adddeviceuser', "nodeid": nodeid, "userids": userids, "rights": rights}, "add_users_to_device", timeout)
|
data = await self._send_command({ "action": 'adddeviceuser', "nodeid": nodeid, "userids": userids, "rights": rights}, "add_users_to_device", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -981,7 +1023,7 @@ class Session(object):
|
|||||||
|
|
||||||
userids = [f"user//{u}" if not u.startswith("user//") else u for u in userids]
|
userids = [f"user//{u}" if not u.startswith("user//") else u for u in userids]
|
||||||
|
|
||||||
data = await self._send_command({ "action": 'adddeviceuser', "nodeid": nodeid, "usernames": userids, "rights": 0, "remove": True }, "remove_users_from_device", timeout)
|
data = await self._send_command({ "action": 'adddeviceuser', "nodeid": nodeid, "usernames": userids, "rights": 0, "remove": True }, "remove_users_from_device", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1018,7 +1060,7 @@ class Session(object):
|
|||||||
if consent:
|
if consent:
|
||||||
op["consent"] = consent
|
op["consent"] = consent
|
||||||
|
|
||||||
data = await self._send_command(op, "add_device_group", timeout)
|
data = await self._send_command(op, "add_device_group", timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
|
|
||||||
@@ -1053,7 +1095,7 @@ class Session(object):
|
|||||||
op["meshname"] = meshid
|
op["meshname"] = meshid
|
||||||
del op["meshid"]
|
del op["meshid"]
|
||||||
|
|
||||||
data = await self._send_command(op, "remove_device_group", timeout)
|
data = await self._send_command(op, "remove_device_group", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1107,7 +1149,7 @@ class Session(object):
|
|||||||
if consent is not None:
|
if consent is not None:
|
||||||
op["consent"] = consent
|
op["consent"] = consent
|
||||||
|
|
||||||
data = await self._send_command(op, "edit_device_group", timeout)
|
data = await self._send_command(op, "edit_device_group", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1138,7 +1180,7 @@ class Session(object):
|
|||||||
op["meshname"] = meshid
|
op["meshname"] = meshid
|
||||||
del op["meshid"]
|
del op["meshid"]
|
||||||
|
|
||||||
data = await self._send_command(op, "move_to_device_group", timeout)
|
data = await self._send_command(op, "move_to_device_group", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1171,7 +1213,7 @@ class Session(object):
|
|||||||
op["meshname"] = meshid
|
op["meshname"] = meshid
|
||||||
del op["meshid"]
|
del op["meshid"]
|
||||||
|
|
||||||
data = await self._send_command(op, "add_user_to_device_group", timeout)
|
data = await self._send_command(op, "add_user_to_device_group", timeout=timeout)
|
||||||
results = data["result"].split(",")
|
results = data["result"].split(",")
|
||||||
out = {}
|
out = {}
|
||||||
for i, result in enumerate(results):
|
for i, result in enumerate(results):
|
||||||
@@ -1213,7 +1255,7 @@ class Session(object):
|
|||||||
tasks = []
|
tasks = []
|
||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
for userid in userids:
|
for userid in userids:
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'removemeshuser', "userid": userid } | id_obj, "remove_users_from_device_group", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'removemeshuser', "userid": userid } | id_obj, "remove_users_from_device_group", timeout=timeout)))
|
||||||
|
|
||||||
out = {}
|
out = {}
|
||||||
for i, task in enumerate(tasks):
|
for i, task in enumerate(tasks):
|
||||||
@@ -1247,7 +1289,7 @@ class Session(object):
|
|||||||
if userid:
|
if userid:
|
||||||
op["userid"] = userid
|
op["userid"] = userid
|
||||||
|
|
||||||
data = await self._send_command(op, "broadcast", timeout)
|
data = await self._send_command(op, "broadcast", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1271,10 +1313,10 @@ class Session(object):
|
|||||||
'''
|
'''
|
||||||
tasks = []
|
tasks = []
|
||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'nodes' }, "device_info", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'nodes' }, "device_info", timeout=timeout)))
|
||||||
tasks.append(tg.create_task(self._send_command_no_response_id({ "action": 'getnetworkinfo', "nodeid": nodeid }, timeout)))
|
tasks.append(tg.create_task(self._send_command_no_response_id({ "action": 'getnetworkinfo', "nodeid": nodeid }, timeout=timeout)))
|
||||||
tasks.append(tg.create_task(self._send_command_no_response_id({ "action": 'lastconnect', "nodeid": nodeid }, timeout)))
|
tasks.append(tg.create_task(self._send_command_no_response_id({ "action": 'lastconnect', "nodeid": nodeid }, timeout=timeout)))
|
||||||
tasks.append(tg.create_task(self._send_command({ "action": 'getsysinfo', "nodeid": nodeid, "nodeinfo": True }, "device_info", timeout)))
|
tasks.append(tg.create_task(self._send_command({ "action": 'getsysinfo', "nodeid": nodeid, "nodeinfo": True }, "device_info", timeout=timeout)))
|
||||||
tasks.append(tg.create_task(self.list_device_groups(timeout=timeout)))
|
tasks.append(tg.create_task(self.list_device_groups(timeout=timeout)))
|
||||||
|
|
||||||
nodes, network, lastconnect, sysinfo, meshes = (_.result() for _ in tasks)
|
nodes, network, lastconnect, sysinfo, meshes = (_.result() for _ in tasks)
|
||||||
@@ -1344,7 +1386,7 @@ class Session(object):
|
|||||||
if consent is not None:
|
if consent is not None:
|
||||||
op["consent"] = consent
|
op["consent"] = consent
|
||||||
|
|
||||||
data = await self._send_command(op, "edit_device", timeout)
|
data = await self._send_command(op, "edit_device", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1402,7 +1444,7 @@ class Session(object):
|
|||||||
continue
|
continue
|
||||||
result[node]["result"].append(event["value"])
|
result[node]["result"].append(event["value"])
|
||||||
async def __(command):
|
async def __(command):
|
||||||
data = await self._send_command(command, "run_command", timeout)
|
data = await self._send_command(command, "run_command", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1479,7 +1521,7 @@ class Session(object):
|
|||||||
if isinstance(nodeids, str):
|
if isinstance(nodeids, str):
|
||||||
nodeids = [nodeids]
|
nodeids = [nodeids]
|
||||||
|
|
||||||
return await self._send_command({ "action": 'wakedevices', "nodeids": nodeids }, "wake_devices", timeout)
|
return await self._send_command({ "action": 'wakedevices', "nodeids": nodeids }, "wake_devices", timeout=timeout)
|
||||||
|
|
||||||
async def reset_devices(self, nodeids, timeout=None):
|
async def reset_devices(self, nodeids, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -1500,7 +1542,7 @@ class Session(object):
|
|||||||
if isinstance(nodeids, str):
|
if isinstance(nodeids, str):
|
||||||
nodeids = [nodeids]
|
nodeids = [nodeids]
|
||||||
|
|
||||||
return await self._send_command({ "action": 'poweraction', "nodeids": nodeids, "actiontype": 3 }, "reset_devices", timeout)
|
return await self._send_command({ "action": 'poweraction', "nodeids": nodeids, "actiontype": 3 }, "reset_devices", timeout=timeout)
|
||||||
|
|
||||||
async def sleep_devices(self, nodeids, timeout=None):
|
async def sleep_devices(self, nodeids, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -1521,7 +1563,7 @@ class Session(object):
|
|||||||
if isinstance(nodeids, str):
|
if isinstance(nodeids, str):
|
||||||
nodeids = [nodeids]
|
nodeids = [nodeids]
|
||||||
|
|
||||||
return await self._send_command({ "action": 'poweraction', "nodeids": nodeids, "actiontype": 4 }, "sleep_devices", timeout)
|
return await self._send_command({ "action": 'poweraction', "nodeids": nodeids, "actiontype": 4 }, "sleep_devices", timeout=timeout)
|
||||||
|
|
||||||
async def power_off_devices(self, nodeids, timeout=None):
|
async def power_off_devices(self, nodeids, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -1542,7 +1584,7 @@ class Session(object):
|
|||||||
if isinstance(nodeids, str):
|
if isinstance(nodeids, str):
|
||||||
nodeids = [nodeids]
|
nodeids = [nodeids]
|
||||||
|
|
||||||
return await self._send_command({ "action": 'poweraction', "nodeids": nodeids, "actiontype": 2 }, "power_off_devices", timeout)
|
return await self._send_command({ "action": 'poweraction', "nodeids": nodeids, "actiontype": 2 }, "power_off_devices", timeout=timeout)
|
||||||
|
|
||||||
async def list_device_shares(self, nodeid, timeout=None):
|
async def list_device_shares(self, nodeid, timeout=None):
|
||||||
'''
|
'''
|
||||||
@@ -1559,7 +1601,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
data = await self._send_command_no_response_id({ "action": 'deviceShares', "nodeid": nodeid }, timeout)
|
data = await self._send_command_no_response_id({ "action": 'deviceShares', "nodeid": nodeid }, timeout=timeout)
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
|
|
||||||
@@ -1604,7 +1646,7 @@ class Session(object):
|
|||||||
end = int(start.timestamp())
|
end = int(start.timestamp())
|
||||||
if end <= start:
|
if end <= start:
|
||||||
raise ValueError("End time must be ahead of start time")
|
raise ValueError("End time must be ahead of start time")
|
||||||
data = await self._send_command({ "action": 'createDeviceShareLink', "nodeid": nodeid, "guestname": name, "p": constants.SharingTypeEnum[type], "consent": consent, "start": start, "end": end }, "add_device_share", timeout)
|
data = await self._send_command({ "action": 'createDeviceShareLink', "nodeid": nodeid, "guestname": name, "p": constants.SharingTypeEnum[type], "consent": consent, "start": start, "end": end }, "add_device_share", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1633,7 +1675,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
data = await self._send_command({ "action": 'removeDeviceShare', "nodeid": nodeid, "publicid": shareid }, "remove_device_share", timeout)
|
data = await self._send_command({ "action": 'removeDeviceShare', "nodeid": nodeid, "publicid": shareid }, "remove_device_share", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1669,7 +1711,7 @@ class Session(object):
|
|||||||
tasks = []
|
tasks = []
|
||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
tasks.append(tg.create_task(asyncio.wait_for(_(), timeout=timeout)))
|
tasks.append(tg.create_task(asyncio.wait_for(_(), timeout=timeout)))
|
||||||
tasks.append({ "action": 'msg', "type": 'openUrl', "nodeid": nodeid, "url": url }, "device_open_url", timeout)
|
tasks.append({ "action": 'msg', "type": 'openUrl', "nodeid": nodeid, "url": url }, "device_open_url", timeout=timeout)
|
||||||
|
|
||||||
res = tasks[1].result()
|
res = tasks[1].result()
|
||||||
success = tasks[2].result()
|
success = tasks[2].result()
|
||||||
@@ -1701,7 +1743,7 @@ class Session(object):
|
|||||||
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
:py:class:`~meshctrl.exceptions.SocketError`: Info about socket closure
|
||||||
asyncio.TimeoutError: Command timed out
|
asyncio.TimeoutError: Command timed out
|
||||||
'''
|
'''
|
||||||
data = await self._send_command({ "action": 'msg', "type": 'messagebox', "nodeid": nodeid, "title": title, "msg": message }, "device_message", timeout)
|
data = await self._send_command({ "action": 'msg', "type": 'messagebox', "nodeid": nodeid, "title": title, "msg": message }, "device_message", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
@@ -1731,7 +1773,7 @@ class Session(object):
|
|||||||
if isinstance(nodeids, str):
|
if isinstance(nodeids, str):
|
||||||
nodeids = [nodeids]
|
nodeids = [nodeids]
|
||||||
|
|
||||||
data = self._send_command({ "action": 'toast', "nodeids": nodeids, "title": "MeshCentral", "msg": message }, "device_toast", timeout)
|
data = self._send_command({ "action": 'toast', "nodeids": nodeids, "title": "MeshCentral", "msg": message }, "device_toast", timeout=timeout)
|
||||||
|
|
||||||
if data.get("result", "ok").lower() != "ok":
|
if data.get("result", "ok").lower() != "ok":
|
||||||
raise exceptions.ServerError(data["result"])
|
raise exceptions.ServerError(data["result"])
|
||||||
|
|||||||
3
tests/.gitignore
vendored
3
tests/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/data
|
data
|
||||||
|
environment/scripts/meshcentral/users.json
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"admin": "3U6zP4iIes5ISH15XxjYLjJcCdw9jU0m", "privileged": "aiIO0zLMGsU7++FYVDNxhlpYlZ1andRB", "unprivileged": "Cz9OMV1wkVd9pXdWi4lkBAAu6TMt43MA"}
|
|
||||||
@@ -7,7 +7,7 @@ import io
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
async def test_commands(env):
|
async def test_commands(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
try:
|
try:
|
||||||
with env.create_agent(mesh.short_meshid) as agent:
|
with env.create_agent(mesh.short_meshid) as agent:
|
||||||
@@ -53,7 +53,7 @@ async def test_commands(env):
|
|||||||
assert (await admin_session.remove_device_group(mesh.meshid, timeout=10)), "Failed to remove device group"
|
assert (await admin_session.remove_device_group(mesh.meshid, timeout=10)), "Failed to remove device group"
|
||||||
|
|
||||||
async def test_upload_download(env):
|
async def test_upload_download(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
try:
|
try:
|
||||||
with env.create_agent(mesh.short_meshid) as agent:
|
with env.create_agent(mesh.short_meshid) as agent:
|
||||||
|
|||||||
@@ -8,14 +8,26 @@ import ssl
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
async def test_sanity(env):
|
async def test_sanity(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as s:
|
async with meshctrl.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as s:
|
||||||
|
got_pong = asyncio.Event()
|
||||||
|
async def _():
|
||||||
|
async for raw in s.raw_messages():
|
||||||
|
if raw == '{action:"pong"}':
|
||||||
|
got_pong.set()
|
||||||
|
break
|
||||||
|
ping_task = None
|
||||||
|
async with asyncio.TaskGroup() as tg:
|
||||||
|
tg.create_task(asyncio.wait_for(_(), timeout=5))
|
||||||
|
tg.create_task(asyncio.wait_for(got_pong.wait(), timeout=5))
|
||||||
|
ping_task = tg.create_task(s.ping(timeout=10))
|
||||||
|
print("\ninfo ping: {}\n".format(ping_task.result()))
|
||||||
print("\ninfo user_info: {}\n".format(await s.user_info()))
|
print("\ninfo user_info: {}\n".format(await s.user_info()))
|
||||||
print("\ninfo server_info: {}\n".format(await s.server_info()))
|
print("\ninfo server_info: {}\n".format(await s.server_info()))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def test_ssl(env):
|
async def test_ssl(env):
|
||||||
try:
|
try:
|
||||||
async with meshctrl.session.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=False) as s:
|
async with meshctrl.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=False) as s:
|
||||||
pass
|
pass
|
||||||
except* ssl.SSLCertVerificationError:
|
except* ssl.SSLCertVerificationError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import io
|
|||||||
thisdir = os.path.dirname(os.path.realpath(__file__))
|
thisdir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
async def test_admin(env):
|
async def test_admin(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session:
|
meshctrl.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session:
|
||||||
admin_users = await admin_session.list_users(timeout=10)
|
admin_users = await admin_session.list_users(timeout=10)
|
||||||
print("\ninfo list_users: {}\n".format(admin_users))
|
print("\ninfo list_users: {}\n".format(admin_users))
|
||||||
try:
|
try:
|
||||||
@@ -34,22 +34,22 @@ async def test_admin(env):
|
|||||||
|
|
||||||
async def test_users(env):
|
async def test_users(env):
|
||||||
try:
|
try:
|
||||||
async with meshctrl.session.Session(env.mcurl[3:], user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl[3:], user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
pass
|
pass
|
||||||
except* ValueError:
|
except* ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception("Connected with bad URL")
|
raise Exception("Connected with bad URL")
|
||||||
try:
|
try:
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", ignore_ssl=True) as admin_session:
|
||||||
pass
|
pass
|
||||||
except* meshctrl.exceptions.MeshCtrlError:
|
except* meshctrl.exceptions.MeshCtrlError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception("Connected with no password")
|
raise Exception("Connected with no password")
|
||||||
async with meshctrl.session.Session(env.mcurl+"/", user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
async with meshctrl.Session(env.mcurl+"/", user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session,\
|
meshctrl.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as unprivileged_session:
|
meshctrl.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as unprivileged_session:
|
||||||
|
|
||||||
assert len(await admin_session.list_users(timeout=10)) == 3, "Wrong number of users"
|
assert len(await admin_session.list_users(timeout=10)) == 3, "Wrong number of users"
|
||||||
|
|
||||||
@@ -74,17 +74,17 @@ async def test_users(env):
|
|||||||
assert len(await admin_session.list_users(timeout=10)) == 3, "Failed to remove user"
|
assert len(await admin_session.list_users(timeout=10)) == 3, "Failed to remove user"
|
||||||
|
|
||||||
async def test_login_token(env):
|
async def test_login_token(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as s:
|
async with meshctrl.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as s:
|
||||||
token = await s.add_login_token("test", expire=1, timeout=10)
|
token = await s.add_login_token("test", expire=1, timeout=10)
|
||||||
print("\ninfo add_login_token: {}\n".format(token))
|
print("\ninfo add_login_token: {}\n".format(token))
|
||||||
|
|
||||||
async with meshctrl.session.Session(env.mcurl, user=token["tokenUser"], password=token["tokenPass"], ignore_ssl=True) as s2:
|
async with meshctrl.Session(env.mcurl, user=token["tokenUser"], password=token["tokenPass"], ignore_ssl=True) as s2:
|
||||||
assert (await s2.user_info())["_id"] == (await s.user_info())["_id"], "Login token logged into wrong account"
|
assert (await s2.user_info())["_id"] == (await s.user_info())["_id"], "Login token logged into wrong account"
|
||||||
# Wait for the login token to expire
|
# Wait for the login token to expire
|
||||||
await asyncio.sleep(65)
|
await asyncio.sleep(65)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with meshctrl.session.Session(env.mcurl, user=token["tokenUser"], password=token["tokenPass"], ignore_ssl=True) as s2:
|
async with meshctrl.Session(env.mcurl, user=token["tokenUser"], password=token["tokenPass"], ignore_ssl=True) as s2:
|
||||||
pass
|
pass
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -94,7 +94,7 @@ async def test_login_token(env):
|
|||||||
token = await s.add_login_token("test2", timeout=10)
|
token = await s.add_login_token("test2", timeout=10)
|
||||||
token2 = await s.add_login_token("test3", timeout=10)
|
token2 = await s.add_login_token("test3", timeout=10)
|
||||||
print("\ninfo add_login_token_no_expire: {}\n".format(token))
|
print("\ninfo add_login_token_no_expire: {}\n".format(token))
|
||||||
async with meshctrl.session.Session(env.mcurl, user=token["tokenUser"], password=token["tokenPass"], ignore_ssl=True) as s2:
|
async with meshctrl.Session(env.mcurl, user=token["tokenUser"], password=token["tokenPass"], ignore_ssl=True) as s2:
|
||||||
assert (await s2.user_info())["_id"] == (await s.user_info())["_id"], "Login token logged into wrong account"
|
assert (await s2.user_info())["_id"] == (await s.user_info())["_id"], "Login token logged into wrong account"
|
||||||
|
|
||||||
r = await s.list_login_tokens(timeout=10)
|
r = await s.list_login_tokens(timeout=10)
|
||||||
@@ -107,9 +107,9 @@ async def test_login_token(env):
|
|||||||
assert len(await s.remove_login_token([token2["name"]], timeout=10)) == 0, "Residual login tokens"
|
assert len(await s.remove_login_token([token2["name"]], timeout=10)) == 0, "Residual login tokens"
|
||||||
|
|
||||||
async def test_mesh_device(env):
|
async def test_mesh_device(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session,\
|
meshctrl.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as unprivileged_session:
|
meshctrl.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as unprivileged_session:
|
||||||
# Test creating a mesh
|
# Test creating a mesh
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
print("\ninfo add_device_group: {}\n".format(mesh))
|
print("\ninfo add_device_group: {}\n".format(mesh))
|
||||||
@@ -266,9 +266,9 @@ async def test_mesh_device(env):
|
|||||||
assert not (await admin_session.add_users_to_device_group((await privileged_session.user_info())["_id"], mesh.meshid, rights=meshctrl.constants.MeshRights.fullrights, timeout=5))[(await privileged_session.user_info())["_id"]]["success"], "Added user to device group which doesn't exist?"
|
assert not (await admin_session.add_users_to_device_group((await privileged_session.user_info())["_id"], mesh.meshid, rights=meshctrl.constants.MeshRights.fullrights, timeout=5))[(await privileged_session.user_info())["_id"]]["success"], "Added user to device group which doesn't exist?"
|
||||||
|
|
||||||
async def test_user_groups(env):
|
async def test_user_groups(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session,\
|
meshctrl.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as unprivileged_session:
|
meshctrl.Session(env.mcurl, user="unprivileged", password=env.users["unprivileged"], ignore_ssl=True) as unprivileged_session:
|
||||||
|
|
||||||
user_group = await admin_session.add_user_group("test", description="aoeu")
|
user_group = await admin_session.add_user_group("test", description="aoeu")
|
||||||
print("\ninfo add_user_group: {}\n".format(user_group))
|
print("\ninfo add_user_group: {}\n".format(user_group))
|
||||||
@@ -294,7 +294,7 @@ async def test_user_groups(env):
|
|||||||
assert await admin_session.remove_user_group(user_group2.id.split("/")[-1])
|
assert await admin_session.remove_user_group(user_group2.id.split("/")[-1])
|
||||||
|
|
||||||
async def test_events(env):
|
async def test_events(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
await admin_session.list_events()
|
await admin_session.list_events()
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
try:
|
try:
|
||||||
@@ -310,7 +310,7 @@ async def test_events(env):
|
|||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
async with meshctrl.session.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session:
|
async with meshctrl.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session:
|
||||||
|
|
||||||
# assert len(await privileged_session.list_events()) == 0, "non-admin user has access to admin events"
|
# assert len(await privileged_session.list_events()) == 0, "non-admin user has access to admin events"
|
||||||
|
|
||||||
@@ -337,8 +337,8 @@ async def test_events(env):
|
|||||||
assert (await admin_session.remove_device_group(mesh.meshid, timeout=10)), "Failed to remove device group"
|
assert (await admin_session.remove_device_group(mesh.meshid, timeout=10)), "Failed to remove device group"
|
||||||
|
|
||||||
async def test_interuser(env):
|
async def test_interuser(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session,\
|
||||||
meshctrl.session.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session:
|
meshctrl.Session(env.mcurl, user="privileged", password=env.users["privileged"], ignore_ssl=True) as privileged_session:
|
||||||
got_message = asyncio.Event()
|
got_message = asyncio.Event()
|
||||||
async def _():
|
async def _():
|
||||||
async for message in admin_session.events({"action": "interuser"}):
|
async for message in admin_session.events({"action": "interuser"}):
|
||||||
@@ -361,7 +361,7 @@ async def test_interuser(env):
|
|||||||
tg.create_task(asyncio.wait_for(got_message.wait(), 5))
|
tg.create_task(asyncio.wait_for(got_message.wait(), 5))
|
||||||
|
|
||||||
async def test_session_files(env):
|
async def test_session_files(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
try:
|
try:
|
||||||
with env.create_agent(mesh.short_meshid) as agent:
|
with env.create_agent(mesh.short_meshid) as agent:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import meshctrl
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
async def test_shell(env):
|
async def test_shell(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
try:
|
try:
|
||||||
with env.create_agent(mesh.short_meshid) as agent:
|
with env.create_agent(mesh.short_meshid) as agent:
|
||||||
@@ -40,7 +40,7 @@ async def test_shell(env):
|
|||||||
|
|
||||||
|
|
||||||
async def test_smart_shell(env):
|
async def test_smart_shell(env):
|
||||||
async with meshctrl.session.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
async with meshctrl.Session(env.mcurl, user="admin", password=env.users["admin"], ignore_ssl=True) as admin_session:
|
||||||
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
mesh = await admin_session.add_device_group("test", description="This is a test group", amtonly=False, features=0, consent=0, timeout=10)
|
||||||
try:
|
try:
|
||||||
with env.create_agent(mesh.short_meshid) as agent:
|
with env.create_agent(mesh.short_meshid) as agent:
|
||||||
|
|||||||
Reference in New Issue
Block a user