Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
841a546505 |
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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]:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user