Compare commits

...

1 Commits

Author SHA1 Message Date
UpstreamData
841a546505 bug: fix some bugs. 2023-03-02 16:17:32 -07:00
10 changed files with 90 additions and 208 deletions

View File

@@ -291,7 +291,17 @@ class _PoolGroup:
Parameters: Parameters:
user_suffix: The suffix to append to username. user_suffix: The suffix to append to username.
""" """
pools = {} pools = {
"Pool1": None,
"UserName1": None,
"Password1": None,
"Pool2": None,
"UserName2": None,
"Password2": None,
"Pool3": None,
"UserName3": None,
"Password3": None,
}
for idx, pool in enumerate(self.pools[:3]): for idx, pool in enumerate(self.pools[:3]):
pool_data = pool.as_inno(user_suffix=user_suffix) pool_data = pool.as_inno(user_suffix=user_suffix)
for key in pool_data: for key in pool_data:
@@ -516,8 +526,14 @@ class MinerConfig:
for group in data["pool_groups"]: for group in data["pool_groups"]:
pool_groups.append(_PoolGroup().from_dict(group)) pool_groups.append(_PoolGroup().from_dict(group))
for key in data: for key in data:
if hasattr(self, key) and not key == "pool_groups": if (
hasattr(self, key)
and not key == "pool_groups"
and not key == "miner_mode"
):
setattr(self, key, data[key]) setattr(self, key, data[key])
if key == "miner_mode":
self.miner_mode = X19PowerMode(data[key])
self.pool_groups = pool_groups self.pool_groups = pool_groups
return self return self

View File

@@ -19,8 +19,6 @@ import logging
from collections import namedtuple from collections import namedtuple
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
import asyncssh
from pyasic.API.cgminer import CGMinerAPI from pyasic.API.cgminer import CGMinerAPI
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
@@ -40,64 +38,6 @@ class CGMiner(BaseMiner):
self.pwd = "admin" self.pwd = "admin"
self.config = None self.config = None
async def send_ssh_command(self, cmd: str) -> Optional[str]:
result = None
try:
conn = await self._get_ssh_connection()
except (asyncssh.Error, OSError):
return None
# open an ssh connection
async with conn:
# 3 retries
for i in range(3):
try:
# run the command and get the result
result = await conn.run(cmd)
result = result.stdout
except Exception as e:
# if the command fails, log it
logging.warning(f"{self} command {cmd} error: {e}")
# on the 3rd retry, return None
if i == 3:
return
continue
# return the result, either command output or None
return result
async def restart_backend(self) -> bool:
"""Restart cgminer hashing process. Wraps [`restart_cgminer`][pyasic.miners._backends.cgminer.CGMiner.restart_cgminer] to standardize."""
return await self.restart_cgminer()
async def restart_cgminer(self) -> bool:
"""Restart cgminer hashing process."""
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
commands = ";".join(commands)
try:
_ret = await self.send_ssh_command(commands)
except (asyncssh.Error, OSError):
return False
else:
if isinstance(_ret, str):
return True
return False
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.")
try:
_ret = await self.send_ssh_command("reboot")
except (asyncssh.Error, OSError):
return False
else:
logging.debug(f"{self}: Reboot command completed.")
if isinstance(_ret, str):
return True
return False
async def resume_mining(self) -> bool: async def resume_mining(self) -> bool:
return False return False
@@ -199,15 +139,6 @@ class CGMiner(BaseMiner):
return self.fw_ver return self.fw_ver
async def get_hostname(self) -> Optional[str]:
try:
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
except (asyncssh.Error, OSError):
return None
if hn:
self.hostname = hn
return self.hostname
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
if not api_summary: if not api_summary:

View File

