pax_global_header00006660000000000000000000000064145453204360014520gustar00rootroot0000000000000052 comment=911b9f8a33e39cb612bb1920185441f9efef4c55 autinerd-webmin-xmlrpc-911b9f8/000077500000000000000000000000001454532043600164625ustar00rootroot00000000000000autinerd-webmin-xmlrpc-911b9f8/.gitignore000066400000000000000000000060341454532043600204550ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ .vscode/ .ruff_cache/ autinerd-webmin-xmlrpc-911b9f8/LICENSE000066400000000000000000000020441454532043600174670ustar00rootroot00000000000000MIT License Copyright (c) 2024 Sid Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. autinerd-webmin-xmlrpc-911b9f8/README.md000066400000000000000000000010541454532043600177410ustar00rootroot00000000000000# Webmin XML-RPC python package This package provides a python interface to interact with the Webmin XML-RPC API. ## Usage ```python from aiohttp.client import ClientSession from webmin_xmlrpc.client import WebminInstance from yarl import URL base_url = URL.build(host="example.com", scheme="https") session = Clientsession(base_url) instance = WebminInstance(session) async def get_data(): data = await instance.update() ``` ## Exposed data - Load (1m, 5m, 15m) - Network interfaces - Memory information - Uptime - Local disk space information autinerd-webmin-xmlrpc-911b9f8/pyproject.toml000066400000000000000000000123631454532043600214030ustar00rootroot00000000000000[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" [project] name='webmin-xmlrpc' description="Provides a python interface to interact with the Webmin XML-RPC API." authors = [ {name = "Sidney Kuyateh", email = 'autinerd@kuyateh.eu'} ] classifiers = [ 'Development Status :: 2 - Pre-Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Programming Language :: Python :: 3 :: Only', ] keywords=['webmin', 'xmlrpc'] dependencies = ["aiohttp"] dynamic = ["version"] readme = "README.md" [project.urls] Homepage = "https://github.com/autinerd/webmin-xmlrpc" [tool.setuptools.dynamic] version = {attr = "webmin_xmlrpc.__version__"} [tool.ruff] select = [ "B002", # Python does not support the unary prefix increment "B007", # Loop control variable {name} not used within loop body "B014", # Exception handler with duplicate exception "B023", # Function definition does not bind loop variable {name} "B026", # Star-arg unpacking after a keyword argument is strongly discouraged "C", # complexity "COM818", # Trailing comma on bare tuple prohibited "D", # docstrings "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) "E", # pycodestyle "F", # pyflakes/autoflake "G", # flake8-logging-format "I", # isort "ICN001", # import concentions; {name} should be imported as {asname} "ISC001", # Implicitly concatenated string literals on one line "N804", # First argument of a class method should be named cls "N805", # First argument of a method should be named self "N815", # Variable {name} in class scope should not be mixedCase "PGH001", # No builtin eval() allowed "PGH004", # Use specific rule codes when using noqa "PLC0414", # Useless import alias. Import alias does not rename original package. "PLC", # pylint "PLE", # pylint "PLR", # pylint "PLW", # pylint "Q000", # Double quotes found but single quotes preferred "RUF006", # Store a reference to the return value of asyncio.create_task "S102", # Use of exec detected "S103", # bad-file-permissions "S108", # hardcoded-temp-file "S306", # suspicious-mktemp-usage "S307", # suspicious-eval-usage "S313", # suspicious-xmlc-element-tree-usage "S314", # suspicious-xml-element-tree-usage "S315", # suspicious-xml-expat-reader-usage "S316", # suspicious-xml-expat-builder-usage "S317", # suspicious-xml-sax-usage "S318", # suspicious-xml-mini-dom-usage "S319", # suspicious-xml-pull-dom-usage "S320", # suspicious-xmle-tree-usage "S601", # paramiko-call "S602", # subprocess-popen-with-shell-equals-true "S604", # call-with-shell-equals-true "S608", # hardcoded-sql-expression "S609", # unix-command-wildcard-injection "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass "SIM117", # Merge with-statements that use the same scope "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys() "SIM201", # Use {left} != {right} instead of not {left} == {right} "SIM208", # Use {expr} instead of not (not {expr}) "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a} "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. "SIM401", # Use get from dict with default instead of an if block "T100", # Trace found: {name} used "T20", # flake8-print "TID251", # Banned imports "TRY004", # Prefer TypeError exception for invalid type "TRY200", # Use raise from to specify exception cause "TRY302", # Remove exception handler; error is immediately re-raised "UP", # pyupgrade "W", # pycodestyle ] ignore = [ "D202", # No blank lines allowed after function docstring "D203", # 1 blank line required before class docstring "D213", # Multi-line docstring summary should start at the second line "D406", # Section name should end with a newline "D407", # Section name underlining "E501", # line too long "E731", # do not assign a lambda expression, use a def # Ignore ignored, as the rule is now back in preview/nursery, which cannot # be ignored anymore without warnings. # https://github.com/astral-sh/ruff/issues/7491 # "PLC1901", # Lots of false positives # False positives https://github.com/astral-sh/ruff/issues/5386 "PLC0208", # Use a sequence type instead of a `set` when iterating over values "PLR0911", # Too many return statements ({returns} > {max_returns}) "PLR0912", # Too many branches ({branches} > {max_branches}) "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) "PLR0915", # Too many statements ({statements} > {max_statements}) "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target "UP006", # keep type annotation style as is "UP007", # keep type annotation style as is # Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923 "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` ]autinerd-webmin-xmlrpc-911b9f8/webmin_xmlrpc/000077500000000000000000000000001454532043600213305ustar00rootroot00000000000000autinerd-webmin-xmlrpc-911b9f8/webmin_xmlrpc/__init__.py000066400000000000000000000001271454532043600234410ustar00rootroot00000000000000"""The library for communicating with the WebMin XMLRPC API.""" __version__ = "0.0.1" autinerd-webmin-xmlrpc-911b9f8/webmin_xmlrpc/client.py000066400000000000000000000041121454532043600231560ustar00rootroot00000000000000"""The client for Webmin XML-RPC.""" from typing import Any from aiohttp import ClientSession from .xmlrpc import XMLRPCClient class WebminInstance: """Represent a Webmin instance.""" data: dict[str, Any] def __init__(self, session: ClientSession): """Initialize the WebMin instance.""" self._client = XMLRPCClient(session) async def get_cpu_info(self) -> dict[str, Any]: """Retrieve the CPU load.""" result = await self._client.call("proc.get_cpu_info") return {"load_1m": result[0], "load_5m": result[1], "load_15m": result[2]} async def get_memory_info(self) -> dict[str, Any]: """Retrieve memory info.""" result = await self._client.call("proc.get_memory_info") return { "mem_total": result[0], "mem_free": result[1], "swap_total": result[2], "swap_free": result[3], } async def get_network_interfaces(self) -> dict[str, Any]: """Retrieve active network interfaces.""" result = await self._client.call("net.active_interfaces") return {"active_interfaces": result} async def get_system_uptime(self) -> dict[str, Any]: """Retrieve uptime.""" result = await self._client.call("proc.get_system_uptime") return { "uptime": {"days": result[0], "minutes": result[1], "seconds": result[2]} } async def local_disk_space(self) -> dict[str, Any]: """Retrieve local disk space.""" result = await self._client.call("mount.local_disk_space") return { "total_space": result[0], "free_space": result[1], "fs": result[2], "used_space": result[3], } async def update(self) -> dict[str, Any]: """Retrieve the current data.""" self.data = ( (await self.get_cpu_info()) | (await self.get_memory_info()) | (await self.local_disk_space()) | (await self.get_system_uptime()) | (await self.get_network_interfaces()) ) return self.data autinerd-webmin-xmlrpc-911b9f8/webmin_xmlrpc/xmlrpc.py000066400000000000000000000014071454532043600232110ustar00rootroot00000000000000"""The XMLRPC client.""" import xmlrpc.client from typing import Any from aiohttp import ClientSession class XMLRPCClient: """Represent a XMLRPC client.""" def __init__(self, session: ClientSession, url: str = "/xmlrpc.cgi"): """Initialize the XMLRPC client.""" self._session = session self._url = url async def call(self, method_name: str, params: tuple = ()) -> Any: """Call a XML-RPC method.""" xmlrequest = xmlrpc.client.dumps(methodname=method_name, params=params) async with self._session.post(url=self._url, data=xmlrequest) as response: response.raise_for_status() xmlresponse = xmlrpc.client.loads((await response.read()).decode("utf-8")) return xmlresponse[0][0]