NVIDIA VPS 开机脚本

背景 第一批申请的额度,一直无法开机,世界第一公司居然这么垃圾… 描述 自动开机脚本,成功自动停止,防频繁触发500 使用方式 浏览器console或dev tool代码块或油猴自己扩展 注意 替换fallbackSimulationId参数为自己的容器id window.__simStateLoo...
NVIDIA VPS 开机脚本
NVIDIA VPS 开机脚本

背景

第一批申请的额度,一直无法开机,世界第一公司居然这么垃圾…

描述

自动开机脚本,成功自动停止,防频繁触发500

使用方式

浏览器console或dev tool代码块或油猴自己扩展

注意

替换fallbackSimulationId参数为自己的容器id

window.__simStateLoop?.stop?.();

window.__simStateLoop = (() => {
  const fallbackSimulationId = "xxx-xxx-xxx-xxx";
  const simulationId =
    window.location.pathname.match(/\/(?:simulation|simulations)\/([0-9a-f-]+)/i)?.[1] ??
    fallbackSimulationId;
  const referrer = window.location.href;
  const apiPrefix = "/api/v1";
  const defaultRetryMs = 60000;
  const serverErrorRetryMs = 30000;
  const capacityRetryMs = 60000;
  const successRetryMs = 15000;
  const loadedStates = new Set(["LOADING", "LOADED", "RUNNING", "STARTING"]);
  const requestCandidates = [
    {
      name: "v1.control.load",
      method: "POST",
      url: new URL(
        `${apiPrefix}/simulation/${simulationId}/control/`,
        window.location.origin
      ).toString(),
      body: JSON.stringify({ action: "load" })
    },
    {
      name: "v1.autoprovision",
      method: "POST",
      url: new URL(
        `${apiPrefix}/simulation/autoprovision/?simulation_id=${encodeURIComponent(
          simulationId
        )}`,
        window.location.origin
      ).toString(),
      body: JSON.stringify({ include_services: true })
    },
    {
      name: "v1.control.load.noTrailingSlash",
      method: "POST",
      url: new URL(
        `${apiPrefix}/simulation/${simulationId}/control`,
        window.location.origin
      ).toString(),
      body: JSON.stringify({ action: "load" })
    },
    {
      name: "v2.simulation.get",
      method: "GET",
      url: new URL(`${apiPrefix}/v2/simulations/${simulationId}/`, window.location.origin).toString(),
      body: undefined
    },
    {
      name: "v2.jobs.list",
      method: "GET",
      url: new URL(
        `${apiPrefix}/v2/jobs/?simulation_id=${encodeURIComponent(simulationId)}`,
        window.location.origin
      ).toString(),
      body: undefined
    },
    {
      name: "v2.nodes.list",
      method: "GET",
      url: new URL(
        `${apiPrefix}/v2/simulations/?id=${encodeURIComponent(simulationId)}`,
        window.location.origin
      ).toString(),
      body: undefined
    }
  ];
  let stopped = false;
  let preferredCandidateIndex = 0;
  let lastObservedState = null;

  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  const parseResponse = (text) => {
    try {
      return JSON.parse(text);
    } catch {
      return text;
    }
  };
  const formatLogValue = (value) =>
    typeof value === "string" ? value.slice(0, 500) : value;
  const withJitter = (ms) => ms + Math.floor(Math.random() * 5000);
  const logHeaders = (headers) => {
    const entries = {};

    for (const [key, value] of headers.entries()) {
      entries[key] = value;
    }

    return entries;
  };
  const stopLoop = (reason) => {
    stopped = true;
    console.log(`simStateLoop stopped: ${reason}`);
  };
  const extractState = (candidateName, data) => {
    if (!data || typeof data !== "object") {
      return null;
    }

    if (typeof data.state === "string") {
      return data.state;
    }

    if (
      candidateName === "v2.simulation.get" &&
      typeof data.state === "string"
    ) {
      return data.state;
    }

    if (
      candidateName === "v2.nodes.list" &&
      Array.isArray(data.results) &&
      data.results.length > 0 &&
      typeof data.results[0]?.state === "string"
    ) {
      return data.results[0].state;
    }

    return null;
  };

  const run = async () => {
    while (!stopped) {
      let waitMs = defaultRetryMs;

      try {
        const candidateOrder = requestCandidates.map((candidate, index) => ({
          candidate,
          index
        }));

        candidateOrder.sort((left, right) => {
          if (left.index === preferredCandidateIndex) {
            return -1;
          }

          if (right.index === preferredCandidateIndex) {
            return 1;
          }

          return left.index - right.index;
        });

        let matchedCandidate = false;

        for (const { candidate, index } of candidateOrder) {
          const requestInit = {
            method: candidate.method,
            credentials: "include",
            referrer,
            headers: {
              accept: "application/json, text/plain, */*"
            }
          };

          if (candidate.body !== undefined) {
            requestInit.headers["content-type"] = "application/json;charset=UTF-8";
            requestInit.body = candidate.body;
          }

          const response = await fetch(candidate.url, requestInit);

          const text = await response.text();
          const data = parseResponse(text);
          const prefix = `[${new Date().toLocaleTimeString()}] ${candidate.name}`;

          console.log(`${prefix} status:`, response.status, response.statusText);
          console.log(`${prefix} headers:`, logHeaders(response.headers));

          if (data && typeof data === "object") {
            console.log(`${prefix} response:`, data);
          } else {
            console.log(`${prefix} response:`, formatLogValue(data));
          }

          const state = extractState(candidate.name, data);

          if (state && state !== lastObservedState) {
            lastObservedState = state;
            console.log(`${prefix} observed state:`, state);
          }

          if (state && loadedStates.has(state)) {
            stopLoop(`simulation state is ${state}`);
            matchedCandidate = true;
            break;
          }

          if (response.ok) {
            preferredCandidateIndex = index;
            matchedCandidate = true;
            waitMs = successRetryMs;
            console.log(`${prefix} next retry in ${Math.round(waitMs / 1000)}s`);
            break;
          }

          if (
            response.status === 400 &&
            data &&
            typeof data === "object" &&
            typeof data.message === "string" &&
            data.message.includes("out of capacity")
          ) {
            preferredCandidateIndex = index;
            matchedCandidate = true;
            waitMs = capacityRetryMs;
            console.warn(
              `${prefix} capacity limited, retry in ${Math.round(waitMs / 1000)}s`
            );
            break;
          }

          if (response.status >= 500) {
            preferredCandidateIndex = index;
            matchedCandidate = true;
            waitMs = serverErrorRetryMs;
            console.warn(
              `${prefix} server error, retry in ${Math.round(waitMs / 1000)}s`
            );
            break;
          }

          if (response.status === 404 || response.status === 405) {
            console.warn(
              `${prefix} endpoint unavailable:`,
              response.status,
              response.statusText
            );
            continue;
          }

          preferredCandidateIndex = index;
          matchedCandidate = true;
          console.error(
            `${prefix} HTTP error:`,
            response.status,
            response.statusText,
            data
          );
          break;
        }

        if (!matchedCandidate) {
          console.error("No compatible simulation start endpoint was found.");
        }
      } catch (error) {
        console.error("request failed:", error);
        waitMs = serverErrorRetryMs;
      }

      const finalWaitMs = withJitter(waitMs);
      if (!stopped) {
        console.log(`next loop in ${Math.round(finalWaitMs / 1000)}s`);
        await sleep(finalWaitMs);
      }
    }
  };

  run();

  return {
    stop() {
      stopLoop("manual stop");
    }
  };
})();

备注: 如有问题,请及时反馈,目前自测无问题

1 个帖子 - 1 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文