gnetclisdk 1.0.101__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of gnetclisdk might be problematic. Click here for more details.
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/PKG-INFO +2 -1
- gnetclisdk-1.1.1/gnetclisdk/config.py +54 -0
- gnetclisdk-1.1.1/gnetclisdk/starter.py +121 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk.egg-info/PKG-INFO +2 -1
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk.egg-info/SOURCES.txt +2 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk.egg-info/requires.txt +1 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/requirements.txt +1 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/MANIFEST.in +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/README.md +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/auth.py +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/client.py +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/exceptions.py +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/interceptors.py +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/proto/server_pb2.py +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/proto/server_pb2.pyi +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk/proto/server_pb2_grpc.py +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk.egg-info/dependency_links.txt +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/gnetclisdk.egg-info/top_level.txt +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/pyproject.toml +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/setup.cfg +0 -0
- {gnetclisdk-1.0.101 → gnetclisdk-1.1.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gnetclisdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Client for Gnetcli GRPC-server
|
|
5
5
|
Home-page: https://github.com/annetutil/gnetcli
|
|
6
6
|
Author: Alexander Balezin
|
|
@@ -16,6 +16,7 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
Requires-Dist: protobuf>=4.24.4
|
|
17
17
|
Requires-Dist: grpcio>=1.59.2
|
|
18
18
|
Requires-Dist: googleapis-common-protos>=1.61.0
|
|
19
|
+
Requires-Dist: pyyaml
|
|
19
20
|
Dynamic: author
|
|
20
21
|
Dynamic: author-email
|
|
21
22
|
Dynamic: classifier
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from dataclasses import dataclass, asdict, field
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class LogConfig:
|
|
9
|
+
level: str = "info"
|
|
10
|
+
json: bool = False
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class AuthAppConfig:
|
|
15
|
+
login: str = ""
|
|
16
|
+
password: str = ""
|
|
17
|
+
private_key: str = "" # path to private key file
|
|
18
|
+
proxy_jump: str = ""
|
|
19
|
+
use_agent: bool = False
|
|
20
|
+
ssh_config: bool = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class Config:
|
|
25
|
+
logging: LogConfig = field(default_factory=LogConfig)
|
|
26
|
+
port: str = "" # Listen address
|
|
27
|
+
http_port: str = "" # Http listen address
|
|
28
|
+
dev_auth: AuthAppConfig = field(default_factory=AuthAppConfig)
|
|
29
|
+
dev_conf: str = "" # Path to yaml with device types
|
|
30
|
+
tls: bool = False
|
|
31
|
+
cert_file: str = ""
|
|
32
|
+
key_file: str = ""
|
|
33
|
+
basic_auth: str = ""
|
|
34
|
+
disable_tcp: bool = False
|
|
35
|
+
unix_socket: str = "" # Unix socket pat
|
|
36
|
+
default_read_timeout: timedelta = timedelta(seconds=0)
|
|
37
|
+
default_cmd_timeout: timedelta = timedelta(seconds=0)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def dict_factory(data):
|
|
41
|
+
return {
|
|
42
|
+
key: (
|
|
43
|
+
f"{value.total_seconds()}s"
|
|
44
|
+
if isinstance(value, timedelta)
|
|
45
|
+
else value
|
|
46
|
+
)
|
|
47
|
+
for key, value in data
|
|
48
|
+
if value != "" # skip empty strings
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def config_to_yaml(cfg: Config) -> str:
|
|
53
|
+
cfg_dict = asdict(cfg, dict_factory=dict_factory)
|
|
54
|
+
return yaml.safe_dump(cfg_dict, sort_keys=False)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from asyncio.subprocess import Process
|
|
5
|
+
from json import JSONDecodeError
|
|
6
|
+
from subprocess import DEVNULL, PIPE
|
|
7
|
+
|
|
8
|
+
from gnetclisdk.config import Config, LogConfig, AuthAppConfig, config_to_yaml
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
DEFAULT_GNETCLI_SERVER_CONF = Config(
|
|
13
|
+
logging=LogConfig(level="debug", json=True),
|
|
14
|
+
dev_auth=AuthAppConfig(use_agent=True, ssh_config=True),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GnetcliStarter:
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
server_path: str,
|
|
22
|
+
server_conf: Config = DEFAULT_GNETCLI_SERVER_CONF,
|
|
23
|
+
start_timeout: int = 5,
|
|
24
|
+
stop_timeout: int = 10,
|
|
25
|
+
):
|
|
26
|
+
self._server_path = server_path
|
|
27
|
+
self._server_conf = server_conf
|
|
28
|
+
self._running = False
|
|
29
|
+
self._proc: Process | None = None
|
|
30
|
+
self._start_timeout = start_timeout
|
|
31
|
+
self._stop_timeout = stop_timeout
|
|
32
|
+
self._reader_task: asyncio.Task | None = None
|
|
33
|
+
|
|
34
|
+
async def _start(self) -> Process:
|
|
35
|
+
logger.debug("Starting Gnetcli server: %s", self._server_path)
|
|
36
|
+
proc = await asyncio.create_subprocess_exec(
|
|
37
|
+
self._server_path,
|
|
38
|
+
"--conf-file",
|
|
39
|
+
"-",
|
|
40
|
+
stdout=DEVNULL, # we do not read stdout
|
|
41
|
+
stderr=PIPE,
|
|
42
|
+
stdin=PIPE,
|
|
43
|
+
)
|
|
44
|
+
self._running = True
|
|
45
|
+
conf_yaml = config_to_yaml(self._server_conf)
|
|
46
|
+
proc.stdin.write(conf_yaml.encode())
|
|
47
|
+
await proc.stdin.drain()
|
|
48
|
+
proc.stdin.close()
|
|
49
|
+
return proc
|
|
50
|
+
|
|
51
|
+
async def _wait_url(self) -> str:
|
|
52
|
+
while proc := self._proc:
|
|
53
|
+
output = await proc.stderr.readline()
|
|
54
|
+
if not output and proc.returncode is not None:
|
|
55
|
+
break
|
|
56
|
+
if not output:
|
|
57
|
+
continue
|
|
58
|
+
logger.debug("gnetcli output: %s", output.strip())
|
|
59
|
+
try:
|
|
60
|
+
data = json.loads(output)
|
|
61
|
+
except JSONDecodeError:
|
|
62
|
+
logger.error("cannot decode data")
|
|
63
|
+
continue
|
|
64
|
+
if data.get("msg") == "init tcp socket":
|
|
65
|
+
logger.debug("Tcp socket found")
|
|
66
|
+
return data.get("address")
|
|
67
|
+
if data.get("msg") == "init unix socket":
|
|
68
|
+
logger.debug("Unix socket found")
|
|
69
|
+
return "unix:" + data.get("path")
|
|
70
|
+
if data.get("level") == "panic":
|
|
71
|
+
logger.error("gnetcli error %s", data)
|
|
72
|
+
return "" # stopped
|
|
73
|
+
|
|
74
|
+
async def __aenter__(self) -> str:
|
|
75
|
+
self._proc = await self._start()
|
|
76
|
+
try:
|
|
77
|
+
url = await asyncio.wait_for(
|
|
78
|
+
self._wait_url(), timeout=self._start_timeout
|
|
79
|
+
)
|
|
80
|
+
except asyncio.TimeoutError:
|
|
81
|
+
logger.error("gnetcli _wait_url timeout, terminating")
|
|
82
|
+
await self._terminate()
|
|
83
|
+
raise RuntimeError("gnetcli start failed")
|
|
84
|
+
logger.info("gnetcli started with url: %s", url)
|
|
85
|
+
self._reader_task = asyncio.create_task(self._communicate())
|
|
86
|
+
return url
|
|
87
|
+
|
|
88
|
+
async def _communicate(self) -> None:
|
|
89
|
+
while proc := self._proc:
|
|
90
|
+
output = await proc.stderr.readline()
|
|
91
|
+
if not output and proc.returncode is not None:
|
|
92
|
+
logger.debug("stop reading, gnetcli terminated")
|
|
93
|
+
return
|
|
94
|
+
if not output:
|
|
95
|
+
continue
|
|
96
|
+
logger.debug("gnetcli output: %s", output.strip())
|
|
97
|
+
|
|
98
|
+
async def _terminate(self) -> None:
|
|
99
|
+
if (proc := self._proc) is None:
|
|
100
|
+
return
|
|
101
|
+
if proc.returncode is not None:
|
|
102
|
+
logger.error(
|
|
103
|
+
"gnetcli already terminated with code: %s", proc.returncode
|
|
104
|
+
)
|
|
105
|
+
return
|
|
106
|
+
logger.debug("terminate gnetcli")
|
|
107
|
+
proc.terminate()
|
|
108
|
+
try:
|
|
109
|
+
await asyncio.wait_for(proc.wait(), timeout=self._stop_timeout)
|
|
110
|
+
except TimeoutError:
|
|
111
|
+
logger.debug("gnetcli terminate failed, killing")
|
|
112
|
+
self._proc.kill()
|
|
113
|
+
logger.debug("gnetcli terminated with code: %s", proc.returncode)
|
|
114
|
+
if self._reader_task is not None and not self._reader_task.cancel() and not self._reader_task.cancelling():
|
|
115
|
+
self._reader_task.cancel()
|
|
116
|
+
self._reader_task = None
|
|
117
|
+
self._proc = None
|
|
118
|
+
|
|
119
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
120
|
+
self.running = False
|
|
121
|
+
await self._terminate()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gnetclisdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Client for Gnetcli GRPC-server
|
|
5
5
|
Home-page: https://github.com/annetutil/gnetcli
|
|
6
6
|
Author: Alexander Balezin
|
|
@@ -16,6 +16,7 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
Requires-Dist: protobuf>=4.24.4
|
|
17
17
|
Requires-Dist: grpcio>=1.59.2
|
|
18
18
|
Requires-Dist: googleapis-common-protos>=1.61.0
|
|
19
|
+
Requires-Dist: pyyaml
|
|
19
20
|
Dynamic: author
|
|
20
21
|
Dynamic: author-email
|
|
21
22
|
Dynamic: classifier
|
|
@@ -5,8 +5,10 @@ requirements.txt
|
|
|
5
5
|
setup.py
|
|
6
6
|
gnetclisdk/auth.py
|
|
7
7
|
gnetclisdk/client.py
|
|
8
|
+
gnetclisdk/config.py
|
|
8
9
|
gnetclisdk/exceptions.py
|
|
9
10
|
gnetclisdk/interceptors.py
|
|
11
|
+
gnetclisdk/starter.py
|
|
10
12
|
gnetclisdk.egg-info/PKG-INFO
|
|
11
13
|
gnetclisdk.egg-info/SOURCES.txt
|
|
12
14
|
gnetclisdk.egg-info/dependency_links.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|