Edits history of script submission #24161 for ' List image bundles (arista_cloudvision)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //native
    
    /**
     * List image bundles
     * List the EOS image bundles (legacy Network Provisioning API) with their key, name and images. CloudVision pages them by index: end_index 0 returns all of them.
     */
    export async function main(
      auth: RT.AristaCloudvision,
      start_index: number = 0,
      end_index: number = 0
    ) {
      return await cv(
        auth,
        `/cvpservice/image/getImageBundles.do?queryparam=&startIndex=${start_index ?? 0}&endIndex=${end_index ?? 0}`
      )
    }
    
    // Calls CloudVision and turns its failures into readable errors. Legacy
    // endpoints report errors as HTTP 200 with an errorCode field.
    async function cv(
      auth: RT.AristaCloudvision,
      path: string,
      options: { body?: unknown; notFound?: string; optional?: boolean } = {}
    ): Promise<any> {
      const url = new URL(path, auth.base_url)
      // cvprac sends legacy endpoints to /cvpservice on CVaaS and /web on
      // on-premises CVP. Its is_cvaas flag is the resource's deployment field;
      // when that is unset, a host under arista.io is taken as CVaaS.
      const cvaas = auth.deployment
        ? auth.deployment === "cvaas"
        : /(^|\.)arista\.io\.?$/i.test(url.hostname)
      if (!cvaas && url.pathname.startsWith("/cvpservice/")) {
        url.pathname = url.pathname.replace(/^\/cvpservice\//, "/web/")
      }
      const response = await fetch(url, {
        method: options.body === undefined ? "GET" : "POST",
        headers: {
          Authorization: `Bearer ${auth.token}`,
          ...(options.body === undefined
            ? {}
            : { "Content-Type": "application/json" }),
          Accept: "application/json",
        },
        body: options.body === undefined ? undefined : JSON.stringify(options.body),
      })
      const text = await response.text()
      if (
        response.status === 401 ||
        response.status === 403 ||
        (!response.ok && text.includes("User is unauthorized"))
      ) {
        throw new Error(
          `CloudVision refused the token (HTTP ${response.status}): check that the service-account token is valid, not expired, and that its role allows this call. ${text}`
        )
      }
      const data =
        text.trim() === ""
          ? {}
          : /^\s*[[{]/.test(text)
            ? JSON.parse(text)
            : undefined
      const error = findError(data)
      if (response.ok && data !== undefined && !error) return data
      const detail = error
        ? `${error.errorCode} ${error.errorMessage ?? (error.errors ?? []).join("; ")}`
        : `HTTP ${response.status} ${text.slice(0, 500)}`
      // Legacy lookups answer an unknown name or ID with an errorCode (cvprac
      // treats any error from getTaskById and getImageBundleByName that way).
      if (
        response.status === 404 ||
        (error && (options.optional || options.notFound))
      ) {
        if (options.optional) return undefined
        if (options.notFound) throw new Error(`${options.notFound} (${detail})`)
      }
      if (response.ok && !error) {
        throw new Error(
          `CloudVision returned a non-JSON response for ${url.pathname}: check that base_url is the CloudVision address. ${detail}`
        )
      }
      throw new Error(`CloudVision ${url.pathname} failed: ${detail}`)
    }
    
    function findError(value: any): any {
      if (!value || typeof value !== "object") return undefined
      // Like cvprac, only a truthy errorCode counts, at any depth.
      if (!Array.isArray(value) && value.errorCode) return value
      for (const item of Object.values(value)) {
        const error = findError(item)
        if (error) return error
      }
      return undefined
    }
    

    Submitted by hugo989 5 hours ago