@@ -37,16 +37,16 @@ class CGMinerA10X(CGMiner, A10X):
async def fault_light_off(self) -> bool: async def fault_light_off(self) -> bool:
return False return False
async def get_config(self, api_pools: dict = None) -> MinerConfig: async def get_config(self, web_pools: dict = None) -> MinerConfig:
if not api_pools: if not web_pools:
try: try:
api_pools = await self.api.pools() web_pools = await self.web.pools()
except APIError as e: except APIError as e:
logging.warning(e) logging.warning(e)
if api_pools: if web_pools:
if "POOLS" in api_pools.keys(): if "pools" in web_pools.keys():
cfg = MinerConfig().from_api(api_pools["POOLS"]) cfg = MinerConfig().from_raw(web_pools)
self.config = cfg self.config = cfg
return self.config return self.config
@@ -69,6 +69,12 @@ class CGMinerA10X(CGMiner, A10X):
async def restart_backend(self) -> bool: async def restart_backend(self) -> bool:
return await self.restart_cgminer() return await self.restart_cgminer()
async def stop_mining(self) -> bool:
return False
async def resume_mining(self) -> bool:
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config self.config = config
await self.web.update_pools(config.as_inno(user_suffix=user_suffix)) await self.web.update_pools(config.as_inno(user_suffix=user_suffix))

View File

@@ -33,13 +33,14 @@ class Goldshell(BFGMiner):
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
pools_data = await self.web.pools() pools_data = await self.web.pools()
# have to delete all the pools one at a time first # have to delete all the pools one at a time first
for pool in pools_data: if pools_data:
await self.web.delpool( for pool in pools_data:
url=pool["url"], await self.web.delpool(
user=pool["user"], url=pool["url"],
password=pool["pass"], user=pool["user"],
dragid=pool["dragid"], password=pool["pass"],
) dragid=pool["dragid"],
)
self.config = config self.config = config

View File

@@ -33,13 +33,14 @@ class Goldshell(BFGMiner):
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
pools_data = await self.web.pools() pools_data = await self.web.pools()
# have to delete all the pools one at a time first # have to delete all the pools one at a time first
for pool in pools_data: if pools_data:
await self.web.delpool( for pool in pools_data:
url=pool["url"], await self.web.delpool(
user=pool["user"], url=pool["url"],
password=pool["pass"], user=pool["user"],
dragid=pool["dragid"], password=pool["pass"],
) dragid=pool["dragid"],
)
self.config = config self.config = config

View File

@@ -160,16 +160,19 @@ class X7(BMMiner):
return False return False
async def stop_mining(self) -> bool: async def stop_mining(self) -> bool:
cfg = await self.get_config() return False
cfg.miner_mode = X19PowerMode.Sleep # maybe in a later update
await self.send_config(cfg) # cfg = await self.get_config()
return True # cfg.miner_mode = X19PowerMode.Sleep
# await self.send_config(cfg)
# return True
async def resume_mining(self) -> bool: async def resume_mining(self) -> bool:
cfg = await self.get_config() return False
cfg.miner_mode = X19PowerMode.Normal # cfg = await self.get_config()
await self.send_config(cfg) # cfg.miner_mode = X19PowerMode.Normal
return True # await self.send_config(cfg)
# return True
async def get_hostname(self) -> Union[str, None]: async def get_hostname(self) -> Union[str, None]:
try: try:

View File

@@ -949,22 +949,25 @@ class MinerFactory(metaclass=Singleton):
@staticmethod @staticmethod
async def __get_model_from_ssh(ip: ipaddress.ip_address) -> Union[str, None]: async def __get_model_from_ssh(ip: ipaddress.ip_address) -> Union[str, None]:
model = None model = None
async with asyncssh.connect( try:
str(ip), async with asyncssh.connect(
known_hosts=None, str(ip),
username="root", known_hosts=None,
password="admin", username="root",
server_host_key_algs=["ssh-rsa"], password="admin",
) as conn: server_host_key_algs=["ssh-rsa"],
board_name = None ) as conn:
cmd = await conn.run("cat /tmp/sysinfo/board_name") board_name = None
if cmd: cmd = await conn.run("cat /tmp/sysinfo/board_name")
board_name = cmd.stdout.strip() if cmd:
if board_name == "am1-s9": board_name = cmd.stdout.strip()
model = "ANTMINER S9" if board_name == "am1-s9":
if board_name == "am2-s17": model = "ANTMINER S9"
model = "ANTMINER S17" if board_name == "am2-s17":
return model model = "ANTMINER S17"
return model
except ConnectionRefusedError:
return None
@staticmethod @staticmethod
async def __get_model_from_graphql(ip: ipaddress.ip_address) -> Union[str, None]: async def __get_model_from_graphql(ip: ipaddress.ip_address) -> Union[str, None]:

View File

