From 04279ce8c6751ada1161463680a0e91df40cf1e8 Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 6 May 2025 01:35:57 +0200 Subject: [PATCH 1/7] Get device info with a device_id or WAN MAC address --- pyproject.toml | 1 + src/gacsco/main.py | 5 ++++- src/gacsco/server.py | 10 +++++++++- uv.lock | 13 ++++++++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 58a3829..c3e71bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.13" dependencies = [ "httpx>=0.28.1", + "netaddr>=1.3.0", "pydantic-settings[yaml]>=2.9.1", "typer>=0.15.3", ] diff --git a/src/gacsco/main.py b/src/gacsco/main.py index 601e97f..50cf21a 100644 --- a/src/gacsco/main.py +++ b/src/gacsco/main.py @@ -40,7 +40,10 @@ def reset(): ... @app.command(help="Get info on the device") -async def info(device_id: Annotated[str, typer.Argument()]): +async def info( + device_id: Annotated[str, typer.Argument(help="device_id or WAN MAC address")], +): + """Get device info, from its device_id or WAN interface MAC address""" try: resp = await server.get_device_info(device_id) print(dumps(resp)) diff --git a/src/gacsco/server.py b/src/gacsco/server.py index eed16a6..79f4dd3 100644 --- a/src/gacsco/server.py +++ b/src/gacsco/server.py @@ -3,6 +3,7 @@ from json import dumps from urllib.parse import quote import httpx +from netaddr import EUI, AddrFormatError from gacsco.config import conf from gacsco.utils import GacscoError @@ -41,8 +42,15 @@ class Server: return resp.json() async def get_device_info(self, device_id: str): - async with httpx.AsyncClient(cookies=self.cookies) as client: + try: + EUI(device_id) + except AddrFormatError: query = {"_id": device_id} + else: + query = { + "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress": device_id + } + async with httpx.AsyncClient(cookies=self.cookies) as client: resp = await client.get( f"{self.url}/devices/?", params={"query": dumps(query)}, diff --git a/uv.lock b/uv.lock index 00a77ec..6af65d7 100644 --- a/uv.lock +++ b/uv.lock @@ -94,10 +94,11 @@ wheels = [ ] [[package]] -name = "genieacs-commander" +name = "gacsco" source = { editable = "." } dependencies = [ { name = "httpx" }, + { name = "netaddr" }, { name = "pydantic-settings", extra = ["yaml"] }, { name = "typer" }, ] @@ -111,6 +112,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "httpx", specifier = ">=0.28.1" }, + { name = "netaddr", specifier = ">=1.3.0" }, { name = "pydantic-settings", extras = ["yaml"], specifier = ">=2.9.1" }, { name = "typer", specifier = ">=0.15.3" }, ] @@ -258,6 +260,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "netaddr" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/90/188b2a69654f27b221fba92fda7217778208532c962509e959a9cee5229d/netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a", size = 2260504, upload-time = "2024-05-28T21:30:37.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cc/f4fe2c7ce68b92cbf5b2d379ca366e1edae38cccaad00f69f529b460c3ef/netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe", size = 2262023, upload-time = "2024-05-28T21:30:34.191Z" }, +] + [[package]] name = "packaging" version = "25.0" From 7de3ac9e6ac49b86e2b9c7271b044c968fe76ee0 Mon Sep 17 00:00:00 2001 From: phil Date: Wed, 21 May 2025 18:01:31 +0200 Subject: [PATCH 2/7] Cosmetic --- README.md | 3 +++ src/gacsco/config.py | 6 +++--- src/gacsco/server.py | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e69de29..f6be477 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +# GenieACS COmmander + +Integration of GenieACS API with ... diff --git a/src/gacsco/config.py b/src/gacsco/config.py index 6c080a6..e205b18 100644 --- a/src/gacsco/config.py +++ b/src/gacsco/config.py @@ -1,16 +1,16 @@ -from typing import Any, Type, Tuple +from typing import Type, Tuple from pathlib import Path from pydantic import BaseModel from pydantic_settings import ( BaseSettings, - SecretsSettingsSource, + # SecretsSettingsSource, SettingsConfigDict, PydanticBaseSettingsSource, YamlConfigSettingsSource, ) -config_files = [Path(Path.cwd().root) / "etc" / "acsc" / "congig.yaml"] +config_files = [Path(Path.cwd().root) / "etc" / "gacsco" / "congig.yaml"] class Connection(BaseModel): diff --git a/src/gacsco/server.py b/src/gacsco/server.py index 79f4dd3..a03639c 100644 --- a/src/gacsco/server.py +++ b/src/gacsco/server.py @@ -1,12 +1,9 @@ -from dataclasses import dataclass from json import dumps -from urllib.parse import quote import httpx from netaddr import EUI, AddrFormatError from gacsco.config import conf -from gacsco.utils import GacscoError class LoginError(Exception): From c4b59bfa14f8b28ca5fe287f724413b3ee7d13d7 Mon Sep 17 00:00:00 2001 From: phil Date: Wed, 21 May 2025 18:02:42 +0200 Subject: [PATCH 3/7] Cosmetic --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f6be477..a4162f6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # GenieACS COmmander Integration of GenieACS API with ... + +* command line utility: + * list devices From 43137c4004208bbe284aaa474fb309c6f8d23392 Mon Sep 17 00:00:00 2001 From: phil Date: Wed, 21 May 2025 18:03:45 +0200 Subject: [PATCH 4/7] Cosmetic --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a4162f6..051f7cb 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,4 @@ Integration of GenieACS API with ... -* command line utility: - * list devices +* command line utility From c82129154609aa26779cad7d633061dc3022d328 Mon Sep 17 00:00:00 2001 From: phil Date: Wed, 21 May 2025 18:23:55 +0200 Subject: [PATCH 5/7] Cosmetic --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 051f7cb..acab014 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ # GenieACS COmmander -Integration of GenieACS API with ... +GenieACS is an open source TR-069 management tool +for remote control (provisioning, configuration, etc) +of various devices. This includes notably end user routers, aka. *boxes*. + +> GenieACS is a high performance Auto Configuration Server (ACS) for remote management +> of TR-069 enabled devices. It utilizes a declarative and fault tolerant +> configuration engine for automating complex provisioning scenarios at scale. +> It's battle-tested to handle hundreds of thousands and potentially millions +> of concurrent devices. + +Integration of [GenieACS](https://github.com/genieacs/genieacs) API with ... * command line utility From 7d1af1199cd0d1bece180d8f52c23e2e21e9d9e1 Mon Sep 17 00:00:00 2001 From: phil Date: Wed, 21 May 2025 18:26:22 +0200 Subject: [PATCH 6/7] Cosmetic --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index acab014..c60c906 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GenieACS COmmander -GenieACS is an open source TR-069 management tool -for remote control (provisioning, configuration, etc) +[GenieACS](https://github.com/genieacs/genieacs) is an open source TR-069 +management tool for remote control (provisioning, configuration, etc) of various devices. This includes notably end user routers, aka. *boxes*. > GenieACS is a high performance Auto Configuration Server (ACS) for remote management @@ -10,6 +10,8 @@ of various devices. This includes notably end user routers, aka. *boxes*. > It's battle-tested to handle hundreds of thousands and potentially millions > of concurrent devices. -Integration of [GenieACS](https://github.com/genieacs/genieacs) API with ... +This tool provides: * command line utility + * list devices +* ... *more to come* ... From 581d7edb9f40dea8102a49e944dcd4daf92c5955 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 25 May 2025 17:34:05 +0200 Subject: [PATCH 7/7] Remove rich dep --- .containerignore | 1 + pyproject.toml | 2 +- src/gacsco/main.py | 9 ++++---- uv.lock | 57 +++++----------------------------------------- 4 files changed, 12 insertions(+), 57 deletions(-) create mode 100644 .containerignore diff --git a/.containerignore b/.containerignore new file mode 100644 index 0000000..1d17dae --- /dev/null +++ b/.containerignore @@ -0,0 +1 @@ +.venv diff --git a/pyproject.toml b/pyproject.toml index c3e71bb..9467fa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dependencies = [ "httpx>=0.28.1", "netaddr>=1.3.0", "pydantic-settings[yaml]>=2.9.1", - "typer>=0.15.3", + "typer-slim>=0.15.4", ] [dependency-groups] diff --git a/src/gacsco/main.py b/src/gacsco/main.py index 50cf21a..a9d6668 100644 --- a/src/gacsco/main.py +++ b/src/gacsco/main.py @@ -3,7 +3,6 @@ from typing_extensions import Annotated from json import dumps import typer -from rich import print as pprint from gacsco.server import Server from gacsco.utils import AsyncTyper, GacscoError @@ -19,11 +18,11 @@ async def main(name: Annotated[str | None, typer.Option(help="Your name")] = Non ... # await server.login() except GacscoError as err: - pprint(err) + typer.echo(err) sys.exit(1) if name is not None: - pprint(f"Hello {name} from genieacs-commander!") + typer.echo(f"Hello {name} from genieacs-commander!") @app.command(help="List devices") @@ -32,7 +31,7 @@ async def list(): resp = await server.list_devices() print(dumps(resp)) except GacscoError as err: - pprint(err) + typer.echo(err) @app.command(help="Reset the device") @@ -48,7 +47,7 @@ async def info( resp = await server.get_device_info(device_id) print(dumps(resp)) except GacscoError as err: - pprint(err) + typer.echo(err) app() diff --git a/uv.lock b/uv.lock index 6af65d7..161a036 100644 --- a/uv.lock +++ b/uv.lock @@ -100,7 +100,7 @@ dependencies = [ { name = "httpx" }, { name = "netaddr" }, { name = "pydantic-settings", extra = ["yaml"] }, - { name = "typer" }, + { name = "typer-slim" }, ] [package.dev-dependencies] @@ -114,7 +114,7 @@ requires-dist = [ { name = "httpx", specifier = ">=0.28.1" }, { name = "netaddr", specifier = ">=1.3.0" }, { name = "pydantic-settings", extras = ["yaml"], specifier = ">=2.9.1" }, - { name = "typer", specifier = ">=0.15.3" }, + { name = "typer-slim", specifier = ">=0.15.4" }, ] [package.metadata.requires-dev] @@ -227,18 +227,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, ] -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, -] - [[package]] name = "matplotlib-inline" version = "0.1.7" @@ -251,15 +239,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, ] -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - [[package]] name = "netaddr" version = "1.3.0" @@ -426,28 +405,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] -[[package]] -name = "rich" -version = "14.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - [[package]] name = "sniffio" version = "1.3.1" @@ -481,18 +438,16 @@ wheels = [ ] [[package]] -name = "typer" -version = "0.15.3" +name = "typer-slim" +version = "0.15.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/1a/5f36851f439884bcfe8539f6a20ff7516e7b60f319bbaf69a90dc35cc2eb/typer-0.15.3.tar.gz", hash = "sha256:818873625d0569653438316567861899f7e9972f2e6e0c16dab608345ced713c", size = 101641, upload-time = "2025-04-28T21:40:59.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/75/fb85e49851d127316f30b7f6001fa0d378c14afdac280dace89d49992518/typer_slim-0.15.4.tar.gz", hash = "sha256:1d9fe638da58f4bdeae891512c47ed83915f81e7a0ee062cf6c572ff38473128", size = 101615, upload-time = "2025-05-14T16:35:00.566Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/20/9d953de6f4367163d23ec823200eb3ecb0050a2609691e512c8b95827a9b/typer-0.15.3-py3-none-any.whl", hash = "sha256:c86a65ad77ca531f03de08d1b9cb67cd09ad02ddddf4b34745b5008f43b239bd", size = 45253, upload-time = "2025-04-28T21:40:56.269Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7f/44304801838529b1051e00fb30e1750cacd10da31da1f3b0fa0e96ae6045/typer_slim-0.15.4-py3-none-any.whl", hash = "sha256:6d134e1b048da37ceacc1ccc3ad28a6f966c8f0833cc1513cf12a21de0da8ed8", size = 45315, upload-time = "2025-05-14T16:34:59.053Z" }, ] [[package]]