File: //var/packages/SupportService/var/actions/SupportService/upload-debug-dat/css_server_api.py
import typing
from enum import Enum
import urllib.request
import urllib.parse
import time
import json
import os
from .multipart_data_encoder import encode_multipart_data
UPLOAD_DEBUG_DAT_CONF = "/var/packages/SupportService/target/configs/upload_debug_dat.conf"
class ResultForCssServer(Enum):
SUCCESS = 1
GENERATE_DEBUG_DAT_FAIL = 2
UPLOAD_FAIL = 3
class ApiPsFilesFail(Exception):
pass
class ApiPsResultsFail(Exception):
pass
class CssServerApi(object):
def __init__(self, token: str, serial_number: str) -> None:
self.token = token
self.serial_number = serial_number
css_server_url = self._get_css_server_url()
if css_server_url is None:
raise Exception("failed to get css_server.url in config")
self.css_server_url = css_server_url
def _get_css_server_url(self) -> typing.Optional[str]:
if not os.path.exists(UPLOAD_DEBUG_DAT_CONF):
# backward compatibility for old SupportService without config file
return "https://supapi.synology.com"
with open(UPLOAD_DEBUG_DAT_CONF, "r", encoding="utf-8") as f:
return json.load(f).get("debug_dat_file_transmitter", {}).get("css_server", {}).get("url", None)
def _post(self, url_rule: str, headers: dict, data: bytes, retry_limit: int) -> typing.Tuple[typing.Optional[int], typing.Optional[str]]:
request = urllib.request.Request(urllib.parse.urljoin(self.css_server_url, url_rule), data=data, headers=headers, method="POST")
for _ in range(retry_limit):
try:
response = urllib.request.urlopen(request, timeout=1800)
status = response.status
body = response.read().decode()
return status, body
except Exception:
time.sleep(3)
return None, None
def send_ps_files(self, debug_dat_path: str, app_list: typing.List[str]):
fields = {f"debug_data_apps[{index}]": app for index, app in enumerate(app_list)}
fields["serial_number"] = self.serial_number
files = [("debug_file", debug_dat_path)]
body, content_type = encode_multipart_data(fields, files)
headers = {"AUTH-TOKEN": self.token, "Content-Type": content_type}
if self._post("/active_insight/ps_files", headers, body, retry_limit=5) == (None, None):
raise ApiPsFilesFail
def send_ps_results(self, reason_for_css: ResultForCssServer):
if reason_for_css == ResultForCssServer.SUCCESS:
status = "Success"
elif reason_for_css == ResultForCssServer.GENERATE_DEBUG_DAT_FAIL:
status = "Failed to generate debug file"
elif reason_for_css == ResultForCssServer.UPLOAD_FAIL:
status = "Failed to upload debug file to server"
headers = {"AUTH-TOKEN": self.token, "Content-Type": "application/json"}
data = json.dumps({"status": status, "serial_number": self.serial_number}).encode("utf-8")
if self._post("/active_insight/ps_results", headers, data, retry_limit=5) == (None, None):
raise ApiPsResultsFail