@@ -19,8 +19,6 @@ import logging
from collections import namedtuple from collections import namedtuple
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
import asyncssh
from pyasic.API.cgminer import CGMinerAPI from pyasic.API.cgminer import CGMinerAPI
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
@@ -40,93 +38,20 @@ class CGMiner(BaseMiner):
self.pwd = "admin" self.pwd = "admin"
self.config = None self.config = None
async def send_ssh_command(self, cmd: str) -> Optional[str]:
result = None
try:
conn = await self._get_ssh_connection()
except (asyncssh.Error, OSError):
return None
# open an ssh connection
async with conn:
# 3 retries
for i in range(3):
try:
# run the command and get the result
result = await conn.run(cmd)
result = result.stdout
except Exception as e:
# if the command fails, log it
logging.warning(f"{self} command {cmd} error: {e}")
# on the 3rd retry, return None
if i == 3:
return
continue
# return the result, either command output or None
return result
async def restart_backend(self) -> bool: async def restart_backend(self) -> bool:
"""Restart cgminer hashing process. Wraps [`restart_cgminer`][pyasic.miners._backends.cgminer.CGMiner.restart_cgminer] to standardize."""
return await self.restart_cgminer()
async def restart_cgminer(self) -> bool:
"""Restart cgminer hashing process."""
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
commands = ";".join(commands)
try:
_ret = await self.send_ssh_command(commands)
except (asyncssh.Error, OSError):
return False
else:
if isinstance(_ret, str):
return True
return False return False
async def get_hostname(self, *args, **kwargs) -> Optional[str]:
return None
async def reboot(self) -> bool: async def reboot(self) -> bool:
"""Reboots power to the physical miner.""" return False
logging.debug(f"{self}: Sending reboot command.")
try: async def stop_mining(self) -> bool:
_ret = await self.send_ssh_command("reboot")
except (asyncssh.Error, OSError):
return False
else:
logging.debug(f"{self}: Reboot command completed.")
if isinstance(_ret, str):
return True
return False return False
async def resume_mining(self) -> bool: async def resume_mining(self) -> bool:
try: return False
commands = [
"mkdir -p /etc/tmp/",
'echo "*/3 * * * * /usr/bin/cgminer-monitor" > /etc/tmp/root',
"crontab -u root /etc/tmp/root",
"/usr/bin/cgminer-monitor >/dev/null 2>&1",
]
commands = ";".join(commands)
await self.send_ssh_command(commands)
except (asyncssh.Error, OSError):
return False
else:
return True
async def stop_mining(self) -> bool:
try:
commands = [
"mkdir -p /etc/tmp/",
'echo "" > /etc/tmp/root',
"crontab -u root /etc/tmp/root",
"killall cgminer",
]
commands = ";".join(commands)
await self.send_ssh_command(commands)
except (asyncssh.Error, OSError):
return False
else:
return True
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
api_pools = await self.api.pools() api_pools = await self.api.pools()
@@ -223,15 +148,6 @@ class CGMiner(BaseMiner):
return self.fw_ver return self.fw_ver
async def get_hostname(self) -> Optional[str]:
try:
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
except (asyncssh.Error, OSError):
return None
if hn:
self.hostname = hn
return self.hostname
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
if not api_summary: if not api_summary:

View File

@@ -14,6 +14,7 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import json import json
import urllib.parse
import warnings import warnings
from typing import Union from typing import Union
@@ -61,9 +62,10 @@ class InnosiliconWebAPI(BaseWebAPI):
f"http://{self.ip}/api/{command}", f"http://{self.ip}/api/{command}",
headers={"Authorization": "Bearer " + self.jwt}, headers={"Authorization": "Bearer " + self.jwt},
timeout=5, timeout=5,
data=parameters, json=parameters,
) )
json_data = response.json() json_data = response.json()
print(json_data)
if ( if (
not json_data.get("success") not json_data.get("success")
and "token" in json_data and "token" in json_data
@@ -104,3 +106,6 @@ class InnosiliconWebAPI(BaseWebAPI):
async def get_error_detail(self): async def get_error_detail(self):
return await self.send_command("getErrorDetail") return await self.send_command("getErrorDetail")
async def pools(self):
return await self.send_command("pools")

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.32.0" version = "0.32.1"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH." description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
authors = ["UpstreamData <brett@upstreamdata.ca>"] authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic" repository = "https://github.com/UpstreamData/pyasic"