refactor: make web handlers much more consistent across types, remove graphql, and make luci and grpc the dedicated web apis for BOSer and BOSMiner respectively.
This commit is contained in:
@@ -13,9 +13,11 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Union
|
||||
from typing import Any
|
||||
|
||||
from pyasic.errors import APIWarning
|
||||
|
||||
@@ -23,11 +25,13 @@ from pyasic.errors import APIWarning
|
||||
class BaseWebAPI(ABC):
|
||||
def __init__(self, ip: str) -> None:
|
||||
# ip address of the miner
|
||||
self.ip = ip # ipaddress.ip_address(ip)
|
||||
self.username = "root"
|
||||
self.ip = ip
|
||||
self.username = None
|
||||
self.pwd = None
|
||||
self.port = 80
|
||||
|
||||
self.token = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls is BaseWebAPI:
|
||||
raise TypeError(f"Only children of '{cls.__name__}' may be instantiated")
|
||||
@@ -39,10 +43,11 @@ class BaseWebAPI(ABC):
|
||||
@abstractmethod
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
pass
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Union
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -26,14 +28,16 @@ from pyasic.web import BaseWebAPI
|
||||
class AntminerModernWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.username = "root"
|
||||
self.pwd = settings.get("default_antminer_web_password", "root")
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}:{self.port}/cgi-bin/{command}.cgi"
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
@@ -44,9 +48,9 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
if parameters:
|
||||
data = await client.post(
|
||||
url,
|
||||
data=json.dumps(parameters),
|
||||
auth=auth,
|
||||
timeout=settings.get("api_function_timeout", 3), # noqa
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
data = await client.get(url, auth=auth)
|
||||
@@ -76,7 +80,9 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
data["multicommand"] = True
|
||||
return data
|
||||
|
||||
async def _handle_multicommand(self, client: httpx.AsyncClient, command: str):
|
||||
async def _handle_multicommand(
|
||||
self, client: httpx.AsyncClient, command: str
|
||||
) -> dict:
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
|
||||
try:
|
||||
@@ -142,14 +148,16 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
class AntminerOldWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.username = "root"
|
||||
self.pwd = settings.get("default_antminer_web_password", "root")
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}:{self.port}/cgi-bin/{command}.cgi"
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import warnings
|
||||
from typing import Any, List, Union
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -31,9 +33,9 @@ class FluxWebAPI(BaseWebAPI):
|
||||
self.username = "admin"
|
||||
self.pwd = settings.get("default_auradine_web_password", "admin")
|
||||
self.port = 8080
|
||||
self.jwt = None
|
||||
self.token = None
|
||||
|
||||
async def auth(self):
|
||||
async def auth(self) -> str | None:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
auth = await client.post(
|
||||
@@ -49,20 +51,24 @@ class FluxWebAPI(BaseWebAPI):
|
||||
else:
|
||||
json_auth = auth.json()
|
||||
try:
|
||||
self.jwt = json_auth["Token"][0]["Token"]
|
||||
self.token = json_auth["Token"][0]["Token"]
|
||||
except LookupError:
|
||||
return None
|
||||
return self.jwt
|
||||
return self.token
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
post: bool = False,
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
if self.jwt is None:
|
||||
post = privileged or not parameters == {}
|
||||
if not parameters == {}:
|
||||
parameters["command"] = command
|
||||
|
||||
if self.token is None:
|
||||
await self.auth()
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
for i in range(settings.get("get_data_retries", 1)):
|
||||
@@ -70,20 +76,14 @@ class FluxWebAPI(BaseWebAPI):
|
||||
if post:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/{command}",
|
||||
headers={"Token": self.jwt},
|
||||
headers={"Token": self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
elif parameters:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/{command}",
|
||||
headers={"Token": self.jwt},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json={"command": command, **parameters},
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}:{self.port}/{command}",
|
||||
headers={"Token": self.jwt},
|
||||
headers={"Token": self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
json_data = response.json()
|
||||
@@ -95,9 +95,7 @@ class FluxWebAPI(BaseWebAPI):
|
||||
await self.auth()
|
||||
continue
|
||||
return json_data
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
except json.JSONDecodeError:
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
pass
|
||||
|
||||
async def multicommand(
|
||||
@@ -166,97 +164,97 @@ class FluxWebAPI(BaseWebAPI):
|
||||
return False, data["STATUS"][0]["Msg"]
|
||||
return True, None
|
||||
|
||||
async def factory_reset(self):
|
||||
return await self.send_command("factory-reset", post=True)
|
||||
async def factory_reset(self) -> dict:
|
||||
return await self.send_command("factory-reset", privileged=True)
|
||||
|
||||
async def get_fan(self):
|
||||
async def get_fan(self) -> dict:
|
||||
return await self.send_command("fan")
|
||||
|
||||
async def set_fan(self, fan: int, speed_pct: int):
|
||||
async def set_fan(self, fan: int, speed_pct: int) -> dict:
|
||||
return await self.send_command("fan", index=fan, percentage=speed_pct)
|
||||
|
||||
async def firmware_upgrade(self, url: str = None, version: str = "latest"):
|
||||
async def firmware_upgrade(self, url: str = None, version: str = "latest") -> dict:
|
||||
if url is not None:
|
||||
return await self.send_command("firmware-upgrade", url=url)
|
||||
return await self.send_command("firmware-upgrade", version=version)
|
||||
|
||||
async def get_frequency(self):
|
||||
async def get_frequency(self) -> dict:
|
||||
return await self.send_command("frequency")
|
||||
|
||||
async def set_frequency(self, board: int, frequency: float):
|
||||
async def set_frequency(self, board: int, frequency: float) -> dict:
|
||||
return await self.send_command("frequency", board=board, frequency=frequency)
|
||||
|
||||
async def ipreport(self):
|
||||
async def ipreport(self) -> dict:
|
||||
return await self.send_command("ipreport")
|
||||
|
||||
async def get_led(self):
|
||||
async def get_led(self) -> dict:
|
||||
return await self.send_command("led")
|
||||
|
||||
async def set_led(self, code: int):
|
||||
async def set_led(self, code: int) -> dict:
|
||||
return await self.send_command("led", code=code)
|
||||
|
||||
async def set_led_custom(self, code: int, led_1: int, led_2: int, msg: str):
|
||||
async def set_led_custom(self, code: int, led_1: int, led_2: int, msg: str) -> dict:
|
||||
return await self.send_command(
|
||||
"led", code=code, led1=led_1, led2=led_2, msg=msg
|
||||
)
|
||||
|
||||
async def get_mode(self):
|
||||
async def get_mode(self) -> dict:
|
||||
return await self.send_command("mode")
|
||||
|
||||
async def set_mode(self, **kwargs):
|
||||
async def set_mode(self, **kwargs) -> dict:
|
||||
return await self.send_command("mode", **kwargs)
|
||||
|
||||
async def get_network(self):
|
||||
async def get_network(self) -> dict:
|
||||
return await self.send_command("network")
|
||||
|
||||
async def set_network(self, **kwargs):
|
||||
async def set_network(self, **kwargs) -> dict:
|
||||
return await self.send_command("network", **kwargs)
|
||||
|
||||
async def password(self, password: str):
|
||||
async def password(self, password: str) -> dict:
|
||||
res = await self.send_command(
|
||||
"password", user=self.username, old=self.pwd, new=password
|
||||
)
|
||||
self.pwd = password
|
||||
return res
|
||||
|
||||
async def get_psu(self):
|
||||
async def get_psu(self) -> dict:
|
||||
return await self.send_command("psu")
|
||||
|
||||
async def set_psu(self, voltage: float):
|
||||
async def set_psu(self, voltage: float) -> dict:
|
||||
return await self.send_command("psu", voltage=voltage)
|
||||
|
||||
async def get_register(self):
|
||||
async def get_register(self) -> dict:
|
||||
return await self.send_command("register")
|
||||
|
||||
async def set_register(self, company: str):
|
||||
async def set_register(self, company: str) -> dict:
|
||||
return await self.send_command("register", parameter=company)
|
||||
|
||||
async def reboot(self):
|
||||
return await self.send_command("restart", post=True)
|
||||
async def reboot(self) -> dict:
|
||||
return await self.send_command("restart", privileged=True)
|
||||
|
||||
async def restart_gcminer(self):
|
||||
async def restart_gcminer(self) -> dict:
|
||||
return await self.send_command("restart", parameter="gcminer")
|
||||
|
||||
async def restart_api_server(self):
|
||||
async def restart_api_server(self) -> dict:
|
||||
return await self.send_command("restart", parameter="api-server")
|
||||
|
||||
async def temperature(self):
|
||||
async def temperature(self) -> dict:
|
||||
return await self.send_command("temperature")
|
||||
|
||||
async def timedate(self, ntp: str, timezone: str):
|
||||
async def timedate(self, ntp: str, timezone: str) -> dict:
|
||||
return await self.send_command("timedate", ntp=ntp, timezone=timezone)
|
||||
|
||||
async def token(self):
|
||||
async def token(self) -> dict:
|
||||
return await self.send_command("token", user=self.username, password=self.pwd)
|
||||
|
||||
async def update_pools(self, pools: List[dict]):
|
||||
async def update_pools(self, pools: list[dict]) -> dict:
|
||||
return await self.send_command("updatepools", pools=pools)
|
||||
|
||||
async def voltage(self):
|
||||
async def voltage(self) -> dict:
|
||||
return await self.send_command("voltage")
|
||||
|
||||
async def get_ztp(self):
|
||||
async def get_ztp(self) -> dict:
|
||||
return await self.send_command("ztp")
|
||||
|
||||
async def set_ztp(self, enable: bool):
|
||||
async def set_ztp(self, enable: bool) -> dict:
|
||||
return await self.send_command("ztp", parameter="on" if enable else "off")
|
||||
|
||||
@@ -1,149 +1,2 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import asyncio
|
||||
from typing import Union
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.web import BaseWebAPI
|
||||
|
||||
from .graphql import BOSerGraphQLAPI
|
||||
from .grpc import BOSerGRPCAPI
|
||||
from .luci import BOSMinerLuCIAPI
|
||||
|
||||
|
||||
class BOSMinerWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
self.luci = BOSMinerLuCIAPI(
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
self._pwd = settings.get("default_bosminer_password", "root")
|
||||
self._port = 80
|
||||
super().__init__(ip)
|
||||
|
||||
@property
|
||||
def pwd(self):
|
||||
return self._pwd
|
||||
|
||||
@pwd.setter
|
||||
def pwd(self, other: str):
|
||||
self._pwd = other
|
||||
self.luci.pwd = other
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._port
|
||||
|
||||
@port.setter
|
||||
def port(self, other: str):
|
||||
self._port = other
|
||||
self.luci.port = other
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, dict],
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
return await self.luci.send_command(command)
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: Union[dict, str], allow_warning: bool = True
|
||||
) -> dict:
|
||||
return await self.luci.multicommand(*commands)
|
||||
|
||||
|
||||
class BOSerWebAPI(BOSMinerWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
self.gql = BOSerGraphQLAPI(
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
self.grpc = BOSerGRPCAPI(ip, settings.get("default_bosminer_password", "root"))
|
||||
self._port = 80
|
||||
super().__init__(ip)
|
||||
|
||||
@property
|
||||
def pwd(self):
|
||||
return self._pwd
|
||||
|
||||
@pwd.setter
|
||||
def pwd(self, other: str):
|
||||
self._pwd = other
|
||||
self.luci.pwd = other
|
||||
self.gql.pwd = other
|
||||
self.grpc.pwd = other
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._port
|
||||
|
||||
@port.setter
|
||||
def port(self, other: str):
|
||||
self._port = other
|
||||
self.luci.port = other
|
||||
self.gql.port = other
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, dict],
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
command_type = self.select_command_type(command)
|
||||
if command_type == "gql":
|
||||
return await self.gql.send_command(command)
|
||||
elif command_type == "grpc":
|
||||
try:
|
||||
return await getattr(self.grpc, command.replace("grpc_", ""))()
|
||||
except AttributeError:
|
||||
raise APIError(f"No gRPC command found for command: {command}")
|
||||
elif command_type == "luci":
|
||||
return await self.luci.send_command(command)
|
||||
|
||||
@staticmethod
|
||||
def select_command_type(command: Union[str, dict]) -> str:
|
||||
if isinstance(command, dict):
|
||||
return "gql"
|
||||
else:
|
||||
return "grpc"
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: Union[dict, str], allow_warning: bool = True
|
||||
) -> dict:
|
||||
cmd_types = {"grpc": [], "gql": []}
|
||||
for cmd in commands:
|
||||
cmd_types[self.select_command_type(cmd)].append(cmd)
|
||||
|
||||
async def no_op():
|
||||
return {}
|
||||
|
||||
if len(cmd_types["grpc"]) > 0:
|
||||
grpc_data_t = asyncio.create_task(
|
||||
self.grpc.multicommand(*cmd_types["grpc"])
|
||||
)
|
||||
else:
|
||||
grpc_data_t = asyncio.create_task(no_op())
|
||||
if len(cmd_types["gql"]) > 0:
|
||||
gql_data_t = asyncio.create_task(self.gql.multicommand(*cmd_types["gql"]))
|
||||
else:
|
||||
gql_data_t = asyncio.create_task(no_op())
|
||||
|
||||
await asyncio.gather(grpc_data_t, gql_data_t)
|
||||
|
||||
data = dict(**grpc_data_t.result(), **gql_data_t.result())
|
||||
return data
|
||||
from .boser import BOSerWebAPI
|
||||
from .bosminer import BOSMinerWebAPI
|
||||
|
||||
@@ -13,15 +13,19 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from betterproto import Message
|
||||
from grpclib import GRPCError, Status
|
||||
from grpclib.client import Channel
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.web import BaseWebAPI
|
||||
|
||||
from .proto.braiins.bos import *
|
||||
from .proto.braiins.bos.v1 import *
|
||||
@@ -41,14 +45,13 @@ class BOSMinerGRPCStub(
|
||||
pass
|
||||
|
||||
|
||||
class BOSerGRPCAPI:
|
||||
def __init__(self, ip: str, pwd: str):
|
||||
self.ip = ip
|
||||
class BOSerWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self.pwd = settings.get("default_bosminer_password", "root")
|
||||
self.port = 50051
|
||||
self._auth = None
|
||||
self._auth_time = datetime.now()
|
||||
self._auth_time = None
|
||||
|
||||
@property
|
||||
def commands(self) -> list:
|
||||
@@ -85,13 +88,15 @@ class BOSerGRPCAPI:
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: str,
|
||||
message: Message = None,
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
auth: bool = True,
|
||||
allow_warning: bool = True,
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
message: betterproto.Message = parameters["message"]
|
||||
metadata = []
|
||||
if auth:
|
||||
if privileged:
|
||||
metadata.append(("authorization", await self.auth()))
|
||||
try:
|
||||
async with Channel(self.ip, self.port) as c:
|
||||
@@ -111,15 +116,15 @@ class BOSerGRPCAPI:
|
||||
except GRPCError as e:
|
||||
raise APIError(f"gRPC command failed - {endpoint}") from e
|
||||
|
||||
async def auth(self):
|
||||
if self._auth is not None and self._auth_time - datetime.now() < timedelta(
|
||||
async def auth(self) -> str | None:
|
||||
if self.token is not None and self._auth_time - datetime.now() < timedelta(
|
||||
seconds=3540
|
||||
):
|
||||
return self._auth
|
||||
return self.token
|
||||
await self._get_auth()
|
||||
return self._auth
|
||||
return self.token
|
||||
|
||||
async def _get_auth(self):
|
||||
async def _get_auth(self) -> str:
|
||||
async with Channel(self.ip, self.port) as c:
|
||||
req = LoginRequest(username=self.username, password=self.pwd)
|
||||
async with c.request(
|
||||
@@ -132,74 +137,79 @@ class BOSerGRPCAPI:
|
||||
await stream.recv_initial_metadata()
|
||||
auth = stream.initial_metadata.get("authorization")
|
||||
if auth is not None:
|
||||
self._auth = auth
|
||||
self.token = auth
|
||||
self._auth_time = datetime.now()
|
||||
return self._auth
|
||||
return self.token
|
||||
|
||||
async def get_api_version(self):
|
||||
async def get_api_version(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_api_version", ApiVersionRequest(), auth=False
|
||||
"get_api_version", message=ApiVersionRequest(), privileged=False
|
||||
)
|
||||
|
||||
async def start(self):
|
||||
return await self.send_command("start", StartRequest())
|
||||
async def start(self) -> dict:
|
||||
return await self.send_command("start", message=StartRequest())
|
||||
|
||||
async def stop(self):
|
||||
return await self.send_command("stop", StopRequest())
|
||||
async def stop(self) -> dict:
|
||||
return await self.send_command("stop", message=StopRequest())
|
||||
|
||||
async def pause_mining(self):
|
||||
return await self.send_command("pause_mining", PauseMiningRequest())
|
||||
async def pause_mining(self) -> dict:
|
||||
return await self.send_command("pause_mining", message=PauseMiningRequest())
|
||||
|
||||
async def resume_mining(self):
|
||||
return await self.send_command("resume_mining", ResumeMiningRequest())
|
||||
async def resume_mining(self) -> dict:
|
||||
return await self.send_command("resume_mining", message=ResumeMiningRequest())
|
||||
|
||||
async def restart(self):
|
||||
return await self.send_command("restart", RestartRequest())
|
||||
async def restart(self) -> dict:
|
||||
return await self.send_command("restart", message=RestartRequest())
|
||||
|
||||
async def reboot(self):
|
||||
return await self.send_command("reboot", RebootRequest())
|
||||
async def reboot(self) -> dict:
|
||||
return await self.send_command("reboot", message=RebootRequest())
|
||||
|
||||
async def set_locate_device_status(self, enable: bool):
|
||||
async def set_locate_device_status(self, enable: bool) -> dict:
|
||||
return await self.send_command(
|
||||
"set_locate_device_status", SetLocateDeviceStatusRequest(enable=enable)
|
||||
"set_locate_device_status",
|
||||
message=SetLocateDeviceStatusRequest(enable=enable),
|
||||
)
|
||||
|
||||
async def get_locate_device_status(self):
|
||||
async def get_locate_device_status(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_locate_device_status", GetLocateDeviceStatusRequest()
|
||||
"get_locate_device_status", message=GetLocateDeviceStatusRequest()
|
||||
)
|
||||
|
||||
async def set_password(self, password: str = None):
|
||||
async def set_password(self, password: str = None) -> dict:
|
||||
return await self.send_command(
|
||||
"set_password", SetPasswordRequest(password=password)
|
||||
"set_password", message=SetPasswordRequest(password=password)
|
||||
)
|
||||
|
||||
async def get_cooling_state(self):
|
||||
return await self.send_command("get_cooling_state", GetCoolingStateRequest())
|
||||
async def get_cooling_state(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_cooling_state", message=GetCoolingStateRequest()
|
||||
)
|
||||
|
||||
async def set_immersion_mode(
|
||||
self,
|
||||
enable: bool,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"set_immersion_mode",
|
||||
SetImmersionModeRequest(
|
||||
message=SetImmersionModeRequest(
|
||||
enable_immersion_mode=enable, save_action=save_action
|
||||
),
|
||||
)
|
||||
|
||||
async def get_tuner_state(self):
|
||||
return await self.send_command("get_tuner_state", GetTunerStateRequest())
|
||||
|
||||
async def list_target_profiles(self):
|
||||
async def get_tuner_state(self) -> dict:
|
||||
return await self.send_command(
|
||||
"list_target_profiles", ListTargetProfilesRequest()
|
||||
"get_tuner_state", message=GetTunerStateRequest()
|
||||
)
|
||||
|
||||
async def list_target_profiles(self) -> dict:
|
||||
return await self.send_command(
|
||||
"list_target_profiles", message=ListTargetProfilesRequest()
|
||||
)
|
||||
|
||||
async def set_default_power_target(
|
||||
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"set_default_power_target",
|
||||
message=SetDefaultPowerTargetRequest(save_action=save_action),
|
||||
@@ -209,10 +219,10 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
power_target: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"set_power_target",
|
||||
SetPowerTargetRequest(
|
||||
message=SetPowerTargetRequest(
|
||||
power_target=Power(watt=power_target), save_action=save_action
|
||||
),
|
||||
)
|
||||
@@ -221,7 +231,7 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
power_target_increment: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"increment_power_target",
|
||||
message=IncrementPowerTargetRequest(
|
||||
@@ -234,7 +244,7 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
power_target_decrement: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"decrement_power_target",
|
||||
message=DecrementPowerTargetRequest(
|
||||
@@ -245,7 +255,7 @@ class BOSerGRPCAPI:
|
||||
|
||||
async def set_default_hashrate_target(
|
||||
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"set_default_hashrate_target",
|
||||
message=SetDefaultHashrateTargetRequest(save_action=save_action),
|
||||
@@ -255,10 +265,10 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
hashrate_target: float,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"set_hashrate_target",
|
||||
SetHashrateTargetRequest(
|
||||
message=SetHashrateTargetRequest(
|
||||
hashrate_target=TeraHashrate(terahash_per_second=hashrate_target),
|
||||
save_action=save_action,
|
||||
),
|
||||
@@ -268,10 +278,10 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
hashrate_target_increment: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"increment_hashrate_target",
|
||||
IncrementHashrateTargetRequest(
|
||||
message=IncrementHashrateTargetRequest(
|
||||
hashrate_target_increment=TeraHashrate(
|
||||
terahash_per_second=hashrate_target_increment
|
||||
),
|
||||
@@ -283,10 +293,10 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
hashrate_target_decrement: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"decrement_hashrate_target",
|
||||
DecrementHashrateTargetRequest(
|
||||
message=DecrementHashrateTargetRequest(
|
||||
hashrate_target_decrement=TeraHashrate(
|
||||
terahash_per_second=hashrate_target_decrement
|
||||
),
|
||||
@@ -301,7 +311,7 @@ class BOSerGRPCAPI:
|
||||
min_power_target: int,
|
||||
enable_shutdown: bool = None,
|
||||
shutdown_duration: int = None,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"set_dps",
|
||||
message=SetDpsRequest(
|
||||
@@ -322,7 +332,7 @@ class BOSerGRPCAPI:
|
||||
wattage_target: int = None,
|
||||
hashrate_target: int = None,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
if wattage_target is not None and hashrate_target is not None:
|
||||
logging.error(
|
||||
"Cannot use both wattage_target and hashrate_target, using wattage_target."
|
||||
@@ -362,62 +372,71 @@ class BOSerGRPCAPI:
|
||||
),
|
||||
)
|
||||
|
||||
async def get_active_performance_mode(self):
|
||||
async def get_active_performance_mode(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_active_performance_mode", GetPerformanceModeRequest()
|
||||
"get_active_performance_mode", message=GetPerformanceModeRequest()
|
||||
)
|
||||
|
||||
async def get_pool_groups(self):
|
||||
return await self.send_command("get_pool_groups", GetPoolGroupsRequest())
|
||||
|
||||
async def create_pool_group(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PoolService/CreatePoolGroup")
|
||||
|
||||
async def update_pool_group(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PoolService/UpdatePoolGroup")
|
||||
|
||||
async def remove_pool_group(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PoolService/RemovePoolGroup")
|
||||
|
||||
async def get_miner_configuration(self):
|
||||
async def get_pool_groups(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_configuration", GetMinerConfigurationRequest()
|
||||
"get_pool_groups", message=GetPoolGroupsRequest()
|
||||
)
|
||||
|
||||
async def get_constraints(self):
|
||||
return await self.send_command("get_constraints", GetConstraintsRequest())
|
||||
async def create_pool_group(self) -> dict:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_license_state(self):
|
||||
return await self.send_command("get_license_state", GetLicenseStateRequest())
|
||||
async def update_pool_group(self) -> dict:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_miner_status(self):
|
||||
return await self.send_command("get_miner_status", GetMinerStatusRequest())
|
||||
async def remove_pool_group(self) -> dict:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_miner_details(self):
|
||||
return await self.send_command("get_miner_details", GetMinerDetailsRequest())
|
||||
|
||||
async def get_miner_stats(self):
|
||||
return await self.send_command("get_miner_stats", GetMinerStatsRequest())
|
||||
|
||||
async def get_hashboards(self):
|
||||
return await self.send_command("get_hashboards", GetHashboardsRequest())
|
||||
|
||||
async def get_support_archive(self):
|
||||
async def get_miner_configuration(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_support_archive", GetSupportArchiveRequest()
|
||||
"get_miner_configuration", message=GetMinerConfigurationRequest()
|
||||
)
|
||||
|
||||
async def get_constraints(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_constraints", message=GetConstraintsRequest()
|
||||
)
|
||||
|
||||
async def get_license_state(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_license_state", message=GetLicenseStateRequest()
|
||||
)
|
||||
|
||||
async def get_miner_status(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_status", message=GetMinerStatusRequest()
|
||||
)
|
||||
|
||||
async def get_miner_details(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_details", message=GetMinerDetailsRequest()
|
||||
)
|
||||
|
||||
async def get_miner_stats(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_stats", message=GetMinerStatsRequest()
|
||||
)
|
||||
|
||||
async def get_hashboards(self) -> dict:
|
||||
return await self.send_command("get_hashboards", message=GetHashboardsRequest())
|
||||
|
||||
async def get_support_archive(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_support_archive", message=GetSupportArchiveRequest()
|
||||
)
|
||||
|
||||
async def enable_hashboards(
|
||||
self,
|
||||
hashboard_ids: List[str],
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"enable_hashboards",
|
||||
EnableHashboardsRequest(
|
||||
message=EnableHashboardsRequest(
|
||||
hashboard_ids=hashboard_ids, save_action=save_action
|
||||
),
|
||||
)
|
||||
@@ -426,10 +445,10 @@ class BOSerGRPCAPI:
|
||||
self,
|
||||
hashboard_ids: List[str],
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
) -> dict:
|
||||
return await self.send_command(
|
||||
"disable_hashboards",
|
||||
DisableHashboardsRequest(
|
||||
message=DisableHashboardsRequest(
|
||||
hashboard_ids=hashboard_ids, save_action=save_action
|
||||
),
|
||||
)
|
||||
@@ -13,33 +13,38 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.web import BaseWebAPI
|
||||
|
||||
|
||||
class BOSMinerLuCIAPI:
|
||||
def __init__(self, ip: str, pwd: str):
|
||||
self.ip = ip
|
||||
class BOSMinerWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self.pwd = settings.get("default_bosminer_password", "root")
|
||||
self.port = 80
|
||||
|
||||
async def multicommand(self, *commands: str) -> dict:
|
||||
data = {}
|
||||
for command in commands:
|
||||
data[command] = await self.send_command(command, ignore_errors=True)
|
||||
return data
|
||||
|
||||
async def send_command(self, path: str, ignore_errors: bool = False) -> dict:
|
||||
async def send_command(
|
||||
self,
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
try:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
await self.auth(client)
|
||||
data = await client.get(
|
||||
f"http://{self.ip}:{self.port}/cgi-bin/luci/{path}",
|
||||
f"http://{self.ip}:{self.port}/cgi-bin/luci/{command}",
|
||||
headers={"User-Agent": "BTC Tools v0.1"},
|
||||
)
|
||||
if data.status_code == 200:
|
||||
@@ -47,14 +52,20 @@ class BOSMinerLuCIAPI:
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(
|
||||
f"LUCI web command failed: path={path}, code={data.status_code}"
|
||||
f"LUCI web command failed: command={command}, code={data.status_code}"
|
||||
)
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(f"LUCI web command failed: path={path}")
|
||||
raise APIError(f"LUCI web command failed: command={command}")
|
||||
|
||||
async def auth(self, session: httpx.AsyncClient):
|
||||
async def multicommand(self, *commands: str) -> dict:
|
||||
data = {}
|
||||
for command in commands:
|
||||
data[command] = await self.send_command(command, ignore_errors=True)
|
||||
return data
|
||||
|
||||
async def auth(self, session: httpx.AsyncClient) -> None:
|
||||
login = {"luci_username": self.username, "luci_password": self.pwd}
|
||||
url = f"http://{self.ip}:{self.port}/cgi-bin/luci"
|
||||
headers = {
|
||||
@@ -63,22 +74,22 @@ class BOSMinerLuCIAPI:
|
||||
}
|
||||
await session.post(url, headers=headers, data=login)
|
||||
|
||||
async def get_net_conf(self):
|
||||
async def get_net_conf(self) -> dict:
|
||||
return await self.send_command("admin/network/iface_status/lan")
|
||||
|
||||
async def get_cfg_metadata(self):
|
||||
async def get_cfg_metadata(self) -> dict:
|
||||
return await self.send_command("admin/miner/cfg_metadata")
|
||||
|
||||
async def get_cfg_data(self):
|
||||
async def get_cfg_data(self) -> dict:
|
||||
return await self.send_command("admin/miner/cfg_data")
|
||||
|
||||
async def get_bos_info(self):
|
||||
async def get_bos_info(self) -> dict:
|
||||
return await self.send_command("bos/info")
|
||||
|
||||
async def get_overview(self):
|
||||
async def get_overview(self) -> dict:
|
||||
return await self.send_command(
|
||||
"admin/status/overview?status=1"
|
||||
) # needs status=1 or it fails
|
||||
|
||||
async def get_api_status(self):
|
||||
async def get_api_status(self) -> dict:
|
||||
return await self.send_command("admin/miner/api_status")
|
||||
@@ -1,105 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import json
|
||||
from typing import Union
|
||||
|
||||
import httpx
|
||||
|
||||
from pyasic import settings
|
||||
|
||||
|
||||
class BOSerGraphQLAPI:
|
||||
def __init__(self, ip: str, pwd: str):
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self.port = 80
|
||||
|
||||
async def multicommand(self, *commands: dict) -> dict:
|
||||
def merge(*d: dict):
|
||||
ret = {}
|
||||
for i in d:
|
||||
if i:
|
||||
for k in i:
|
||||
if k not in ret:
|
||||
ret[k] = i[k]
|
||||
else:
|
||||
ret[k] = merge(ret[k], i[k])
|
||||
return None if ret == {} else ret
|
||||
|
||||
command = merge(*commands)
|
||||
data = await self.send_command(command)
|
||||
if data is not None:
|
||||
if data.get("data") is None:
|
||||
try:
|
||||
commands = list(commands)
|
||||
# noinspection PyTypeChecker
|
||||
commands.remove({"bos": {"faultLight": None}})
|
||||
command = merge(*commands)
|
||||
data = await self.send_command(command)
|
||||
except (LookupError, ValueError):
|
||||
pass
|
||||
if not data:
|
||||
data = {}
|
||||
data["multicommand"] = False
|
||||
return data
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: dict,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}:{self.port}/graphql"
|
||||
query = command
|
||||
if command is None:
|
||||
return {}
|
||||
if command.get("query") is None:
|
||||
query = {"query": self.parse_command(command)}
|
||||
try:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
await self.auth(client)
|
||||
data = await client.post(url, json=query)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if data.status_code == 200:
|
||||
try:
|
||||
return data.json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
|
||||
def parse_command(self, graphql_command: Union[dict, set]) -> str:
|
||||
if isinstance(graphql_command, dict):
|
||||
data = []
|
||||
for key in graphql_command:
|
||||
if graphql_command[key] is not None:
|
||||
parsed = self.parse_command(graphql_command[key])
|
||||
data.append(key + parsed)
|
||||
else:
|
||||
data.append(key)
|
||||
else:
|
||||
data = graphql_command
|
||||
return "{" + ",".join(data) + "}"
|
||||
|
||||
async def auth(self, client: httpx.AsyncClient) -> None:
|
||||
url = f"http://{self.ip}:{self.port}/graphql"
|
||||
await client.post(
|
||||
url,
|
||||
json={
|
||||
"query": (
|
||||
f'mutation{{auth{{login(username:"{self.username}", password:"{self.pwd}"){{__typename}}}}}}'
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -13,8 +13,10 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Union
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -28,32 +30,30 @@ class ePICWebAPI(BaseWebAPI):
|
||||
super().__init__(ip)
|
||||
self.username = "root"
|
||||
self.pwd = settings.get("default_epic_web_password", "letmein")
|
||||
self.token = None
|
||||
self.port = 4028
|
||||
self.token = None
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
post: bool = False,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
if post or parameters != {}:
|
||||
post = True
|
||||
post = privileged or not parameters == {}
|
||||
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
for i in range(settings.get("get_data_retries", 1) + 1):
|
||||
try:
|
||||
if post:
|
||||
epic_param = {
|
||||
"param": parameters.get("parameters"),
|
||||
"password": self.pwd,
|
||||
}
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/{command}",
|
||||
timeout=5,
|
||||
json=epic_param,
|
||||
json={
|
||||
**parameters,
|
||||
"password": self.pwd,
|
||||
},
|
||||
)
|
||||
else:
|
||||
response = await client.get(
|
||||
@@ -89,31 +89,31 @@ class ePICWebAPI(BaseWebAPI):
|
||||
return data
|
||||
|
||||
async def restart_epic(self) -> dict:
|
||||
return await self.send_command("softreboot", post=True)
|
||||
return await self.send_command("softreboot", privileged=True)
|
||||
|
||||
async def reboot(self) -> dict:
|
||||
return await self.send_command("reboot", post=True)
|
||||
return await self.send_command("reboot", privileged=True)
|
||||
|
||||
async def pause_mining(self) -> dict:
|
||||
return await self.send_command("miner", post=True, parameters="Stop")
|
||||
return await self.send_command("miner", param="Stop")
|
||||
|
||||
async def resume_mining(self) -> dict:
|
||||
return await self.send_command("miner", post=True, parameters="Autostart")
|
||||
return await self.send_command("miner", param="Autostart")
|
||||
|
||||
async def stop_mining(self) -> dict:
|
||||
return await self.send_command("miner", post=True, parameters="Stop")
|
||||
return await self.send_command("miner", param="Stop")
|
||||
|
||||
async def start_mining(self) -> dict:
|
||||
return await self.send_command("miner", post=True, parameters="Autostart")
|
||||
return await self.send_command("miner", param="Autostart")
|
||||
|
||||
async def summary(self):
|
||||
async def summary(self) -> dict:
|
||||
return await self.send_command("summary")
|
||||
|
||||
async def hashrate(self):
|
||||
async def hashrate(self) -> dict:
|
||||
return await self.send_command("hashrate")
|
||||
|
||||
async def network(self):
|
||||
async def network(self) -> dict:
|
||||
return await self.send_command("network")
|
||||
|
||||
async def capabilities(self):
|
||||
async def capabilities(self) -> dict:
|
||||
return await self.send_command("capabilities")
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import warnings
|
||||
from typing import Union
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -28,9 +30,9 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
super().__init__(ip)
|
||||
self.username = "admin"
|
||||
self.pwd = settings.get("default_goldshell_web_password", "123456789")
|
||||
self.jwt = None
|
||||
self.token = None
|
||||
|
||||
async def auth(self):
|
||||
async def auth(self) -> str | None:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
await client.get(f"http://{self.ip}:{self.port}/user/logout")
|
||||
@@ -54,47 +56,43 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
f"Could not authenticate web token with miner: {self}"
|
||||
)
|
||||
else:
|
||||
self.jwt = auth.get("JWT Token")
|
||||
self.token = auth.get("JWT Token")
|
||||
else:
|
||||
self.jwt = auth.get("JWT Token")
|
||||
return self.jwt
|
||||
self.token = auth.get("JWT Token")
|
||||
return self.token
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
if parameters.get("pool_pwd"):
|
||||
parameters["pass"] = parameters["pool_pwd"]
|
||||
parameters.pop("pool_pwd")
|
||||
if self.jwt is None:
|
||||
if self.token is None:
|
||||
await self.auth()
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
for _ in range(settings.get("get_data_retries", 1)):
|
||||
try:
|
||||
if parameters:
|
||||
if not parameters == {}:
|
||||
response = await client.put(
|
||||
f"http://{self.ip}:{self.port}/mcb/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
headers={"Authorization": "Bearer " + self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}:{self.port}/mcb/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
headers={"Authorization": "Bearer " + self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
json_data = response.json()
|
||||
return json_data
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
except TypeError:
|
||||
await self.auth()
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
pass
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||
@@ -107,7 +105,7 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
try:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}:{self.port}/mcb/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
headers={"Authorization": "Bearer " + self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
json_data = response.json()
|
||||
@@ -120,19 +118,25 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
await self.auth()
|
||||
return data
|
||||
|
||||
async def pools(self):
|
||||
async def pools(self) -> dict:
|
||||
return await self.send_command("pools")
|
||||
|
||||
async def newpool(self, url: str, user: str, password: str):
|
||||
return await self.send_command("newpool", url=url, user=user, pool_pwd=password)
|
||||
|
||||
async def delpool(self, url: str, user: str, password: str, dragid: int = 0):
|
||||
async def newpool(self, url: str, user: str, password: str) -> dict:
|
||||
# looks dumb, but cant pass `pass` since it is a built in type
|
||||
return await self.send_command(
|
||||
"delpool", url=url, user=user, pool_pwd=password, dragid=dragid
|
||||
"newpool", **{"url": url, "user": user, "pass": password}
|
||||
)
|
||||
|
||||
async def setting(self):
|
||||
async def delpool(
|
||||
self, url: str, user: str, password: str, dragid: int = 0
|
||||
) -> dict:
|
||||
# looks dumb, but cant pass `pass` since it is a built in type
|
||||
return await self.send_command(
|
||||
"delpool", **{"url": url, "user": user, "pass": password, "dragid": dragid}
|
||||
)
|
||||
|
||||
async def setting(self) -> dict:
|
||||
return await self.send_command("setting")
|
||||
|
||||
async def status(self):
|
||||
async def status(self) -> dict:
|
||||
return await self.send_command("status")
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import warnings
|
||||
from typing import Union
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -29,9 +31,9 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
super().__init__(ip)
|
||||
self.username = "admin"
|
||||
self.pwd = settings.get("default_innosilicon_web_password", "admin")
|
||||
self.jwt = None
|
||||
self.token = None
|
||||
|
||||
async def auth(self):
|
||||
async def auth(self) -> str | None:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
auth = await client.post(
|
||||
@@ -42,24 +44,25 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
warnings.warn(f"Could not authenticate web token with miner: {self}")
|
||||
else:
|
||||
json_auth = auth.json()
|
||||
self.jwt = json_auth.get("jwt")
|
||||
return self.jwt
|
||||
self.token = json_auth.get("jwt")
|
||||
return self.token
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
if self.jwt is None:
|
||||
if self.token is None:
|
||||
await self.auth()
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
for _ in range(settings.get("get_data_retries", 1)):
|
||||
try:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/api/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
headers={"Authorization": "Bearer " + self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
)
|
||||
@@ -79,9 +82,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
raise APIError(json_data["message"])
|
||||
raise APIError("Innosilicon web api command failed.")
|
||||
return json_data
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
except json.JSONDecodeError:
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
pass
|
||||
|
||||
async def multicommand(
|
||||
@@ -95,7 +96,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
try:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/api/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
headers={"Authorization": "Bearer " + self.token},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
json_data = response.json()
|
||||
@@ -123,14 +124,14 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
async def type(self) -> dict:
|
||||
return await self.send_command("type")
|
||||
|
||||
async def get_all(self):
|
||||
async def get_all(self) -> dict:
|
||||
return await self.send_command("getAll")
|
||||
|
||||
async def get_error_detail(self):
|
||||
async def get_error_detail(self) -> dict:
|
||||
return await self.send_command("getErrorDetail")
|
||||
|
||||
async def pools(self):
|
||||
async def pools(self) -> dict:
|
||||
return await self.send_command("pools")
|
||||
|
||||
async def poweroff(self):
|
||||
async def poweroff(self) -> dict:
|
||||
return await self.send_command("poweroff")
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import warnings
|
||||
from typing import Union
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -30,7 +32,7 @@ class VNishWebAPI(BaseWebAPI):
|
||||
self.pwd = settings.get("default_vnish_web_password", "admin")
|
||||
self.token = None
|
||||
|
||||
async def auth(self):
|
||||
async def auth(self) -> str | None:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
auth = await client.post(
|
||||
@@ -51,11 +53,13 @@ class VNishWebAPI(BaseWebAPI):
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
post = privileged or not parameters == {}
|
||||
if self.token is None:
|
||||
await self.auth()
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
@@ -65,15 +69,7 @@ class VNishWebAPI(BaseWebAPI):
|
||||
if command.startswith("system"):
|
||||
auth = "Bearer " + self.token
|
||||
|
||||
if parameters.get("post"):
|
||||
parameters.pop("post")
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/api/v1/{command}",
|
||||
headers={"Authorization": auth},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
)
|
||||
elif not parameters == {}:
|
||||
if post:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:{self.port}/api/v1/{command}",
|
||||
headers={"Authorization": auth},
|
||||
@@ -94,11 +90,7 @@ class VNishWebAPI(BaseWebAPI):
|
||||
if json_data:
|
||||
return json_data
|
||||
return {"success": True}
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
except AttributeError:
|
||||
except (httpx.HTTPError, json.JSONDecodeError, AttributeError):
|
||||
pass
|
||||
|
||||
async def multicommand(
|
||||
@@ -111,40 +103,40 @@ class VNishWebAPI(BaseWebAPI):
|
||||
return data
|
||||
|
||||
async def restart_vnish(self) -> dict:
|
||||
return await self.send_command("mining/restart", post=True)
|
||||
return await self.send_command("mining/restart", privileged=True)
|
||||
|
||||
async def reboot(self) -> dict:
|
||||
return await self.send_command("system/reboot", post=True)
|
||||
return await self.send_command("system/reboot", privileged=True)
|
||||
|
||||
async def pause_mining(self) -> dict:
|
||||
return await self.send_command("mining/pause", post=True)
|
||||
return await self.send_command("mining/pause", privileged=True)
|
||||
|
||||
async def resume_mining(self) -> dict:
|
||||
return await self.send_command("mining/resume", post=True)
|
||||
return await self.send_command("mining/resume", privileged=True)
|
||||
|
||||
async def stop_mining(self) -> dict:
|
||||
return await self.send_command("mining/stop", post=True)
|
||||
return await self.send_command("mining/stop", privileged=True)
|
||||
|
||||
async def start_mining(self) -> dict:
|
||||
return await self.send_command("mining/start", post=True)
|
||||
return await self.send_command("mining/start", privileged=True)
|
||||
|
||||
async def info(self):
|
||||
async def info(self) -> dict:
|
||||
return await self.send_command("info")
|
||||
|
||||
async def summary(self):
|
||||
async def summary(self) -> dict:
|
||||
return await self.send_command("summary")
|
||||
|
||||
async def chips(self):
|
||||
async def chips(self) -> dict:
|
||||
return await self.send_command("chips")
|
||||
|
||||
async def layout(self):
|
||||
async def layout(self) -> dict:
|
||||
return await self.send_command("layout")
|
||||
|
||||
async def status(self):
|
||||
async def status(self) -> dict:
|
||||
return await self.send_command("status")
|
||||
|
||||
async def settings(self):
|
||||
async def settings(self) -> dict:
|
||||
return await self.send_command("settings")
|
||||
|
||||
async def autotune_presets(self):
|
||||
async def autotune_presets(self) -> dict:
|
||||
return await self.send_command("autotune/presets")
|
||||
|
||||
Reference in New Issue
Block a user