refactor: move ssh to miner.ssh
This commit is contained in:
@@ -29,6 +29,7 @@ from pyasic.miners.base import (
|
|||||||
WebAPICommand,
|
WebAPICommand,
|
||||||
)
|
)
|
||||||
from pyasic.rpc import APIError
|
from pyasic.rpc import APIError
|
||||||
|
from pyasic.ssh.antminer import AntminerModernSSH
|
||||||
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
|
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
|
||||||
|
|
||||||
ANTMINER_MODERN_DATA_LOC = DataLocations(
|
ANTMINER_MODERN_DATA_LOC = DataLocations(
|
||||||
@@ -85,6 +86,10 @@ class AntminerModern(BMMiner):
|
|||||||
"""Handler for AntMiners with the modern web interface, such as S19"""
|
"""Handler for AntMiners with the modern web interface, such as S19"""
|
||||||
|
|
||||||
_web_cls = AntminerModernWebAPI
|
_web_cls = AntminerModernWebAPI
|
||||||
|
web: AntminerModernWebAPI
|
||||||
|
|
||||||
|
_ssh_cls = AntminerModernSSH
|
||||||
|
ssh: AntminerModernSSH
|
||||||
|
|
||||||
data_locations = ANTMINER_MODERN_DATA_LOC
|
data_locations = ANTMINER_MODERN_DATA_LOC
|
||||||
|
|
||||||
@@ -391,6 +396,7 @@ class AntminerOld(CGMiner):
|
|||||||
"""Handler for AntMiners with the old web interface, such as S17"""
|
"""Handler for AntMiners with the old web interface, such as S17"""
|
||||||
|
|
||||||
_web_cls = AntminerOldWebAPI
|
_web_cls = AntminerOldWebAPI
|
||||||
|
web: AntminerOldWebAPI
|
||||||
|
|
||||||
data_locations = ANTMINER_OLD_DATA_LOC
|
data_locations = ANTMINER_OLD_DATA_LOC
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class BFGMiner(BaseMiner):
|
|||||||
"""Base handler for BFGMiner based miners."""
|
"""Base handler for BFGMiner based miners."""
|
||||||
|
|
||||||
_api_cls = BFGMinerRPCAPI
|
_api_cls = BFGMinerRPCAPI
|
||||||
|
api: BFGMinerRPCAPI
|
||||||
|
|
||||||
data_locations = BFGMINER_DATA_LOC
|
data_locations = BFGMINER_DATA_LOC
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class BMMiner(BaseMiner):
|
|||||||
"""Base handler for BMMiner based miners."""
|
"""Base handler for BMMiner based miners."""
|
||||||
|
|
||||||
_api_cls = BMMinerRPCAPI
|
_api_cls = BMMinerRPCAPI
|
||||||
|
api: BMMinerRPCAPI
|
||||||
|
|
||||||
data_locations = BMMINER_DATA_LOC
|
data_locations = BMMINER_DATA_LOC
|
||||||
|
|
||||||
@@ -80,14 +81,6 @@ class BMMiner(BaseMiner):
|
|||||||
self.config = MinerConfig.from_api(pools)
|
self.config = MinerConfig.from_api(pools)
|
||||||
return self.config
|
return self.config
|
||||||
|
|
||||||
async def reboot(self) -> bool:
|
|
||||||
logging.debug(f"{self}: Sending reboot command.")
|
|
||||||
ret = await self.send_ssh_command("reboot")
|
|
||||||
logging.debug(f"{self}: Reboot command completed.")
|
|
||||||
if ret is None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||||
##################################################
|
##################################################
|
||||||
@@ -122,9 +115,6 @@ class BMMiner(BaseMiner):
|
|||||||
|
|
||||||
return self.fw_ver
|
return self.fw_ver
|
||||||
|
|
||||||
async def _get_hostname(self) -> Optional[str]:
|
|
||||||
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
|
|
||||||
return hn
|
|
||||||
|
|
||||||
async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||||
# get hr from API
|
# get hr from API
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from pyasic.miners.base import (
|
|||||||
WebAPICommand,
|
WebAPICommand,
|
||||||
)
|
)
|
||||||
from pyasic.rpc.bosminer import BOSMinerRPCAPI
|
from pyasic.rpc.bosminer import BOSMinerRPCAPI
|
||||||
|
from pyasic.ssh.braiins_os import BOSMinerSSH
|
||||||
from pyasic.web.braiins_os import BOSerWebAPI, BOSMinerWebAPI
|
from pyasic.web.braiins_os import BOSerWebAPI, BOSMinerWebAPI
|
||||||
|
|
||||||
BOSMINER_DATA_LOC = DataLocations(
|
BOSMINER_DATA_LOC = DataLocations(
|
||||||
@@ -98,7 +99,11 @@ class BOSMiner(BaseMiner):
|
|||||||
"""Handler for old versions of BraiinsOS+ (pre-gRPC)"""
|
"""Handler for old versions of BraiinsOS+ (pre-gRPC)"""
|
||||||
|
|
||||||
_api_cls = BOSMinerRPCAPI
|
_api_cls = BOSMinerRPCAPI
|
||||||
|
api: BOSMinerRPCAPI
|
||||||
_web_cls = BOSMinerWebAPI
|
_web_cls = BOSMinerWebAPI
|
||||||
|
web: BOSMinerWebAPI
|
||||||
|
_ssh_cls = BOSMinerSSH
|
||||||
|
ssh: BOSMinerSSH
|
||||||
|
|
||||||
firmware = "BOS+"
|
firmware = "BOS+"
|
||||||
|
|
||||||
@@ -108,7 +113,7 @@ class BOSMiner(BaseMiner):
|
|||||||
supports_autotuning = True
|
supports_autotuning = True
|
||||||
|
|
||||||
async def fault_light_on(self) -> bool:
|
async def fault_light_on(self) -> bool:
|
||||||
ret = await self.send_ssh_command("miner fault_light on")
|
ret = await self.ssh.fault_light_on()
|
||||||
|
|
||||||
if isinstance(ret, str):
|
if isinstance(ret, str):
|
||||||
self.light = True
|
self.light = True
|
||||||
@@ -116,7 +121,7 @@ class BOSMiner(BaseMiner):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def fault_light_off(self) -> bool:
|
async def fault_light_off(self) -> bool:
|
||||||
ret = await self.send_ssh_command("miner fault_light off")
|
ret = await self.ssh.fault_light_off()
|
||||||
|
|
||||||
if isinstance(ret, str):
|
if isinstance(ret, str):
|
||||||
self.light = False
|
self.light = False
|
||||||
@@ -127,7 +132,7 @@ class BOSMiner(BaseMiner):
|
|||||||
return await self.restart_bosminer()
|
return await self.restart_bosminer()
|
||||||
|
|
||||||
async def restart_bosminer(self) -> bool:
|
async def restart_bosminer(self) -> bool:
|
||||||
ret = await self.send_ssh_command("/etc/init.d/bosminer restart")
|
ret = await self.ssh.restart_bosminer()
|
||||||
|
|
||||||
if isinstance(ret, str):
|
if isinstance(ret, str):
|
||||||
return True
|
return True
|
||||||
@@ -156,14 +161,14 @@ class BOSMiner(BaseMiner):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def reboot(self) -> bool:
|
async def reboot(self) -> bool:
|
||||||
ret = await self.send_ssh_command("/sbin/reboot")
|
ret = await self.ssh.reboot()
|
||||||
|
|
||||||
if isinstance(ret, str):
|
if isinstance(ret, str):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def get_config(self) -> MinerConfig:
|
async def get_config(self) -> MinerConfig:
|
||||||
raw_data = await self.send_ssh_command("cat /etc/bosminer.toml")
|
raw_data = await self.ssh.get_config_file()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
toml_data = toml.loads(raw_data)
|
toml_data = toml.loads(raw_data)
|
||||||
@@ -189,7 +194,7 @@ class BOSMiner(BaseMiner):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
conn = await self._get_ssh_connection()
|
conn = await self.ssh._get_connection()
|
||||||
except ConnectionError as e:
|
except ConnectionError as e:
|
||||||
raise APIError("SSH connection failed when sending config.") from e
|
raise APIError("SSH connection failed when sending config.") from e
|
||||||
|
|
||||||
@@ -246,7 +251,7 @@ class BOSMiner(BaseMiner):
|
|||||||
f"option dns '{dns}'",
|
f"option dns '{dns}'",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data = await self.send_ssh_command("cat /etc/config/network")
|
data = await self.ssh.get_network_config()
|
||||||
|
|
||||||
split_data = data.split("\n\n")
|
split_data = data.split("\n\n")
|
||||||
for idx, val in enumerate(split_data):
|
for idx, val in enumerate(split_data):
|
||||||
@@ -254,7 +259,7 @@ class BOSMiner(BaseMiner):
|
|||||||
split_data[idx] = cfg_data_lan
|
split_data[idx] = cfg_data_lan
|
||||||
config = "\n\n".join(split_data)
|
config = "\n\n".join(split_data)
|
||||||
|
|
||||||
conn = await self._get_ssh_connection()
|
conn = await self.ssh._get_connection()
|
||||||
|
|
||||||
async with conn:
|
async with conn:
|
||||||
await conn.run("echo '" + config + "' > /etc/config/network")
|
await conn.run("echo '" + config + "' > /etc/config/network")
|
||||||
@@ -268,7 +273,7 @@ class BOSMiner(BaseMiner):
|
|||||||
"option proto 'dhcp'",
|
"option proto 'dhcp'",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data = await self.send_ssh_command("cat /etc/config/network")
|
data = await self.ssh.get_network_config()
|
||||||
|
|
||||||
split_data = data.split("\n\n")
|
split_data = data.split("\n\n")
|
||||||
for idx, val in enumerate(split_data):
|
for idx, val in enumerate(split_data):
|
||||||
@@ -276,7 +281,7 @@ class BOSMiner(BaseMiner):
|
|||||||
split_data[idx] = cfg_data_lan
|
split_data[idx] = cfg_data_lan
|
||||||
config = "\n\n".join(split_data)
|
config = "\n\n".join(split_data)
|
||||||
|
|
||||||
conn = await self._get_ssh_connection()
|
conn = await self.ssh._get_connection()
|
||||||
|
|
||||||
async with conn:
|
async with conn:
|
||||||
await conn.run("echo '" + config + "' > /etc/config/network")
|
await conn.run("echo '" + config + "' > /etc/config/network")
|
||||||
@@ -346,9 +351,7 @@ class BOSMiner(BaseMiner):
|
|||||||
|
|
||||||
async def _get_hostname(self) -> Union[str, None]:
|
async def _get_hostname(self) -> Union[str, None]:
|
||||||
try:
|
try:
|
||||||
hostname = (
|
hostname = (await self.ssh.get_hostname()).strip()
|
||||||
await self.send_ssh_command("cat /proc/sys/kernel/hostname")
|
|
||||||
).strip()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"{self} - Getting hostname failed: {e}")
|
logging.error(f"{self} - Getting hostname failed: {e}")
|
||||||
return None
|
return None
|
||||||
@@ -520,7 +523,7 @@ class BOSMiner(BaseMiner):
|
|||||||
return self.light
|
return self.light
|
||||||
try:
|
try:
|
||||||
data = (
|
data = (
|
||||||
await self.send_ssh_command("cat /sys/class/leds/'Red LED'/delay_off")
|
await self.ssh.get_led_status()
|
||||||
).strip()
|
).strip()
|
||||||
self.light = False
|
self.light = False
|
||||||
if data == "50":
|
if data == "50":
|
||||||
@@ -652,7 +655,9 @@ class BOSer(BaseMiner):
|
|||||||
"""Handler for new versions of BraiinsOS+ (post-gRPC)"""
|
"""Handler for new versions of BraiinsOS+ (post-gRPC)"""
|
||||||
|
|
||||||
_api_cls = BOSMinerRPCAPI
|
_api_cls = BOSMinerRPCAPI
|
||||||
|
web: BOSMinerRPCAPI
|
||||||
_web_cls = BOSerWebAPI
|
_web_cls = BOSerWebAPI
|
||||||
|
web: BOSerWebAPI
|
||||||
|
|
||||||
data_locations = BOSER_DATA_LOC
|
data_locations = BOSER_DATA_LOC
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,10 @@ BTMINER_DATA_LOC = DataLocations(
|
|||||||
|
|
||||||
|
|
||||||
class BTMiner(BaseMiner):
|
class BTMiner(BaseMiner):
|
||||||
|
"""Base handler for BTMiner based miners."""
|
||||||
|
|
||||||
_api_cls = BTMinerRPCAPI
|
_api_cls = BTMinerRPCAPI
|
||||||
|
api: BTMinerRPCAPI
|
||||||
|
|
||||||
data_locations = BTMINER_DATA_LOC
|
data_locations = BTMINER_DATA_LOC
|
||||||
|
|
||||||
|
|||||||
@@ -65,15 +65,10 @@ class CGMiner(BaseMiner):
|
|||||||
"""Base handler for CGMiner based miners"""
|
"""Base handler for CGMiner based miners"""
|
||||||
|
|
||||||
_api_cls = CGMinerRPCAPI
|
_api_cls = CGMinerRPCAPI
|
||||||
|
api: CGMinerRPCAPI
|
||||||
|
|
||||||
data_locations = CGMINER_DATA_LOC
|
data_locations = CGMINER_DATA_LOC
|
||||||
|
|
||||||
async def reboot(self) -> bool:
|
|
||||||
ret = await self.send_ssh_command("reboot")
|
|
||||||
if ret is None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
async def get_config(self) -> MinerConfig:
|
async def get_config(self) -> MinerConfig:
|
||||||
# get pool data
|
# get pool data
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ EPIC_DATA_LOC = DataLocations(
|
|||||||
|
|
||||||
|
|
||||||
class ePIC(BaseMiner):
|
class ePIC(BaseMiner):
|
||||||
|
"""Handler for miners with the ePIC board"""
|
||||||
|
|
||||||
_web_cls = ePICWebAPI
|
_web_cls = ePICWebAPI
|
||||||
|
web: ePICWebAPI
|
||||||
|
|
||||||
firmware = "ePIC"
|
firmware = "ePIC"
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ GOLDSHELL_DATA_LOC = DataLocations(
|
|||||||
|
|
||||||
|
|
||||||
class GoldshellMiner(BFGMiner):
|
class GoldshellMiner(BFGMiner):
|
||||||
|
"""Handler for goldshell miners"""
|
||||||
|
|
||||||
_web_cls = GoldshellWebAPI
|
_web_cls = GoldshellWebAPI
|
||||||
|
web: GoldshellWebAPI
|
||||||
|
|
||||||
data_locations = GOLDSHELL_DATA_LOC
|
data_locations = GOLDSHELL_DATA_LOC
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class Innosilicon(CGMiner):
|
|||||||
"""Base handler for Innosilicon miners"""
|
"""Base handler for Innosilicon miners"""
|
||||||
|
|
||||||
_web_cls = InnosiliconWebAPI
|
_web_cls = InnosiliconWebAPI
|
||||||
|
web: InnosiliconWebAPI
|
||||||
|
|
||||||
data_locations = INNOSILICON_DATA_LOC
|
data_locations = INNOSILICON_DATA_LOC
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ LUXMINER_DATA_LOC = DataLocations(
|
|||||||
|
|
||||||
|
|
||||||
class LUXMiner(BaseMiner):
|
class LUXMiner(BaseMiner):
|
||||||
|
"""Handler for LuxOS miners"""
|
||||||
|
|
||||||
_api_cls = LUXMinerRPCAPI
|
_api_cls = LUXMinerRPCAPI
|
||||||
|
api: LUXMinerRPCAPI
|
||||||
|
|
||||||
firmware = "LuxOS"
|
firmware = "LuxOS"
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,10 @@ VNISH_DATA_LOC = DataLocations(
|
|||||||
|
|
||||||
|
|
||||||
class VNish(BMMiner):
|
class VNish(BMMiner):
|
||||||
|
"""Handler for VNish miners"""
|
||||||
|
|
||||||
_web_cls = VNishWebAPI
|
_web_cls = VNishWebAPI
|
||||||
|
web: VNishWebAPI
|
||||||
|
|
||||||
firmware = "VNish"
|
firmware = "VNish"
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,10 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import asyncio
|
import asyncio
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
|
||||||
import warnings
|
import warnings
|
||||||
from dataclasses import dataclass, field, make_dataclass
|
from dataclasses import dataclass, field, make_dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, List, Optional, Protocol, Tuple, Type, TypeVar, Union
|
from typing import List, Optional, Protocol, Tuple, Type, TypeVar, Union
|
||||||
|
|
||||||
import asyncssh
|
|
||||||
|
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, MinerData
|
from pyasic.data import Fan, HashBoard, MinerData
|
||||||
@@ -109,9 +106,12 @@ DataLocations = make_dataclass(
|
|||||||
class MinerProtocol(Protocol):
|
class MinerProtocol(Protocol):
|
||||||
_api_cls: Type = None
|
_api_cls: Type = None
|
||||||
_web_cls: Type = None
|
_web_cls: Type = None
|
||||||
|
_ssh_cls: Type = None
|
||||||
|
|
||||||
api = None
|
ip: str = None
|
||||||
web = None
|
api: _api_cls = None
|
||||||
|
web: _web_cls = None
|
||||||
|
ssh: _ssh_cls = None
|
||||||
|
|
||||||
make: str = None
|
make: str = None
|
||||||
raw_model: str = None
|
raw_model: str = None
|
||||||
@@ -150,69 +150,6 @@ class MinerProtocol(Protocol):
|
|||||||
model_data.append(f"({self.firmware})")
|
model_data.append(f"({self.firmware})")
|
||||||
return " ".join(model_data)
|
return " ".join(model_data)
|
||||||
|
|
||||||
@property
|
|
||||||
def pwd(self) -> Dict[str, str]:
|
|
||||||
data = {}
|
|
||||||
if self.web is not None:
|
|
||||||
data["web"] = self.web.pwd
|
|
||||||
if self.api is not None:
|
|
||||||
data["api"] = self.api.pwd
|
|
||||||
return data
|
|
||||||
|
|
||||||
@pwd.setter
|
|
||||||
def pwd(self, val: str) -> None:
|
|
||||||
if self.web is not None:
|
|
||||||
self.web.pwd = val
|
|
||||||
if self.api is not None:
|
|
||||||
self.api.pwd = val
|
|
||||||
|
|
||||||
@property
|
|
||||||
def username(self) -> Dict[str, str]:
|
|
||||||
data = {}
|
|
||||||
if self.web is not None:
|
|
||||||
data["web"] = self.web.pwd
|
|
||||||
if self.api is not None:
|
|
||||||
data["api"] = self.api.pwd
|
|
||||||
return data
|
|
||||||
|
|
||||||
@username.setter
|
|
||||||
def username(self, val) -> None:
|
|
||||||
if self.web is not None:
|
|
||||||
self.web.username = val
|
|
||||||
|
|
||||||
async def _get_ssh_connection(self) -> asyncssh.connect:
|
|
||||||
"""Create a new asyncssh connection"""
|
|
||||||
try:
|
|
||||||
conn = await asyncssh.connect(
|
|
||||||
str(self.ip),
|
|
||||||
known_hosts=None,
|
|
||||||
username="root",
|
|
||||||
password=self.pwd,
|
|
||||||
server_host_key_algs=["ssh-rsa"],
|
|
||||||
)
|
|
||||||
return conn
|
|
||||||
except asyncssh.misc.PermissionDenied as e:
|
|
||||||
raise ConnectionRefusedError from e
|
|
||||||
except Exception as e:
|
|
||||||
raise ConnectionError from e
|
|
||||||
|
|
||||||
async def send_ssh_command(self, cmd: str) -> Optional[str]:
|
|
||||||
"""Send an ssh command to the miner"""
|
|
||||||
try:
|
|
||||||
conn = await asyncio.wait_for(self._get_ssh_connection(), timeout=10)
|
|
||||||
except (ConnectionError, asyncio.TimeoutError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with conn:
|
|
||||||
resp = await conn.run(cmd)
|
|
||||||
result = str(max(resp.stdout, resp.stderr, key=len))
|
|
||||||
|
|
||||||
return result
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"{self} command {cmd} error: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def check_light(self) -> bool:
|
async def check_light(self) -> bool:
|
||||||
return await self.get_fault_light()
|
return await self.get_fault_light()
|
||||||
|
|
||||||
@@ -597,9 +534,9 @@ class MinerProtocol(Protocol):
|
|||||||
ip=str(self.ip),
|
ip=str(self.ip),
|
||||||
make=self.make,
|
make=self.make,
|
||||||
model=self.model,
|
model=self.model,
|
||||||
expected_chips=self.expected_chips
|
expected_chips=self.expected_chips * self.expected_hashboards
|
||||||
if self.expected_chips is not None
|
if self.expected_chips is not None
|
||||||
else 0 * self.expected_hashboards,
|
else 0,
|
||||||
expected_hashboards=self.expected_hashboards,
|
expected_hashboards=self.expected_hashboards,
|
||||||
hashboards=[
|
hashboards=[
|
||||||
HashBoard(slot=i, expected_chips=self.expected_chips)
|
HashBoard(slot=i, expected_chips=self.expected_chips)
|
||||||
@@ -632,6 +569,8 @@ class BaseMiner(MinerProtocol):
|
|||||||
self.api = self._api_cls(ip)
|
self.api = self._api_cls(ip)
|
||||||
if self._web_cls is not None:
|
if self._web_cls is not None:
|
||||||
self.web = self._web_cls(ip)
|
self.web = self._web_cls(ip)
|
||||||
|
if self._ssh_cls is not None:
|
||||||
|
self.ssh = self._ssh_cls(ip)
|
||||||
|
|
||||||
|
|
||||||
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)
|
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)
|
||||||
|
|||||||
@@ -28,13 +28,15 @@ _settings = { # defaults
|
|||||||
"factory_get_timeout": 3,
|
"factory_get_timeout": 3,
|
||||||
"get_data_retries": 1,
|
"get_data_retries": 1,
|
||||||
"api_function_timeout": 5,
|
"api_function_timeout": 5,
|
||||||
"default_whatsminer_password": "admin",
|
"default_whatsminer_rpc_password": "admin",
|
||||||
"default_innosilicon_password": "admin",
|
"default_innosilicon_web_password": "admin",
|
||||||
"default_antminer_password": "root",
|
"default_antminer_web_password": "root",
|
||||||
"default_bosminer_password": "root",
|
"default_bosminer_web_password": "root",
|
||||||
"default_vnish_password": "admin",
|
"default_vnish_web_password": "admin",
|
||||||
"default_goldshell_password": "123456789",
|
"default_goldshell_web_password": "123456789",
|
||||||
"default_hive_password": "admin",
|
"default_hive_web_password": "admin",
|
||||||
|
"default_antminer_ssh_password": "miner",
|
||||||
|
"default_bosminer_ssh_password": "root",
|
||||||
"socket_linger_time": 1000,
|
"socket_linger_time": 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
pyasic/ssh/__init__.py
Normal file
47
pyasic/ssh/__init__.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import asyncssh
|
||||||
|
|
||||||
|
|
||||||
|
class MinerSSH:
|
||||||
|
def __init__(self, ip: str) -> None:
|
||||||
|
self.ip = ip
|
||||||
|
self.pwd = None
|
||||||
|
self.username = "root"
|
||||||
|
self.port = 22
|
||||||
|
|
||||||
|
async def _get_connection(self) -> asyncssh.connect:
|
||||||
|
"""Create a new asyncssh connection"""
|
||||||
|
try:
|
||||||
|
conn = await asyncssh.connect(
|
||||||
|
str(self.ip),
|
||||||
|
port=self.port,
|
||||||
|
known_hosts=None,
|
||||||
|
username=self.username,
|
||||||
|
password=self.pwd,
|
||||||
|
server_host_key_algs=["ssh-rsa"],
|
||||||
|
)
|
||||||
|
return conn
|
||||||
|
except asyncssh.misc.PermissionDenied as e:
|
||||||
|
raise ConnectionRefusedError from e
|
||||||
|
except Exception as e:
|
||||||
|
raise ConnectionError from e
|
||||||
|
|
||||||
|
async def send_command(self, cmd: str) -> Optional[str]:
|
||||||
|
"""Send an ssh command to the miner"""
|
||||||
|
try:
|
||||||
|
conn = await asyncio.wait_for(self._get_connection(), timeout=10)
|
||||||
|
except (ConnectionError, asyncio.TimeoutError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with conn:
|
||||||
|
resp = await conn.run(cmd)
|
||||||
|
result = str(max(resp.stdout, resp.stderr, key=len))
|
||||||
|
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"{self} command {cmd} error: {e}")
|
||||||
|
return None
|
||||||
9
pyasic/ssh/antminer.py
Normal file
9
pyasic/ssh/antminer.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from pyasic import settings
|
||||||
|
from pyasic.ssh import MinerSSH
|
||||||
|
|
||||||
|
|
||||||
|
class AntminerModernSSH(MinerSSH):
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__(ip)
|
||||||
|
self.pwd = settings.get("default_antminer_ssh_password", "root")
|
||||||
|
self.username = "miner"
|
||||||
35
pyasic/ssh/braiins_os.py
Normal file
35
pyasic/ssh/braiins_os.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
from pyasic import settings
|
||||||
|
from pyasic.ssh import MinerSSH
|
||||||
|
|
||||||
|
|
||||||
|
class BOSMinerSSH(MinerSSH):
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__(ip)
|
||||||
|
self.pwd = settings.get("default_bosminer_ssh_password", "root")
|
||||||
|
|
||||||
|
async def get_board_info(self):
|
||||||
|
return await self.send_command("bosminer model -d")
|
||||||
|
|
||||||
|
async def fault_light_on(self):
|
||||||
|
return await self.send_command("miner fault_light on")
|
||||||
|
|
||||||
|
async def fault_light_off(self):
|
||||||
|
return await self.send_command("miner fault_light off")
|
||||||
|
|
||||||
|
async def restart_bosminer(self):
|
||||||
|
return await self.send_command("/etc/init.d/bosminer restart")
|
||||||
|
|
||||||
|
async def reboot(self):
|
||||||
|
return await self.send_command("/sbin/reboot")
|
||||||
|
|
||||||
|
async def get_config_file(self):
|
||||||
|
return await self.send_command("cat /etc/bosminer.toml")
|
||||||
|
|
||||||
|
async def get_network_config(self):
|
||||||
|
return await self.send_command("cat /etc/config/network")
|
||||||
|
|
||||||
|
async def get_hostname(self):
|
||||||
|
return await self.send_command("cat /proc/sys/kernel/hostname")
|
||||||
|
|
||||||
|
async def get_led_status(self):
|
||||||
|
return await self.send_command("cat /sys/class/leds/'Red LED'/delay_off")
|
||||||
Reference in New Issue
Block a user