feature: add so_linger option to settings.
This commit is contained in:
@@ -13,8 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import enum
|
import enum
|
||||||
import ipaddress
|
import ipaddress
|
||||||
@@ -25,6 +23,7 @@ from typing import AsyncGenerator, Callable, List, Optional, Tuple, Union
|
|||||||
import anyio
|
import anyio
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
from pyasic import settings
|
||||||
from pyasic.logger import logger
|
from pyasic.logger import logger
|
||||||
from pyasic.miners.antminer import *
|
from pyasic.miners.antminer import *
|
||||||
from pyasic.miners.avalonminer import *
|
from pyasic.miners.avalonminer import *
|
||||||
@@ -45,8 +44,6 @@ from pyasic.miners.innosilicon import *
|
|||||||
from pyasic.miners.unknown import UnknownMiner
|
from pyasic.miners.unknown import UnknownMiner
|
||||||
from pyasic.miners.whatsminer import *
|
from pyasic.miners.whatsminer import *
|
||||||
|
|
||||||
from pyasic import settings
|
|
||||||
|
|
||||||
|
|
||||||
class MinerTypes(enum.Enum):
|
class MinerTypes(enum.Enum):
|
||||||
ANTMINER = 0
|
ANTMINER = 0
|
||||||
@@ -479,11 +476,15 @@ class MinerFactory:
|
|||||||
|
|
||||||
async def _get_miner_web(self, ip: str):
|
async def _get_miner_web(self, ip: str):
|
||||||
urls = [f"http://{ip}/", f"https://{ip}/"]
|
urls = [f"http://{ip}/", f"https://{ip}/"]
|
||||||
async with httpx.AsyncClient(verify=False) as session:
|
async with httpx.AsyncClient(
|
||||||
|
transport=settings.transport(verify=False)
|
||||||
|
) as session:
|
||||||
tasks = [asyncio.create_task(self._web_ping(session, url)) for url in urls]
|
tasks = [asyncio.create_task(self._web_ping(session, url)) for url in urls]
|
||||||
|
|
||||||
text, resp = await concurrent_get_first_result(
|
text, resp = await concurrent_get_first_result(
|
||||||
tasks, lambda x: x[0] is not None and self._parse_web_type(x[0], x[1]) is not None
|
tasks,
|
||||||
|
lambda x: x[0] is not None
|
||||||
|
and self._parse_web_type(x[0], x[1]) is not None,
|
||||||
)
|
)
|
||||||
if text is not None:
|
if text is not None:
|
||||||
return self._parse_web_type(text, resp)
|
return self._parse_web_type(text, resp)
|
||||||
@@ -612,7 +613,7 @@ class MinerFactory:
|
|||||||
location: str,
|
location: str,
|
||||||
auth: Optional[httpx.DigestAuth] = None,
|
auth: Optional[httpx.DigestAuth] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as session:
|
async with httpx.AsyncClient(transport=settings.transport()) as session:
|
||||||
try:
|
try:
|
||||||
data = await session.get(
|
data = await session.get(
|
||||||
f"http://{str(ip)}{location}",
|
f"http://{str(ip)}{location}",
|
||||||
@@ -808,7 +809,7 @@ class MinerFactory:
|
|||||||
|
|
||||||
async def get_miner_model_innosilicon(self, ip: str) -> Optional[str]:
|
async def get_miner_model_innosilicon(self, ip: str) -> Optional[str]:
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as session:
|
async with httpx.AsyncClient(transport=settings.transport()) as session:
|
||||||
auth_req = await session.post(
|
auth_req = await session.post(
|
||||||
f"http://{ip}/api/auth",
|
f"http://{ip}/api/auth",
|
||||||
data={"username": "admin", "password": "admin"},
|
data={"username": "admin", "password": "admin"},
|
||||||
@@ -838,7 +839,7 @@ class MinerFactory:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as session:
|
async with httpx.AsyncClient(transport=settings.transport()) as session:
|
||||||
d = await session.post(
|
d = await session.post(
|
||||||
f"http://{ip}/graphql",
|
f"http://{ip}/graphql",
|
||||||
json={"query": "{bosminer {info{modelName}}}"},
|
json={"query": "{bosminer {info{modelName}}}"},
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class MinerNetwork:
|
|||||||
"""
|
"""
|
||||||
hosts = []
|
hosts = []
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
hosts = [*hosts, *cls.from_address(address)]
|
hosts = [*hosts, *cls.from_address(address).hosts]
|
||||||
return cls(sorted(list(set(hosts))))
|
return cls(sorted(list(set(hosts))))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -63,7 +63,9 @@ class MinerNetwork:
|
|||||||
return cls.from_octets(*octets)
|
return cls.from_octets(*octets)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_octets(cls, oct_1: str, oct_2: str, oct_3: str, oct_4: str) -> "MinerNetwork":
|
def from_octets(
|
||||||
|
cls, oct_1: str, oct_2: str, oct_3: str, oct_4: str
|
||||||
|
) -> "MinerNetwork":
|
||||||
"""Parse 4 octet constructors into a MinerNetwork.
|
"""Parse 4 octet constructors into a MinerNetwork.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -167,7 +169,9 @@ class MinerNetwork:
|
|||||||
try:
|
try:
|
||||||
return await ping_and_get_miner(ip)
|
return await ping_and_get_miner(ip)
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
tasks = [ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]]
|
tasks = [
|
||||||
|
ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]
|
||||||
|
]
|
||||||
for miner in asyncio.as_completed(tasks):
|
for miner in asyncio.as_completed(tasks):
|
||||||
try:
|
try:
|
||||||
return await miner
|
return await miner
|
||||||
|
|||||||
@@ -13,10 +13,13 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
import socket
|
||||||
from typing import Any
|
import struct
|
||||||
|
from ssl import SSLContext
|
||||||
|
from typing import Any, Union
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
from httpx import AsyncHTTPTransport
|
||||||
|
|
||||||
_settings = { # defaults
|
_settings = { # defaults
|
||||||
"network_ping_retries": 1,
|
"network_ping_retries": 1,
|
||||||
@@ -32,14 +35,25 @@ _settings = { # defaults
|
|||||||
"default_bosminer_password": "root",
|
"default_bosminer_password": "root",
|
||||||
"default_vnish_password": "admin",
|
"default_vnish_password": "admin",
|
||||||
"default_goldshell_password": "123456789",
|
"default_goldshell_password": "123456789",
|
||||||
|
"so_linger_time": 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssl_cxt = httpx.create_ssl_context()
|
||||||
|
|
||||||
|
|
||||||
|
def transport(verify: Union[str, bool, SSLContext] = ssl_cxt):
|
||||||
|
l_onoff = 1
|
||||||
|
l_linger = get("so_linger_time", 1000)
|
||||||
|
|
||||||
|
opts = [(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", l_onoff, l_linger))]
|
||||||
|
|
||||||
|
return AsyncHTTPTransport(socket_options=opts, verify=verify)
|
||||||
|
|
||||||
|
|
||||||
def get(key: str, other: Any = None) -> Any:
|
def get(key: str, other: Any = None) -> Any:
|
||||||
return _settings.get(key, other)
|
return _settings.get(key, other)
|
||||||
|
|
||||||
|
|
||||||
def update(key: str, val: Any) -> Any:
|
def update(key: str, val: Any) -> Any:
|
||||||
_settings[key] = val
|
_settings[key] = val
|
||||||
|
|
||||||
ssl_cxt = httpx.create_ssl_context()
|
|
||||||
@@ -38,10 +38,13 @@ class AntminerModernWebAPI(BaseWebAPI):
|
|||||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
if parameters:
|
if parameters:
|
||||||
data = await client.post(
|
data = await client.post(
|
||||||
url, data=json.dumps(parameters), auth=auth, timeout=settings.get("api_function_timeout", 3) # noqa
|
url,
|
||||||
|
data=json.dumps(parameters),
|
||||||
|
auth=auth,
|
||||||
|
timeout=settings.get("api_function_timeout", 3), # noqa
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
data = await client.get(url, auth=auth)
|
data = await client.get(url, auth=auth)
|
||||||
@@ -57,7 +60,7 @@ class AntminerModernWebAPI(BaseWebAPI):
|
|||||||
async def multicommand(
|
async def multicommand(
|
||||||
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||||
) -> dict:
|
) -> dict:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
tasks = [
|
tasks = [
|
||||||
asyncio.create_task(self._handle_multicommand(client, command))
|
asyncio.create_task(self._handle_multicommand(client, command))
|
||||||
for command in commands
|
for command in commands
|
||||||
@@ -149,10 +152,13 @@ class AntminerOldWebAPI(BaseWebAPI):
|
|||||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
if parameters:
|
if parameters:
|
||||||
data = await client.post(
|
data = await client.post(
|
||||||
url, data=parameters, auth=auth, timeout=settings.get("api_function_timeout", 3)
|
url,
|
||||||
|
data=parameters,
|
||||||
|
auth=auth,
|
||||||
|
timeout=settings.get("api_function_timeout", 3),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
data = await client.get(url, auth=auth)
|
data = await client.get(url, auth=auth)
|
||||||
@@ -170,7 +176,7 @@ class AntminerOldWebAPI(BaseWebAPI):
|
|||||||
) -> dict:
|
) -> dict:
|
||||||
data = {k: None for k in commands}
|
data = {k: None for k in commands}
|
||||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
for command in commands:
|
for command in commands:
|
||||||
try:
|
try:
|
||||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ class BOSMinerGQLAPI:
|
|||||||
if command.get("query") is None:
|
if command.get("query") is None:
|
||||||
query = {"query": self.parse_command(command)}
|
query = {"query": self.parse_command(command)}
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
await self.auth(client)
|
await self.auth(client)
|
||||||
data = await client.post(url, json=query)
|
data = await client.post(url, json=query)
|
||||||
except httpx.HTTPError:
|
except httpx.HTTPError:
|
||||||
@@ -239,7 +239,7 @@ class BOSMinerLuCIAPI:
|
|||||||
|
|
||||||
async def send_command(self, path: str, ignore_errors: bool = False) -> dict:
|
async def send_command(self, path: str, ignore_errors: bool = False) -> dict:
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
await self.auth(client)
|
await self.auth(client)
|
||||||
data = await client.get(
|
data = await client.get(
|
||||||
f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"}
|
f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class GoldshellWebAPI(BaseWebAPI):
|
|||||||
self.jwt = None
|
self.jwt = None
|
||||||
|
|
||||||
async def auth(self):
|
async def auth(self):
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
try:
|
try:
|
||||||
await client.get(f"http://{self.ip}/user/logout")
|
await client.get(f"http://{self.ip}/user/logout")
|
||||||
auth = (
|
auth = (
|
||||||
@@ -71,7 +71,7 @@ class GoldshellWebAPI(BaseWebAPI):
|
|||||||
parameters.pop("pool_pwd")
|
parameters.pop("pool_pwd")
|
||||||
if not self.jwt:
|
if not self.jwt:
|
||||||
await self.auth()
|
await self.auth()
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
for i in range(settings.get("get_data_retries", 1)):
|
for i in range(settings.get("get_data_retries", 1)):
|
||||||
try:
|
try:
|
||||||
if parameters:
|
if parameters:
|
||||||
@@ -102,7 +102,7 @@ class GoldshellWebAPI(BaseWebAPI):
|
|||||||
data = {k: None for k in commands}
|
data = {k: None for k in commands}
|
||||||
data["multicommand"] = True
|
data["multicommand"] = True
|
||||||
await self.auth()
|
await self.auth()
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
for command in commands:
|
for command in commands:
|
||||||
try:
|
try:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
|||||||
self.jwt = None
|
self.jwt = None
|
||||||
|
|
||||||
async def auth(self):
|
async def auth(self):
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
try:
|
try:
|
||||||
auth = await client.post(
|
auth = await client.post(
|
||||||
f"http://{self.ip}/api/auth",
|
f"http://{self.ip}/api/auth",
|
||||||
@@ -54,7 +54,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
|||||||
) -> dict:
|
) -> dict:
|
||||||
if not self.jwt:
|
if not self.jwt:
|
||||||
await self.auth()
|
await self.auth()
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
for i in range(settings.get("get_data_retries", 1)):
|
for i in range(settings.get("get_data_retries", 1)):
|
||||||
try:
|
try:
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
@@ -90,7 +90,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
|||||||
data = {k: None for k in commands}
|
data = {k: None for k in commands}
|
||||||
data["multicommand"] = True
|
data["multicommand"] = True
|
||||||
await self.auth()
|
await self.auth()
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
for command in commands:
|
for command in commands:
|
||||||
try:
|
try:
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class VNishWebAPI(BaseWebAPI):
|
|||||||
self.token = None
|
self.token = None
|
||||||
|
|
||||||
async def auth(self):
|
async def auth(self):
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
try:
|
try:
|
||||||
auth = await client.post(
|
auth = await client.post(
|
||||||
f"http://{self.ip}/api/v1/unlock",
|
f"http://{self.ip}/api/v1/unlock",
|
||||||
@@ -58,7 +58,7 @@ class VNishWebAPI(BaseWebAPI):
|
|||||||
) -> dict:
|
) -> dict:
|
||||||
if not self.token:
|
if not self.token:
|
||||||
await self.auth()
|
await self.auth()
|
||||||
async with httpx.AsyncClient(verify=settings.ssl_cxt) as client:
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
for i in range(settings.get("get_data_retries", 1)):
|
for i in range(settings.get("get_data_retries", 1)):
|
||||||
try:
|
try:
|
||||||
auth = self.token
|
auth = self.token
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ readme = "README.md"
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.8"
|
python = "^3.8"
|
||||||
httpx = "^0.25.0"
|
httpx = "^0.25.2"
|
||||||
asyncssh = "^2.14.1"
|
asyncssh = "^2.14.1"
|
||||||
grpc-requests = "^0.1.11"
|
grpc-requests = "^0.1.12"
|
||||||
passlib = "^1.7.4"
|
passlib = "^1.7.4"
|
||||||
pyaml = "^23.9.7"
|
pyaml = "^23.9.7"
|
||||||
toml = "^0.10.2"
|
toml = "^0.10.2"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from pyasic.network import MinerNetwork
|
|||||||
|
|
||||||
class NetworkTest(unittest.TestCase):
|
class NetworkTest(unittest.TestCase):
|
||||||
def test_net_range(self):
|
def test_net_range(self):
|
||||||
net_range_str = "192.168.1.29, 192.168.1.40-192.168.1.43, 192.168.1.60"
|
net_range_str = ["192.168.1.29", "192.168.1.40-43", "192.168.1.60"]
|
||||||
net_range_list = [
|
net_range_list = [
|
||||||
"192.168.1.29",
|
"192.168.1.29",
|
||||||
"192.168.1.40",
|
"192.168.1.40",
|
||||||
@@ -32,8 +32,8 @@ class NetworkTest(unittest.TestCase):
|
|||||||
"192.168.1.60",
|
"192.168.1.60",
|
||||||
]
|
]
|
||||||
|
|
||||||
net_1 = list(MinerNetwork(net_range_str).get_network().hosts())
|
net_1 = list(MinerNetwork.from_list(net_range_list).hosts)
|
||||||
net_2 = list(MinerNetwork(net_range_list).get_network().hosts())
|
net_2 = list(MinerNetwork.from_list(net_range_str).hosts)
|
||||||
|
|
||||||
correct_net = [
|
correct_net = [
|
||||||
ipaddress.IPv4Address("192.168.1.29"),
|
ipaddress.IPv4Address("192.168.1.29"),
|
||||||
@@ -51,11 +51,9 @@ class NetworkTest(unittest.TestCase):
|
|||||||
net_1_str = "192.168.1.0"
|
net_1_str = "192.168.1.0"
|
||||||
net_1_mask = "/29"
|
net_1_mask = "/29"
|
||||||
|
|
||||||
net_1 = list(MinerNetwork(net_1_str, mask=net_1_mask).get_network().hosts())
|
net_1 = list(MinerNetwork.from_subnet(net_1_str + net_1_mask).hosts)
|
||||||
|
|
||||||
net_2 = list(
|
net_2 = list(MinerNetwork.from_list(["192.168.1.1-5", "192.168.1.6"]).hosts)
|
||||||
MinerNetwork("192.168.1.1-192.168.1.5, 192.168.1.6").get_network().hosts()
|
|
||||||
)
|
|
||||||
|
|
||||||
correct_net = [
|
correct_net = [
|
||||||
ipaddress.IPv4Address("192.168.1.1"),
|
ipaddress.IPv4Address("192.168.1.1"),
|
||||||
@@ -70,11 +68,10 @@ class NetworkTest(unittest.TestCase):
|
|||||||
self.assertTrue(net_2 == correct_net)
|
self.assertTrue(net_2 == correct_net)
|
||||||
|
|
||||||
def test_net_defaults(self):
|
def test_net_defaults(self):
|
||||||
net = MinerNetwork()
|
net = MinerNetwork.from_subnet("192.168.1.1/24")
|
||||||
net_obj = net.get_network()
|
self.assertEqual(
|
||||||
self.assertEqual(net_obj, MinerNetwork("192.168.1.0", mask=24).get_network())
|
net.hosts, list(ipaddress.ip_network("192.168.1.0/24").hosts())
|
||||||
|
)
|
||||||
self.assertEqual(net_obj, net.get_network())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user