feature: add so_linger option to settings.

This commit is contained in:
UpstreamData
2023-11-27 09:24:28 -07:00
parent 2b443497ea
commit 3f1183a4f9
10 changed files with 68 additions and 46 deletions

View File

@@ -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}}}"},

View File

@@ -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

View File

@@ -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()

View File

@@ -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"

View File

@@ -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"}

View File

@@ -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(

View File

@@ -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(

View File

@@ -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

View File

@@ -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"

View File

@@ -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__":