From cb57ed07de66babbdd0d91edea75f329eb682c08 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 20 Feb 2024 15:27:30 +0300 Subject: [PATCH] refactor vscale output provider reuse client with headers, base_url enable http2 --- pddnsc/outputs/vscale.py | 177 ++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 96 deletions(-) diff --git a/pddnsc/outputs/vscale.py b/pddnsc/outputs/vscale.py index 7b142c8..ed1acb7 100644 --- a/pddnsc/outputs/vscale.py +++ b/pddnsc/outputs/vscale.py @@ -11,111 +11,85 @@ class VscaleDomains(BaseOutputProvider): self.__token = self.get_api_token() if not self.__token: raise KeyError("no api token, use env VSCALE_API_TOKEN") + target = self.config["target"] + self.domain = self.config["domain"] + self.target = f"{target}.{self.domain}" + self.ttl = self.config.get("ttl", 300) def get_api_token(self) -> str: token_env = self.config.get("api_token_env", "VSCALE_API_TOKEN") return os.environ[token_env] - async def find_domain_id(self, transport) -> Optional[int]: - domain = self.config["domain"] - headers = {"X-Token": self.__token} - async with httpx.AsyncClient(transport=transport) as client: - response = await client.get( - "https://api.vscale.io/v1/domains/", headers=headers - ) - if httpx.codes.is_success(response.status_code): - data = response.json() - if isinstance(data, list): - for entry in data: - if entry["name"] == domain: - return entry["id"] - else: - raise TypeError( - "failed to find domain id, unexpected response type" - ) + async def find_domain_id(self, client) -> Optional[int]: + response = await client.get("/domains/") + if httpx.codes.is_success(response.status_code): + data = response.json() + if isinstance(data, list): + for entry in data: + if entry["name"] == self.domain: + return entry["id"] else: - raise ValueError( - f"failed to find domain id, code: {response.status_code}" - ) + raise TypeError("failed to find domain id, unexpected response type") + else: + raise ValueError(f"failed to find domain id, code: {response.status_code}") - async def find_record(self, transport, domain_id, record_type) -> Optional[int]: - target = self.config["target"] - domain = self.config["domain"] - target = f"{target}.{domain}" - headers = {"X-Token": self.__token} - async with httpx.AsyncClient(transport=transport) as client: - response = await client.get( - f"https://api.vscale.io/v1/domains/{domain_id}/records/", - headers=headers, + async def find_record(self, client, domain_id, record_type) -> Optional[int]: + response = await client.get( + f"/domains/{domain_id}/records/", + ) + if httpx.codes.is_success(response.status_code): + data = response.json() + if isinstance(data, list): + for entry in data: + if entry["name"] == self.target and entry["type"] == record_type: + return entry["id"] + else: + raise RuntimeError( + f"error list records {domain_id=}: ", response.status_code ) - if httpx.codes.is_success(response.status_code): - data = response.json() - if isinstance(data, list): - for entry in data: - if entry["name"] == target and entry["type"] == record_type: - return entry["id"] - else: - raise RuntimeError( - f"error list records {domain_id=}: ", response.status_code - ) - async def get_record_value(self, transport, domain_id, record_id) -> str: - headers = {"X-Token": self.__token} - async with httpx.AsyncClient(transport=transport) as client: - response = await client.get( - f"https://api.vscale.io/v1/domains/{domain_id}/records/{record_id}", - headers=headers, - ) - if httpx.codes.is_success(response.status_code): - data = response.json() - if isinstance(data, dict): - return data["content"] + async def get_record_value(self, client, domain_id, record_id) -> str: + response = await client.get(f"/domains/{domain_id}/records/{record_id}") + if httpx.codes.is_success(response.status_code): + data = response.json() + if isinstance(data, dict): + return data["content"] - async def change_record(self, transport, domain_id, record_id, record_type, value): - target = self.config["target"] - domain = self.config["domain"] - target = f"{target}.{domain}" - ttl = self.config.get("ttl", 300) - headers = {"X-Token": self.__token, "Content-Type": "application/json"} + async def change_record(self, client, domain_id, record_id, record_type, value): data = { "content": value, - "name": target, - "ttl": ttl, + "name": self.target, + "ttl": self.ttl, "type": record_type, "id": record_id, } - async with httpx.AsyncClient(transport=transport) as client: - response = await client.put( - f"https://api.vscale.io/v1/domains/{domain_id}/records/{record_id}", - headers=headers, - json=data, - ) - if not httpx.codes.is_success(response.status_code): - raise RuntimeError( - f"failed to change record: {target=},{domain_id=}, {record_id=}, {record_type=}, {value=}" - ) - async def create_record(self, transport, domain_id, record_type, value): - target = self.config["target"] - domain = self.config["domain"] - target = f"{target}.{domain}" - ttl = self.config.get("ttl", 300) - headers = {"X-Token": self.__token, "Content-Type": "application/json"} - data = {"content": value, "name": target, "ttl": ttl, "type": record_type} - async with httpx.AsyncClient(transport=transport) as client: - response = await client.post( - f"https://api.vscale.io/v1/domains/{domain_id}/records/", - headers=headers, - json=data, + response = await client.put( + f"/domains/{domain_id}/records/{record_id}", + json=data, + ) + if not httpx.codes.is_success(response.status_code): + raise RuntimeError( + f"failed to change record: {self.target=},{domain_id=}, {record_id=}, {record_type=}, {value=}" + ) + + async def create_record(self, client, domain_id, record_type, value): + data = { + "content": value, + "name": self.target, + "ttl": self.ttl, + "type": record_type, + } + response = await client.post( + f"/domains/{domain_id}/records/", + json=data, + ) + if not httpx.codes.is_success(response.status_code): + raise RuntimeError( + f"failed to create record: {self.target=},{domain_id=}, {record_type=}, {value=}, {response.status_code=}" ) - if not httpx.codes.is_success(response.status_code): - raise RuntimeError( - f"failed to create record: {target=},{domain_id=}, {record_type=}, {value=}, {response.status_code=}" - ) async def set_addrs_imp(self, source_provider, addr_v4, addr_v6): - transport = self.best_transport(addr_v4, addr_v6) - domain_id = await self.find_domain_id(transport) save_ipv4 = self.config.get("ipv4", False) save_ipv6 = self.config.get("ipv6", False) if "ipv4" not in self.config and "ipv6" not in self.config: @@ -128,15 +102,26 @@ class VscaleDomains(BaseOutputProvider): if addr_v6 and save_ipv6: save_addrs.append(("AAAA", addr_v6)) - for record_type, value in save_addrs: - record_id = await self.find_record(transport, domain_id, record_type) - if record_id: - old_value = await self.get_record_value(transport, domain_id, record_id) - if old_value != value: - await self.change_record( - transport, domain_id, record_id, record_type, value + transport = self.best_transport(addr_v4, addr_v6) + api_base = self.config.get("api_base", "https://api.vscale.io/v1/") + headers = {"X-Token": self.__token} + async with httpx.AsyncClient( + transport=transport, base_url=api_base, headers=headers, http2=True + ) as client: + domain_id = await self.find_domain_id(client) + for record_type, value in save_addrs: + record_id = await self.find_record(client, domain_id, record_type) + if record_id: + old_value = await self.get_record_value( + client, domain_id, record_id ) + if old_value != value: + await self.change_record( + client, domain_id, record_id, record_type, value + ) + else: + print( + f"vscale: skip record change ({record_type=}), value equal" + ) else: - print(f"vscale: skip record change ({record_type=}), value equal") - else: - await self.create_record(transport, domain_id, record_type, value) + await self.create_record(client, domain_id, record_type, value)