diff --git a/.github/scripts/release-build.sh b/.github/scripts/release-build.sh index a0d5379..c9b9ca5 100755 --- a/.github/scripts/release-build.sh +++ b/.github/scripts/release-build.sh @@ -1,13 +1,30 @@ #!/bin/env bash set -e -osc_api_version=${1#v} +service=${1:-all} +osc_api_version=${2#v} +oks_api_url=${3:-https://docs.outscale.com/_attachments/oks.yaml} -if [ -z "$osc_api_version" ]; then - echo "run $0 with version tag as argument, abort." +case "$service" in + osc|oks|all) + ;; + *) + echo "Unknown service '$service'. Expected one of: osc, oks, all." + exit 1 + ;; +esac + +if [ "$service" != "oks" ] && [ -z "$osc_api_version" ]; then + echo "run $0 with an OSC API version when building service '$service', abort." + exit 1 +fi + +if [ "$service" != "osc" ] && [ -z "$oks_api_url" ]; then + echo "run $0 with an OKS OpenAPI URL when building service '$service', abort." exit 1 fi root=$(cd "$(dirname $0)/../.." && pwd) +cd "$root" # build new version number local_sdk_version=$(cat $root/osc_sdk_python/VERSION) @@ -17,14 +34,28 @@ local_sdk_version_patch=$(echo $local_sdk_version | cut -d '.' -f 3) new_sdk_version_minor=$(( local_sdk_version_minor + 1 )) new_sdk_version="$local_sdk_version_major.$new_sdk_version_minor.0" -# Update osc-api version -curl --retry 10 -o "${root}/osc_sdk_python/resources/outscale.yaml" "https://raw.githubusercontent.com/outscale/osc-api/refs/tags/${osc_api_version}/outscale.yaml" -git add "${root}/osc_sdk_python/resources/outscale.yaml" +if [ "$service" = "osc" ] || [ "$service" = "all" ]; then + # Update osc-api version + curl --retry 10 -o "${root}/osc_sdk_python/resources/osc/api.yaml" "https://raw.githubusercontent.com/outscale/osc-api/refs/tags/${osc_api_version}/outscale.yaml" + uv run python -m osc_sdk_python.codegen.generator osc --skip-overlay + git add "${root}/osc_sdk_python/resources/osc" "${root}/osc_sdk_python/generated/osc" +fi + +if [ "$service" = "oks" ] || [ "$service" = "all" ]; then + # Update oks-api version + curl --retry 10 -o "${root}/osc_sdk_python/resources/oks/api.yaml" "${oks_api_url}" + uv run python -m osc_sdk_python.codegen.generator oks --skip-overlay + git add "${root}/osc_sdk_python/resources/oks" "${root}/osc_sdk_python/generated/oks" +fi # Setup new SDK version for f in "$root/README.md" "$root/osc_sdk_python/VERSION"; do sed -i "s/$local_sdk_version_major\.$local_sdk_version_minor\.$local_sdk_version_patch/$local_sdk_version_major\.$new_sdk_version_minor\.0/g" "$f" - git add "$f" done -uv version $(cat osc_sdk_python/VERSION) +uv version "$(cat osc_sdk_python/VERSION)" + +git add "$root/README.md" "$root/osc_sdk_python/VERSION" "$root/pyproject.toml" +if [ -f "$root/uv.lock" ]; then + git add "$root/uv.lock" +fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6c2f33..536ce89 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,9 +3,22 @@ name: osc-sdk-python release build on: workflow_dispatch: inputs: - api_version: - description: 'Outscale API version' + service: + description: 'Service to build' required: true + type: choice + default: all + options: + - osc + - oks + - all + api_version: + description: 'Outscale API version, required for osc or all' + required: false + oks_api_url: + description: 'OKS OpenAPI URL, used for oks or all' + required: false + default: 'https://docs.outscale.com/_attachments/oks.yaml' permissions: contents: write @@ -24,9 +37,11 @@ jobs: - name: Set up Python run: uv python install - name: Build release - run: .github/scripts/release-build.sh "$API_VERSION" + run: .github/scripts/release-build.sh "$SERVICE" "$API_VERSION" "$OKS_API_URL" env: + SERVICE: ${{ github.event.inputs.service }} API_VERSION: ${{ github.event.inputs.api_version }} + OKS_API_URL: ${{ github.event.inputs.oks_api_url }} - name: Get SDK version id: get-sdk-version run: | @@ -36,13 +51,20 @@ jobs: with: committer: "Outscale Bot " author: "Outscale Bot " - commit-message: "🔖 release: osc-sdk-python v${{ env.sdk_version }}" + commit-message: "🔖 release: osc-sdk-python SDK-V2 v${{ env.sdk_version }}" body: | - Automatic build of SDK v${{ env.sdk_version }} version based on Outscale API ${{ env.api_version }}. - title: "SDK v${{ env.sdk_version }}" + Automatic ${{ env.service }} build of SDK-V2 v${{ env.sdk_version }}. + + Outscale API version: ${{ env.api_version }} + OKS API URL: ${{ env.oks_api_url }} + title: "SDK-V2 v${{ env.sdk_version }} (${{ env.service }})" + base: feat/async-typed-sdk + branch: auto-build/async-typed-sdk-${{ env.service }}-${{ env.sdk_version }} token: "${{ env.token }}" labels: "kind/feature" env: sdk_version: ${{ steps.get-sdk-version.outputs.sdk_version }} + service: ${{ github.event.inputs.service }} api_version: ${{ github.event.inputs.api_version }} + oks_api_url: ${{ github.event.inputs.oks_api_url }} token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/MANIFEST.in b/MANIFEST.in index 32f8eaa..22f9f2e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,2 @@ -include osc_sdk_python/osc-api/outscale.yaml -include osc_sdk_python/resources/gateway_errors.yaml +recursive-include osc_sdk_python/resources *.yaml include osc_sdk_python/VERSION diff --git a/README.md b/README.md index 63f11f1..6a4e387 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,10 @@ It allows you to: - Configure multiple profiles through environment variables or credential files. +- Use either the synchronous `Client` or asynchronous `AsyncClient`. - Customize retry and rate-limit behavior. -- Enable detailed logging of requests and responses. +- Use SDK-managed authentication, retry, and rate limiting through httpx. +- Enable detailed request logging. You will need an Outscale account and API credentials. If you do not have one yet, please visit the [Outscale website](https://outscale.com/). @@ -47,7 +49,7 @@ You will need an Outscale account and API credentials. If you do not have one ye ## ✅ Requirements -- Python 3.x +- Python 3.10+ - `pip` (Python package manager) - Access to the OUTSCALE API (valid access key / secret key or basic auth) - Network access to the Outscale API endpoints @@ -77,7 +79,7 @@ make package Then install the built wheel: ```bash -pip install dist/osc_sdk_python-0.41.0-py3-none-any.whl +pip install dist/osc_sdk_python-0.42.0-py3-none-any.whl ``` --- @@ -134,9 +136,12 @@ Example: } ``` -Notes: +Precedence order: -* Environment variables have priority over credentials files. +1. Explicit constructor arguments +2. Environment variables +3. Credentials file +4. SDK defaults ### Basic Authentication @@ -146,29 +151,81 @@ Note that some API calls may be blocked with this method. See the [authenticatio Example: ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client -with Gateway(email="your@email.com", password="yourAccountPassword") as gw: - keys = gw.ReadAccessKeys() +with Client(email="your@email.com", password="yourAccountPassword") as client: + keys = client.osc.ReadAccessKeys() +``` + +### Async Usage + +Use `AsyncClient` when calling the SDK from async Python code: + +```python +import asyncio + +from osc_sdk_python import AsyncClient + + +async def main(): + async with AsyncClient(profile="default") as client: + vms = await client.osc.read_vms() + print(vms) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +Typed request and response models under `osc_sdk_python.generated.*` are async-first: generated typed methods are available on `AsyncClient` with snake_case names such as `await client.osc.read_vms(...)`. Synchronous callers should use dynamic action methods such as `client.osc.ReadVms(...)` or `client.osc.raw("ReadVms", **params)`. + +### Multi-Service Client + +Use `Client` or `AsyncClient` to access multiple services from one SDK object: + +```python +from osc_sdk_python import Client + +with Client(profile="default") as client: + vms = client.osc.ReadVms() + projects = client.oks.ListProjects() +``` + +Async example: + +```python +import asyncio + +from osc_sdk_python import AsyncClient + + +async def main(): + async with AsyncClient(profile="default") as client: + vms = await client.osc.read_vms() + projects = await client.oks.list_projects() + + +if __name__ == "__main__": + asyncio.run(main()) ``` ### Retry Options -The following options can be provided when initializing the `Gateway` to customize the retry behavior of the SDK: +The following options can be provided when initializing the `Client` or `AsyncClient` to customize the retry behavior of the SDK: * `max_retries` (integer, default `3`) * `retry_backoff_factor` (float, default `1.0`) * `retry_backoff_jitter` (float, default `3.0`) * `retry_backoff_max` (float, default `30`) -These correspond to their counterparts in [`urllib3.util.Retry`](https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html#urllib3.util.Retry). +These configure the SDK retry policy used by the sync and async httpx transports. Example: ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client -gw = Gateway( +client = Client( max_retries=5, retry_backoff_factor=0.5, retry_backoff_jitter=1.0, @@ -178,22 +235,62 @@ gw = Gateway( ### Rate Limit Options -You can also configure rate limiting when initializing the `Gateway`: +You can also configure rate limiting when initializing the `Client` or `AsyncClient`: * `limiter_max_requests` (integer, default `5`) -* `limiter_window` (integer, default `1`) +* `limiter_window` (integer seconds, default `1`) Example: ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client -gw = Gateway( +client = Client( limiter_max_requests=20, limiter_window=5, ) ``` +### Error Handling + +Public SDK methods raise SDK-owned exceptions. Catch `SdkError` for any SDK failure, or a specific subclass such as `SdkClientError`, `SdkServerError`, `SdkTransportError`, `SdkValidationError`, `SdkConfigurationError`, or `SdkResponseError`. + +```python +from osc_sdk_python import AsyncClient, SdkError + +async def main(): + try: + async with AsyncClient(profile="default") as client: + print(await client.osc.read_vms()) + except SdkError as err: + print(err) +``` + +### HTTP Transport Behavior + +Authentication, retry, rate limiting, and API error handling are integrated into the SDK httpx layer: + +* `SdkAuth` signs outgoing requests and also supports OKS and basic authentication. +* `SdkTransport` applies sync rate limiting, retries, and SDK error conversion. +* `AsyncSdkTransport` provides the same behavior for `AsyncClient`. + +Most users do not need to instantiate these classes directly. Configure behavior through `Client` or `AsyncClient` options: + +```python +from osc_sdk_python import Client + +with Client( + profile="default", + max_retries=5, + retry_backoff_factor=0.5, + limiter_max_requests=20, + limiter_window=5, +) as client: + vms = client.osc.ReadVms() +``` + +For custom httpx integrations, the transport components are available from `osc_sdk_python.runtime.transport`. + More usage patterns and logging examples are documented in: * [docs/examples.md](docs/examples.md) @@ -205,8 +302,9 @@ More usage patterns and logging examples are documented in: Some example topics covered in `docs/examples.md`: * Listing VMs and volumes +* Async usage with `AsyncClient` * Using profiles and regions -* Raw calls with `gw.raw("ActionName", **params)` +* Raw calls with `client.osc.raw("ActionName", **params)` * Enabling and reading logs --- diff --git a/SDK_Architecture.md b/SDK_Architecture.md new file mode 100644 index 0000000..3162222 --- /dev/null +++ b/SDK_Architecture.md @@ -0,0 +1,601 @@ +# OUTSCALE Python SDK Architecture + +## 1. Purpose + +This document describes the architecture of the OUTSCALE Python SDK V2. + +SDK V2 moves the Python SDK from a single-service gateway-style interface to a generated, typed, multi-service SDK. The SDK is designed to support OSC, OKS, and future OUTSCALE services from one Python package while keeping synchronous OSC usage available for compatibility. + +The main development focus areas are: + +- Make async usage the primary SDK experience. +- Generate typed async methods for service operations. +- Keep synchronous calls supported for compatibility with blocking Python applications. +- Replace the single `Gateway` entry point with `Client` and `AsyncClient` service namespaces. +- Support multiple services such as OSC and OKS from one SDK object. +- Allow more services to be added later without redesigning the SDK. +- Support both OSC action-style OpenAPI and REST/path-style OpenAPI. +- Generate request and response models from OpenAPI using Pydantic. +- Keep service-specific differences inside the generator, overlays, service clients, or generated modules. +- Share runtime behavior for credentials, endpoints, authentication, retries, rate limiting, transport, logging, and errors. +- Avoid manual edits to generated code. + +## 2. Public API + +SDK V2 exposes two main client entry points: + +- `AsyncClient`: primary, typed, async-first API. +- `Client`: synchronous compatibility API for blocking usage. + +Both clients expose service namespaces: + +```text +Client / AsyncClient + |- osc + |- oks + |- future services +``` + +### Async Usage + +Async usage is the main SDK V2 interface. Generated typed methods use `snake_case` names and typed request/response models. + +```python +import asyncio + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ReadVmsRequest + + +async def main(): + async with AsyncClient(profile="default") as client: + response = await client.osc.read_vms(ReadVmsRequest()) + for vm in response.vms: + print(vm.vm_id) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +Async OKS usage follows the same generated typed pattern: + +```python +import asyncio + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.oks import ListProjectsRequest + + +async def main(): + async with AsyncClient(profile="default") as client: + projects = await client.oks.list_projects(ListProjectsRequest()) + print(projects) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +Async clients also keep raw calls where needed: + +```python +from osc_sdk_python import AsyncClient + +async with AsyncClient(profile="default") as client: + response = await client.osc.raw("ReadVms") +``` + +### Sync Usage + +Sync usage is kept for compatibility. Sync service clients expose dynamic OpenAPI operation methods using the existing action-style names. + +```python +from osc_sdk_python import Client + +with Client(profile="default") as client: + vms = client.osc.ReadVms() + projects = client.oks.ListProjects() +``` + +Raw sync calls are also supported: + +```python +from osc_sdk_python import Client + +with Client(profile="default") as client: + response = client.osc.raw("ReadVms") +``` + +### Gateway Compatibility + +The package may keep `Gateway` and `AsyncGateway` aliases for compatibility, but the SDK V2 architecture should use `Client` and `AsyncClient` as the preferred public entry points. `Client` gives the SDK a stable structure for multi-service support, while `Gateway` represents the older single-service OSC shape. + +## 3. High-Level Architecture + +```text +User code + -> Client / AsyncClient + -> service namespace: osc, oks, ... + -> generated typed method or compatibility dynamic method + -> RequestSpec + -> shared runtime call layer + -> httpx transport + -> authentication, retry, rate limiting + -> response decoding + -> Pydantic typed response or compatibility dict response +``` + +The SDK is split into three main areas: + +- Public client layer: `Client`, `AsyncClient`, service gateways, and compatibility aliases. +- Generated service layer: async typed mixins and Pydantic models under `osc_sdk_python.generated.*`. +- Runtime layer: request execution, authentication, transport, retries, rate limiting, logging, and errors. + +## 4. Package Structure + +The SDK V2 structure is organized around shared runtime code and generated service slices. + +```text +osc_sdk_python/ + __init__.py + outscale_gateway.py + credentials.py + exceptions.py + problem.py + runtime/ + call.py + request.py + transport.py + codegen/ + adapters.py + generator.py + ir.py + overlay.py + generated/ + osc/ + __init__.py + async_client.py + models.py + oks/ + __init__.py + async_client.py + models.py + resources/ + osc/ + api.yaml + cfg.yaml / overlay files when needed + oks/ + api.yaml + cfg.yaml / overlay files when needed +``` + +Key responsibilities: + +- `outscale_gateway.py` wires service clients and compatibility APIs. +- `runtime.call` owns sync and async request execution. +- `runtime.request` defines `RequestSpec`, the common request description passed to the runtime. +- `runtime.transport` owns httpx auth, retries, rate limiting, and HTTP error conversion. +- `codegen` turns OpenAPI into generated models and async typed methods. +- `generated.` contains generated Pydantic models and async typed mixins. + +## 5. Client Layer + +`Client` creates synchronous service namespaces: + +```text +Client + |- osc: OutscaleGateway + |- oks: OksGateway +``` + +`AsyncClient` creates asynchronous service namespaces: + +```text +AsyncClient + |- osc: AsyncOutscaleGateway + |- oks: AsyncOksGateway +``` + +Each service namespace receives the same client configuration arguments, so profile, credentials, endpoint overrides, retry settings, rate limits, and TLS settings are applied consistently. + +The async service clients inherit generated typed mixins: + +```text +AsyncOutscaleGateway + -> AsyncOscTypedMixin + -> AsyncOpenAPIActionAPI + +AsyncOksGateway + -> AsyncOksTypedMixin + -> AsyncOpenAPIPathAPI +``` + +This is why async operations can expose typed snake_case methods such as `read_vms` and `list_projects`. + +## 6. Sync Compatibility Layer + +The sync API keeps dynamic operation dispatch. The service client reads the OpenAPI specification, builds a gateway structure, and resolves method calls dynamically. + +```text +client.osc.ReadVms(...) + -> __getattr__("ReadVms") + -> validate action and parameters from OpenAPI request schema + -> Call.api("ReadVms", service="api", ...) + -> POST /api/v1/ReadVms + -> return decoded JSON dict +``` + +For REST/path-style services, the sync layer can map an operation name to method, path, path parameters, query parameters, and request body. + +Sync is important, but it is not the primary typed SDK V2 surface. Its role is compatibility and blocking use cases. + +## 7. Async Typed Layer + +The async typed layer is generated under `osc_sdk_python.generated.`. + +For each service, the generator emits: + +- `models.py`: Pydantic request and response models. +- `async_client.py`: an async typed mixin with one method per operation. +- `__init__.py`: exported models and the service mixin. + +Generated methods: + +- Use `snake_case` operation names converted from OpenAPI `operationId`. +- Accept a typed request model or a Python value that Pydantic can validate. +- Serialize request models using OpenAPI aliases. +- Build a `RequestSpec` for the runtime. +- Await the shared async runtime call. +- Validate the decoded response into the generated response model. +- Raise SDK-owned validation or response exceptions when Pydantic validation fails. + +Example generated OSC method shape: + +```python +response = await client.osc.read_vms(ReadVmsRequest()) +``` + +Example generated OKS method shape: + +```python +projects = await client.oks.list_projects(ListProjectsRequest()) +``` + +This design gives async users typed responses such as `response.vms` and `vm.vm_id` instead of only raw dictionaries. + +## 8. Generator Architecture + +The generator converts service OpenAPI files into Python source code. + +```text +resources//api.yaml or cfg.yaml + -> load_spec + -> optional overlay application + -> PathOperationAdapter + -> intermediate representation + -> render Pydantic models + -> render async typed mixin + -> render service exports +``` + +The generator entry point is: + +```bash +python -m osc_sdk_python.codegen.generator +``` + +A subset of services can be generated explicitly: + +```bash +python -m osc_sdk_python.codegen.generator oks osc +``` + +Generated files include a header stating that typed request and response models are async-first and that generated typed methods are exposed on `AsyncClient`. + +## 9. Supported OpenAPI Styles + +The generator must support two OpenAPI styles. + +### 9.1 OSC Action-Style OpenAPI + +OSC operations are action-style operations. The OpenAPI path usually matches the action name. + +Example shape: + +```text +/ReadVms + operationId: ReadVms + request schema: ReadVmsRequest + response schema: ReadVmsResponse +``` + +The generator recognizes this as an action-body operation when the path name matches the operation ID. In that case, the request model is serialized as the JSON body. + +Async typed behavior: + +```text +client.osc.read_vms(ReadVmsRequest(...)) + -> RequestSpec(service="api", method="POST", path="/ReadVms", json_body=) + -> typed ReadVmsResponse +``` + +Sync compatibility behavior: + +```text +client.osc.ReadVms(...) + -> POST /api/v1/ReadVms + -> decoded dict +``` + +The action-style support preserves compatibility while allowing the async SDK to provide typed Python methods. + +### 9.2 REST/Path-Style OpenAPI + +REST/path-style services describe operations through HTTP methods, paths, parameters, and request bodies. + +Example shape: + +```text +GET /projects + operationId: ListProjects + query parameters: name, status, page, limit + response schema: ProjectResponseList + +POST /projects + operationId: CreateProject + request body: CreateProjectRequest body + response schema: ProjectResponse +``` + +Async typed behavior: + +```text +client.oks.list_projects(ListProjectsRequest(...)) +client.oks.create_project(CreateProjectRequest(...)) +``` + +The generator maps: + +- Path parameters into `path_params`. +- Query parameters into `query_params`. +- JSON request bodies into `json_body`. +- 2xx JSON responses into generated response models. + +## 10. Intermediate Representation + +The generator uses a common intermediate representation so OSC action-style and REST/path-style APIs can share code generation. + +The IR contains: + +- `Field`: Python name, OpenAPI alias, type annotation, and required flag. +- `Model`: model name, field list, or alias type. +- `Operation`: operation ID, generated method name, request model, response model, HTTP method, path, path fields, query fields, body field, and action-body flag. + +This common IR is the bridge between OpenAPI differences and consistent Python output. + +## 11. OpenAPI Overlays + +Some service specifications may need SDK-specific corrections before generation. The overlay loader supports `cfg.yaml` files that point to a base spec and an overlay file. + +Overlays can: + +- Patch schema details. +- Remove invalid or unsupported nodes. +- Fix names or metadata used by generation. +- Adjust operation or parameter definitions. +- Keep corrections outside generated Python files. + +Current release policy: overlays are not applied during release generation. Release builds use the base OpenAPI specifications with `--skip-overlay` because the current overlay files are not yet fully validated and can incorrectly mark optional response fields as required. + +The rule remains: fix the spec input, overlay, or generator. Do not manually edit generated code. However, overlays should only become part of release generation after they are reviewed, tested, and proven not to over-constrain generated models. + +## 12. Runtime Request Flow + +Both sync and async calls use `RequestSpec` to describe the HTTP request. + +```text +RequestSpec + service + method + path + json_body + query_params +``` + +The runtime resolves the final endpoint from the profile: + +```text +profile.get_endpoint(service) + RequestSpec.path +``` + +Then it sends the request through httpx using the SDK transport. + +Sync flow: + +```text +Call.request + -> httpx.Client + -> SdkTransport + -> response JSON +``` + +Async flow: + +```text +AsyncCall.request + -> httpx.AsyncClient + -> AsyncSdkTransport + -> response JSON +``` + +## 13. Authentication + +Authentication is handled by `SdkAuth` in the httpx layer. + +The runtime supports: + +- OSC signed authentication with access key and secret key. +- IAM V2 credentials for configured services. +- OKS-specific access key and secret key headers. +- Basic authentication when login and password are configured. +- Service-aware signing using region and service name. + +Services can choose the right authentication behavior through the `service` value on `RequestSpec`. + +## 14. Configuration and Profiles + +The SDK uses profiles for credentials, regions, endpoints, and runtime options. + +Configuration precedence is: + +1. Explicit client constructor arguments. +2. Environment variables. +3. `~/.osc/config.json` or a configured credentials file. +4. SDK defaults. + +Important profile fields include: + +- `access_key` and `secret_key`. +- `access_key_v2` and `secret_key_v2`. +- `iam_v2_services`. +- `login` and `password`. +- `region`, defaulting to `eu-west-2`. +- `protocol`, defaulting to `https`. +- Service endpoints such as `api`, `oks`, `lbu`, `oos`, `fcu`, `eim`, and `direct_link`. +- TLS verification behavior. + +The service endpoint model is important for multi-service support because each namespace can resolve to its own base URL. + +## 15. Retry, Rate Limiting, and Transport + +The runtime uses httpx transports for both sync and async clients. + +Shared transport behavior includes: + +- Rate limiting before requests. +- Retry policy with max retries, exponential backoff, jitter, and `Retry-After` support. +- Client/server HTTP error conversion into SDK exceptions. +- Transport error wrapping. +- TLS verification settings from the profile. +- `trust_env=False` to avoid implicit environment proxy behavior unless the SDK chooses to support it explicitly. + +Sync uses `SdkTransport`. Async uses `AsyncSdkTransport`. + +## 16. Error Model + +SDK V2 exposes SDK-owned exceptions rather than leaking raw transport or validation errors. + +Important exception categories include: + +- `SdkError` as the base SDK exception. +- `SdkUsageError` for incorrect SDK usage. +- `SdkConfigurationError` for missing or invalid configuration. +- `SdkValidationError` for request validation problems. +- `SdkResponseError` for invalid response bodies. +- `SdkTransportError` for low-level transport failures. +- `SdkClientError` for HTTP 4xx responses. +- `SdkServerError` for HTTP 5xx responses. + +The runtime also decodes OUTSCALE problem formats where possible and attaches request/response context to HTTP errors. + +## 17. Logging + +SDK V2 uses Python's standard `logging` module with the `osc_sdk_python` logger. + +Request logs include: + +- Mode: sync or async. +- Service name. +- HTTP method. +- URI. +- JSON payload. + +Logging should remain safe for users. Sensitive values such as credentials and authentication headers must not be exposed in normal logs. + +## 18. Testing Strategy + +The repository separates tests by behavior area: + +```text +tests/ + async_/ + osc/ + oks/ + sync/ + osc/ + oks/ + unit/ +``` + +Testing should cover: + +- Async typed OSC operations. +- Async typed OKS operations. +- Sync compatibility methods. +- Raw sync and async calls. +- Client lifecycle and context managers. +- Profile loading and endpoint resolution. +- Authentication behavior. +- RequestSpec path resolution. +- Transport retry and rate limiting. +- OpenAPI adapter behavior. +- Overlay application. +- SDK exception mapping. +- Pydantic request and response validation. + +The async tests are especially important because async typed usage is the primary SDK V2 interface. + +## 19. Adding a New Service + +A new service should be added through the generator pipeline. + +Steps: + +1. Add `resources//api.yaml`. +2. Add `cfg.yaml` and overlay files only if the base OpenAPI specification needs SDK-specific corrections; keep release generation on `--skip-overlay` until those overlays are validated. +3. Configure the service name used in `RequestSpec` and endpoint resolution. +4. Run the generator for that service. +5. Add the generated service package under `osc_sdk_python.generated.`. +6. Add the async typed mixin to the async service gateway. +7. Register the service namespace on `Client` and `AsyncClient`. +8. Add sync compatibility behavior if the service needs blocking support. +9. Add async, sync, and generator tests. +10. Update README and examples. + +Future services should fit into the same client and runtime architecture rather than creating separate one-off SDK clients. + +## 20. Versioning and Compatibility + +SDK V2 should follow semantic versioning. + +Compatibility rules: + +- Async typed APIs are the main SDK V2 direction. +- Sync support remains available for compatibility. +- `Gateway` compatibility can remain, but `Client` and `AsyncClient` should be the preferred V2 entry points. +- Breaking changes require a major version bump or a migration path. +- Generated code should be reproducible from the intended release inputs, generator flags, templates, and generator code. +- Generated files should not be manually edited. + +## 21. Notes + +- Configuration precedence should remain explicit and predictable: + 1. Direct constructor args, for example `Gateway(access_key="...", secret_key="...", region="...")`. + 2. Explicit file profile, for example `Gateway(profile="prod")`. + 3. Environment direct values, for example `OSC_ACCESS_KEY`, `OSC_SECRET_KEY`, and `OSC_REGION`. + 4. Environment-selected profile, for example `OSC_PROFILE=prod`. + 5. File default profile, for example the `default` profile in the credentials file. + 6. SDK defaults, for example `protocol="https"` and `region="eu-west-2"`. +- Release generation currently uses `--skip-overlay` for OSC and OKS until overlays are redesigned and validated. +- Async profile or credential updates could be improved: `AsyncCall.update_profile()` recreates the underlying httpx.AsyncClient without explicitly closing the previous instance. Since AsyncClient requires await client.aclose(), consider introducing an async update_profile() method or another lifecycle mechanism to ensure the previous client is cleaned up safely. Also add test for it. +- Retry environment variables such as `OSC_MAX_RETRIES`, `OSC_RETRY_BACKOFF_FACTOR`, `OSC_RETRY_BACKOFF_JITTER`, and `OSC_RETRY_BACKOFF_MAX` are documented in the README but are not currently wired into the SDK runtime. Retry configuration currently works through constructor arguments only, so environment variable support should be fixed or the documentation should be corrected. + +## 22. Summary + +The OUTSCALE Python SDK V2 is an async-first, generated, typed, multi-service SDK. + +`AsyncClient` is the primary interface and exposes typed snake_case operations generated from OpenAPI, such as `client.osc.read_vms(...)` and `client.oks.list_projects(...)`. `Client` keeps synchronous compatibility through service namespaces and dynamic operation methods such as `client.osc.ReadVms(...)`. + +The generator supports both OSC action-style OpenAPI and REST/path-style OpenAPI by normalizing them into a shared intermediate representation. The runtime then applies common configuration, endpoint resolution, authentication, retries, rate limiting, logging, error handling, and httpx transport behavior across all services. + diff --git a/docs/examples.md b/docs/examples.md index 24de958..0334f38 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -3,40 +3,99 @@ Basic usage with the default profile: ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client -with Gateway() as gw: +with Client() as client: # Example: list VMs - vms = gw.ReadVms() + vms = client.osc.ReadVms() print(vms) ``` +Async usage with the default profile: + +```python +import asyncio + +from osc_sdk_python import AsyncClient + + +async def main(): + async with AsyncClient() as client: + # Example: list VMs + vms = await client.osc.read_vms() + print(vms) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + Using a specific profile: ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client -gw = Gateway(profile="profile_1") +client = Client(profile="profile_1") +``` + +Using a specific profile with the async client: + +```python +from osc_sdk_python import AsyncClient + +client = AsyncClient(profile="profile_1") +``` + +Using multiple services from one client: + +```python +from osc_sdk_python import Client + +with Client(profile="profile_1") as client: + vms = client.osc.ReadVms() + projects = client.oks.ListProjects() +``` + +Using multiple services from one async client: + +```python +import asyncio + +from osc_sdk_python import AsyncClient + + +async def main(): + async with AsyncClient(profile="profile_1") as client: + vms = await client.osc.read_vms() + projects = await client.oks.list_projects() + + +if __name__ == "__main__": + asyncio.run(main()) ``` Calling actions: -* **Typed methods**: `gw.ReadVms(...)`, `gw.CreateVms(...)`, etc. -* **Raw calls**: `gw.raw("ActionName", **params)` +* **Sync dynamic methods**: `client.osc.ReadVms(...)`, `client.osc.CreateVms(...)`, etc. +* **Raw calls**: `client.osc.raw("ActionName", **params)` +* **Async typed methods**: `await client.osc.read_vms(...)`, `await client.osc.create_vms(...)`, etc. +* **Async raw calls**: `await client.osc.raw("ActionName", **params)` + +Typed request and response models under `osc_sdk_python.generated.*` are async-first today: generated typed methods are exposed on `AsyncClient` and use snake_case operation names. Synchronous callers should continue to use dynamic action methods such as `client.osc.ReadVms(...)` or raw calls such as `client.osc.raw("ReadVms", **params)`. Example: ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client -with Gateway(profile="profile_1") as gw: +with Client(profile="profile_1") as client: # Calls with API action as method - result = gw.ReadSecurityGroups(Filters={"SecurityGroupNames": ["default"]}) - result = gw.CreateVms(ImageId="ami-3e158364", VmType="tinav4.c2r4") + result = client.osc.ReadSecurityGroups(Filters={"SecurityGroupNames": ["default"]}) + result = client.osc.CreateVms(ImageId="ami-3e158364", VmType="tinav4.c2r4") # Or raw calls: - result = gw.raw("ReadVms") - result = gw.raw( + result = client.osc.raw("ReadVms") + result = client.osc.raw( "CreateVms", ImageId="ami-xx", BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], @@ -45,41 +104,136 @@ with Gateway(profile="profile_1") as gw: ) ``` +Async example: + +```python +import asyncio + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import CreateVmsRequest, ReadSecurityGroupsRequest + + +async def main(): + async with AsyncClient(profile="profile_1") as client: + # Calls with operationId converted to snake_case + result = await client.osc.read_security_groups( + ReadSecurityGroupsRequest(filters={"SecurityGroupNames": ["default"]}) + ) + result = await client.osc.create_vms( + CreateVmsRequest(image_id="ami-3e158364", vm_type="tinav4.c2r4") + ) + + # Or raw calls: + result = await client.osc.raw("ReadVms") + result = await client.osc.raw( + "CreateVms", + ImageId="ami-xx", + BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], + SecurityGroupIds=["sg-aaa", "sg-bbb"], + Wrong="wrong", + ) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +--- + +### Handling SDK exceptions + +Public SDK methods raise exceptions owned by the SDK. Catch `SdkError` to handle any SDK failure, or catch a narrower subclass when you need a specific category. + +```python +import asyncio + +from osc_sdk_python import AsyncClient, SdkError, SdkClientError + +async def main(): + try: + async with AsyncClient() as client: + print(await client.osc.read_vms()) + except SdkClientError as err: + print("API rejected the request:", err) + if err.response is not None: + print("status:", err.response.status_code) + except SdkError as err: + print("SDK error:", err) + +if __name__ == "__main__": + asyncio.run(main()) +``` + --- -## 💡 Examples +## Examples ### List all VM and Volume IDs ```python -from osc_sdk_python import Gateway +from osc_sdk_python import Client if __name__ == "__main__": - with Gateway() as gw: + with Client() as client: print("Your virtual machines:") - for vm in gw.ReadVms()["Vms"]: + for vm in client.osc.ReadVms()["Vms"]: print(vm["VmId"]) print("\nYour volumes:") - for volume in gw.ReadVolumes()["Volumes"]: + for volume in client.osc.ReadVolumes()["Volumes"]: print(volume["VolumeId"]) ``` +### List all VM and Volume IDs asynchronously + +```python +import asyncio + +from osc_sdk_python import AsyncClient + + +async def main(): + async with AsyncClient() as client: + print("Your virtual machines:") + for vm in (await client.osc.read_vms()).vms: + print(vm.vm_id) + + print("\nYour volumes:") + for volume in (await client.osc.read_volumes()).volumes: + print(volume.volume_id) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + ### Enabling logs ```python -from osc_sdk_python import * +import logging + +from osc_sdk_python import Client if __name__ == "__main__": - with Gateway(profile="profile_1") as gw: - # 'what' can be LOG_KEEP_ONLY_LAST_REQ or LOG_ALL - # Here we print logs in memory, standard output and standard error - gw.log.config(type=LOG_MEMORY | LOG_STDIO | LOG_STDERR, what=LOG_KEEP_ONLY_LAST_REQ) + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + ) + + with Client(profile="profile_1") as client: + result = client.osc.raw("ReadVms") + print(result) +``` - result = gw.raw("ReadVms") +This logs requests through Python's standard `logging` module using the `osc_sdk_python` logger: - last_request = gw.log.str() - print(last_request) +```text +2026-06-15 12:45:10,123 - INFO - mode: sync +service: api +method: POST +uri: /api/v1/ReadVms +payload: +{} ``` Usage examples can be combined with the official [Outscale API documentation](https://docs.outscale.com/en/userguide/Home.html). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6a6e7a9..16cb174 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -5,7 +5,7 @@ Some users may encounter UTF-8 issues that look like this: ```bash -Problem reading (…)osc_sdk_python/osc-api/outscale.yaml:'ascii' codec can't decode byte 0xe2 in position 14856: ordinal not in range(128) +Problem reading (…)osc_sdk_python/resources/osc/api.yaml:'ascii' codec can't decode byte 0xe2 in position 14856: ordinal not in range(128) ``` To avoid this issue, configure your locale as follows: @@ -18,4 +18,8 @@ If you do not want your locale to be set system-wide, you can do: ```bash LC_ALL=en_US.UTF-8 pip install osc-sdk-python -``` \ No newline at end of file +``` + +### SDK-owned exceptions + +Public SDK methods raise exceptions from `osc_sdk_python`, rooted at `SdkError`. Use `except SdkError` for a broad SDK boundary, then inspect subclasses such as `SdkClientError`, `SdkServerError`, `SdkTransportError`, `SdkValidationError`, `SdkConfigurationError`, and `SdkResponseError` when needed. diff --git a/local-tests.sh b/local-tests.sh index b3c0da9..8e9c326 100755 --- a/local-tests.sh +++ b/local-tests.sh @@ -1,4 +1,4 @@ -export OSC_TEST_PASSWORD=ashita wa dochida +export OSC_TEST_PASSWORD="ashita wa dochida" export OSC_TEST_LOGIN=joe export OSC_SECRET_KEY=0000001111112222223333334444445555555666 export OSC_ACCESS_KEY=11112211111110000000 diff --git a/osc_sdk_python/VERSION b/osc_sdk_python/VERSION index 72a8a63..787ffc3 100644 --- a/osc_sdk_python/VERSION +++ b/osc_sdk_python/VERSION @@ -1 +1 @@ -0.41.0 +0.42.0 diff --git a/osc_sdk_python/__init__.py b/osc_sdk_python/__init__.py index 8c91ad5..9f9e1aa 100644 --- a/osc_sdk_python/__init__.py +++ b/osc_sdk_python/__init__.py @@ -1,16 +1,22 @@ from .outscale_gateway import OutscaleGateway as Gateway -from .outscale_gateway import LOG_NONE -from .outscale_gateway import LOG_STDERR -from .outscale_gateway import LOG_STDIO -from .outscale_gateway import LOG_MEMORY +from .outscale_gateway import AsyncOutscaleGateway as AsyncGateway +from .outscale_gateway import Client +from .outscale_gateway import AsyncClient from .version import get_version from .problem import Problem, ProblemDecoder -from .limiter import RateLimiter -from .retry import Retry - -# what to Log -from .outscale_gateway import LOG_ALL -from .outscale_gateway import LOG_KEEP_ONLY_LAST_REQ +from .runtime.transport import RateLimiter +from .exceptions import ( + SdkClientError, + SdkConfigurationError, + SdkError, + SdkHttpError, + SdkOperationError, + SdkResponseError, + SdkServerError, + SdkTransportError, + SdkUsageError, + SdkValidationError, +) __author__ = "Outscale SAS" __version__ = get_version() @@ -18,14 +24,20 @@ "__version__", "__author__", "Gateway", - "LOG_NONE", - "LOG_STDERR", - "LOG_STDIO", - "LOG_MEMORY", - "LOG_ALL", - "LOG_KEEP_ONLY_LAST_REQ", + "AsyncGateway", + "Client", + "AsyncClient", "Problem", "ProblemDecoder", "RateLimiter", - "Retry", + "SdkError", + "SdkUsageError", + "SdkConfigurationError", + "SdkValidationError", + "SdkOperationError", + "SdkTransportError", + "SdkHttpError", + "SdkClientError", + "SdkServerError", + "SdkResponseError", ] diff --git a/osc_sdk_python/authentication.py b/osc_sdk_python/authentication.py deleted file mode 100644 index 23fd8ac..0000000 --- a/osc_sdk_python/authentication.py +++ /dev/null @@ -1,176 +0,0 @@ -import datetime -import hashlib -import hmac -import base64 - -from .version import get_version -from .credentials import Profile - -VERSION: str = get_version() -DEFAULT_USER_AGENT = "osc-sdk-python/" + VERSION - - -class Authentication: - def __init__( - self, - credentials: Profile, - host: str, - method="POST", - service="api", - content_type="application/json; charset=utf-8", - algorithm="OSC4-HMAC-SHA256", - signed_headers="content-type;host;x-osc-date", - user_agent=DEFAULT_USER_AGENT, - ): - self.access_key = credentials.access_key - self.secret_key = credentials.secret_key - self.login = credentials.login - self.password = credentials.password - self.host = host - self.region = credentials.region - self.content_type = content_type - self.method = method - self.service = service - self.algorithm = algorithm - self.signed_headers = signed_headers - self.user_agent = user_agent - self.x509_client_cert = credentials.x509_client_cert - - def forge_headers_signed(self, uri, request_data): - date_iso, date = self.build_dates() - credential_scope = "{}/{}/{}/osc4_request".format( - date, self.region, self.service - ) - - canonical_request = self.build_canonical_request(date_iso, uri, request_data) - str_to_sign = self.create_string_to_sign( - date_iso, credential_scope, canonical_request - ) - signature = self.compute_signature(date, str_to_sign) - authorisation = self.build_authorization_header(credential_scope, signature) - - return { - "Content-Type": self.content_type, - "X-Osc-Date": date_iso, - "Authorization": authorisation, - "User-Agent": self.user_agent, - } - - def build_dates(self): - """Return YYYYMMDDTHHmmssZ, YYYYMMDD""" - t = datetime.datetime.now(datetime.timezone.utc) - return t.strftime("%Y%m%dT%H%M%SZ"), t.strftime("%Y%m%d") - - def sign(self, key, msg): - return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest() - - def get_signature_key(self, key, date_stamp_value): - k_date = self.sign(("OSC4" + key).encode("utf-8"), date_stamp_value) - k_region = self.sign(k_date, self.region) - k_service = self.sign(k_region, self.service) - k_signing = self.sign(k_service, "osc4_request") - return k_signing - - def build_canonical_request(self, date_iso, canonical_uri, request_data): - # - # Step 1 is to define the verb (GET, POST, etc.)--already done. - # Step 2: Create canonical URI--the part of the URI from domain to query - # string (use '/' if no path) - # canonical_uri = '/' - # Step 3: Create the canonical query string. In this example, request - # parameters are passed in the body of the request and the query string - # is blank. - # Step 4: Create the canonical headers. Header names must be trimmed - # and lowercase, and sorted in code point order from low to high. - # Note that there is a trailing \n. - # Step 5: Create the list of signed headers. This lists the headers - # in the canonical_headers list, delimited with ";" and in alpha order. - # Note: The request can include any headers; canonical_headers and - # signed_headers include those that you want to be included in the - # hash of the request. "Host" and "x-amz-date" are always required. - # Step 6: Create payload hash. In this example, the payload (body of - # the request) contains the request parameters. - # Step 7: Combine elements to create canonical request - canonical_querystring = "" - canonical_headers = ( - "content-type:" - + self.content_type - + "\n" - + "host:" - + self.host - + "\n" - + "x-osc-date:" - + date_iso - + "\n" - ) - payload_hash = hashlib.sha256(request_data.encode("utf-8")).hexdigest() - return ( - self.method - + "\n" - + canonical_uri - + "\n" - + canonical_querystring - + "\n" - + canonical_headers - + "\n" - + self.signed_headers - + "\n" - + payload_hash - ) - - def create_string_to_sign(self, date_iso, credential_scope, canonical_request): - # ************* TASK 2: CREATE THE STRING TO SIGN************* - # Match the algorithm to the hashing algorithm you use, either SHA-1 or - # SHA-256 (recommended) - return ( - self.algorithm - + "\n" - + date_iso - + "\n" - + credential_scope - + "\n" - + hashlib.sha256(canonical_request.encode("utf-8")).hexdigest() - ) - - def compute_signature(self, date, string_to_sign): - # ************* TASK 3: CALCULATE THE SIGNATURE ************* - # Create the signing key using the function defined above. - signing_key = self.get_signature_key(self.secret_key, date) - - # Sign the string_to_sign using the signing_key - return hmac.new( - signing_key, string_to_sign.encode("utf-8"), hashlib.sha256 - ).hexdigest() - - def build_authorization_header(self, credential_scope, signature): - # ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST ************* - # Put the signature information in a header named Authorization. - return ( - self.algorithm - + " " - + "Credential=" - + self.access_key - + "/" - + credential_scope - + ", " - + "SignedHeaders=" - + self.signed_headers - + ", " - + "Signature=" - + signature - ) - - def is_basic_auth_configured(self): - return self.login is not None and self.password is not None - - def get_basic_auth_header(self): - if not self.is_basic_auth_configured(): - raise Exception("email or password not set") - creds = self.login + ":" + self.password - b64_creds = str(base64.b64encode(creds.encode("utf-8")), "utf-8") - date_iso, _ = self.build_dates() - return { - "Content-Type": self.content_type, - "X-Osc-Date": date_iso, - "Authorization": "Basic " + b64_creds, - } diff --git a/osc_sdk_python/call.py b/osc_sdk_python/call.py deleted file mode 100644 index 123c75a..0000000 --- a/osc_sdk_python/call.py +++ /dev/null @@ -1,97 +0,0 @@ -from .authentication import Authentication -from .authentication import DEFAULT_USER_AGENT -from .credentials import Profile -from .requester import Requester -from requests import Session -from urllib3.util import parse_url -from datetime import timedelta -from .limiter import RateLimiter - -import json -import warnings - - -class Call(object): - def __init__(self, logger=None, limiter=None, **kwargs): - self.version = kwargs.pop("version", "latest") - self.host = kwargs.pop("host", None) - self.ssl = kwargs.pop("_ssl", True) - self.user_agent = kwargs.pop("user_agent", DEFAULT_USER_AGENT) - self.logger = logger - self.limiter: RateLimiter | None = limiter - self.retry_kwargs = {} - self.session = Session() - self.session.trust_env = False - - kwargs = self.update_limiter(**kwargs) - kwargs = self.update_retry(**kwargs) - self.update_profile(**kwargs) - - def update_credentials(self, **kwargs): - warnings.warn( - "update_credentials is deprecated, use update_profile instead", - DeprecationWarning, - stacklevel=2, - ) - return self.update_profile(**kwargs) - - def update_profile(self, **kwargs): - self.profile = Profile.from_standard_configuration( - kwargs.pop("path", None), kwargs.pop("profile", None) - ) - self.profile.merge(Profile(**kwargs)) - return kwargs - - def update_limiter(self, **kwargs): - limiter_window = kwargs.pop("limiter_window", None) - if limiter_window is not None and self.limiter is not None: - self.limiter.window = timedelta(seconds=int(limiter_window)) - - limiter_max_requests = kwargs.pop("limiter_max_requests", None) - if limiter_max_requests is not None and self.limiter is not None: - self.limiter.max_requests = limiter_max_requests - - return kwargs - - def update_retry(self, **kwargs): - max_retries = kwargs.pop("max_retries", None) - if max_retries is not None: - self.retry_kwargs["max_retries"] = int(max_retries) - - for key in ["backoff_factor", "backoff_jitter", "backoff_max"]: - value = kwargs.pop(f"retry_{key}", None) - if value is not None: - self.retry_kwargs[key] = float(value) - return kwargs - - def api(self, action, service="api", **data): - try: - endpoint = self.profile.get_endpoint(service) + "/" + action - parsed_url = parse_url(endpoint) - uri = parsed_url.path - host = parsed_url.host - - if self.limiter is not None: - self.limiter.acquire() - - requester = Requester( - self.session, - Authentication( - self.profile, - host, - user_agent=self.user_agent, - ), - endpoint, - **self.retry_kwargs, - ) - if self.logger is not None: - self.logger.do_log( - "uri: " + uri + "\npayload:\n" + json.dumps(data, indent=2) - ) - return requester.send(uri, json.dumps(data)) - except Exception as err: - raise err - - def close(self): - if self.session: - self.session.close() diff --git a/osc_sdk_python/codegen/__init__.py b/osc_sdk_python/codegen/__init__.py new file mode 100644 index 0000000..4bcfe30 --- /dev/null +++ b/osc_sdk_python/codegen/__init__.py @@ -0,0 +1,2 @@ +"""Small OpenAPI code generation helpers for generated typed clients.""" + diff --git a/osc_sdk_python/codegen/adapters.py b/osc_sdk_python/codegen/adapters.py new file mode 100644 index 0000000..98f9c85 --- /dev/null +++ b/osc_sdk_python/codegen/adapters.py @@ -0,0 +1,251 @@ +import keyword +import logging +import re +from typing import Any + +from .ir import Field, Model, Operation + +logger = logging.getLogger("osc_sdk_python.codegen") + + +def snake_case(value: str) -> str: + value = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", value) + value = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", value) + name = re.sub(r"\W+", "_", value).strip("_").lower() + if not name: + name = "value" + if name[0].isdigit(): + name = "_" + name + if keyword.iskeyword(name): + name += "_" + return name + + +def class_name(value: str) -> str: + name = re.sub(r"\W+", "_", value).strip("_") + if not name: + return "Generated" + if name[0].isdigit(): + name = "_" + name + return name + + +def schema_type(schema: dict[str, Any], ref_resolver=class_name) -> str: + if schema.get("type") == "null": + return "None" + + if schema.get("nullable"): + return ( + schema_type( + {k: v for k, v in schema.items() if k != "nullable"}, ref_resolver + ) + + " | None" + ) + + if "$ref" in schema: + return ref_resolver(ref_name(schema["$ref"])) + + enum_values = schema.get("enum") + if enum_values: + return "Literal[" + ", ".join(repr(value) for value in enum_values) + "]" + + for composed in ("allOf", "oneOf", "anyOf"): + options = schema.get(composed) + if not options: + continue + if len(options) == 1: + return schema_type(options[0], ref_resolver) + if composed in {"oneOf", "anyOf"}: + option_types = [schema_type(option, ref_resolver) for option in options] + if "Any" not in option_types: + option_types = [ + option_type for option_type in option_types if option_type != "None" + ] + [ + option_type for option_type in option_types if option_type == "None" + ] + if composed == "oneOf": + logger.warning( + "OpenAPI oneOf with %d schemas represented as a union; " + "exclusivity is not enforced", + len(options), + ) + return " | ".join(dict.fromkeys(option_types)) + if composed == "allOf": + refs = [option for option in options if "$ref" in option] + inline_options = [option for option in options if "$ref" not in option] + if len(refs) == 1 and all( + not option or set(option).issubset({"description", "title"}) + for option in inline_options + ): + return schema_type(refs[0], ref_resolver) + logger.warning( + "OpenAPI %s with %d schemas cannot be represented precisely; using Any", + composed, + len(options), + ) + return "Any" + + typ = schema.get("type") + fmt = schema.get("format") + if typ == "boolean": + return "bool" + if typ == "integer": + return "int" + if typ == "number": + return "float" + if typ == "string": + if fmt in {"date-time", "datetime"}: + return "datetime.datetime" + return "str" + if typ == "array": + item_type = schema_type(schema.get("items", {}), ref_resolver) + return f"list[{item_type}]" + if typ == "object": + additional = schema.get("additionalProperties") + if isinstance(additional, dict): + return f"dict[str, {schema_type(additional, ref_resolver)}]" + return "dict[str, Any]" + logger.warning("OpenAPI schema without a supported type; using Any") + return "Any" + + +def ref_name(ref: str) -> str: + return ref.rsplit("/", 1)[-1] + + +class PathOperationAdapter: + def __init__(self, spec: dict[str, Any], service: str): + self.spec = spec + self.service = service + + def operations(self, selected: set[str] | None = None) -> list[Operation]: + operations = [] + for path, path_item in self.spec.get("paths", {}).items(): + for method in ["get", "post", "put", "patch", "delete"]: + operation = path_item.get(method) + if operation is None: + continue + + operation_id = operation.get("operationId") + if operation_id is None: + continue + if selected is not None and operation_id not in selected: + continue + + path_fields = [] + query_fields = [] + for parameter in path_item.get("parameters", []) + operation.get( + "parameters", [] + ): + location = parameter.get("in") + if location not in {"path", "query"}: + continue + name = parameter["name"] + field = Field( + name=snake_case(name), + alias=name, + annotation=schema_type(parameter.get("schema", {})), + required=parameter.get("required", False), + ) + if location == "path": + path_fields.append(field) + else: + query_fields.append(field) + + body_schema, body_required = self._body_schema(operation) + uses_request_as_body = False + body_field = None + request_fields = path_fields + query_fields + request_model = None + + if ( + body_schema is not None + and not request_fields + and "$ref" in body_schema + and self._is_action_body_operation(path, operation_id) + ): + request_model = Model(class_name(ref_name(body_schema["$ref"]))) + uses_request_as_body = True + elif body_schema is not None: + body_field = Field( + name="body", + alias="body", + annotation=schema_type(body_schema), + required=body_required, + ) + request_fields.append(body_field) + + if request_model is None: + request_model = Model(f"{operation_id}Request", request_fields) + response_model = self._response_model(operation) + operations.append( + Operation( + operation_id=operation_id, + method_name=snake_case(operation_id), + request_model=request_model, + response_model=response_model, + http_method=method.upper(), + path=path, + path_fields=path_fields, + query_fields=query_fields, + body_field=body_field, + uses_request_as_body=uses_request_as_body, + ) + ) + return operations + + def schema_models(self) -> list[Model]: + models = [] + schemas = self.spec.get("components", {}).get("schemas", {}) + for schema_name, schema in schemas.items(): + required_fields = set(schema.get("required", [])) + fields = [] + if "properties" not in schema and ( + schema.get("enum") or schema.get("type") not in {None, "object"} + ): + models.append( + Model( + class_name(schema_name), + alias=schema_type(schema), + ) + ) + continue + + for property_name, property_schema in schema.get("properties", {}).items(): + fields.append( + Field( + name=snake_case(property_name), + alias=property_name, + annotation=schema_type(property_schema), + required=property_name in required_fields, + ) + ) + models.append(Model(class_name(schema_name), fields)) + return models + + def _body_schema( + self, operation: dict[str, Any] + ) -> tuple[dict[str, Any] | None, bool]: + request_body = operation.get("requestBody") + if request_body is None: + return None, False + + content = request_body.get("content", {}) + schema = content.get("application/json", {}).get("schema", {}) + return schema, request_body.get("required", False) + + def _is_action_body_operation(self, path: str, operation_id: str) -> bool: + return path.strip("/").lower() == operation_id.lower() + + def _response_model(self, operation: dict[str, Any]) -> str: + responses = operation.get("responses", {}) + for status in sorted(responses): + if not str(status).startswith("2"): + continue + content = responses[status].get("content", {}) + schema = content.get("application/json", {}).get("schema", {}) + if "$ref" in schema: + return class_name(ref_name(schema["$ref"])) + if schema: + return schema_type(schema) + return "dict[str, Any]" diff --git a/osc_sdk_python/codegen/generator.py b/osc_sdk_python/codegen/generator.py new file mode 100644 index 0000000..fdb51c0 --- /dev/null +++ b/osc_sdk_python/codegen/generator.py @@ -0,0 +1,360 @@ +from pathlib import Path +import argparse +import re +from typing import Iterable + +from .adapters import PathOperationAdapter +from .ir import Field, Model, Operation +from .overlay import load_spec + + +GENERATED_HEADER = '''"""Generated typed {service_label} client slice. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. + +Do not edit by hand. Regenerate with: + python -m osc_sdk_python.codegen.generator + + python -m osc_sdk_python.codegen.generator oks osc +""" +''' + + +DEFAULT_SERVICE_NAMES = { + "osc": "api", +} + + +def _service_label(package_name: str) -> str: + return package_name.upper() + + +def _service_class_name(package_name: str) -> str: + return "".join( + part.capitalize() for part in re.split(r"[_\W]+", package_name) if part + ) + + +def _mixin_name(package_name: str) -> str: + return f"Async{_service_class_name(package_name)}TypedMixin" + + +def _header(package_name: str) -> str: + return GENERATED_HEADER.format(service_label=_service_label(package_name)) + + +def _annotation(value: str, required: bool) -> str: + if required or " | None" in value: + return value + return value + " | None" + + +def _field_args(required: bool, alias: str) -> str: + if required: + return f"alias={alias!r}" + return f"default=None, alias={alias!r}" + + +def _render_model(model: Model) -> str: + if model.alias is not None: + return f"{model.name} = {model.alias}" + + lines = [f"class {model.name}(GeneratedModel):"] + if not model.fields: + lines.append(" pass") + return "\n".join(lines) + + for field in model.fields: + lines.append( + f" {field.name}: {_annotation(field.annotation, field.required)} = Field({_field_args(field.required, field.alias)})" + ) + return "\n".join(lines) + + +def render_models( + operations: Iterable[Operation], + schema_models: Iterable[Model], + package_name: str, +) -> str: + schema_models = list(schema_models) + schema_model_names = {model.name for model in schema_models} + models = [_render_model(model) for model in schema_models] + models.extend( + _render_model(operation.request_model) + for operation in operations + if operation.request_model.name not in schema_model_names + ) + typing_imports = ["Literal"] + if any(re.search(r"\bAny\b", model) for model in models): + typing_imports.insert(0, "Any") + datetime_import = "" + if any("datetime.datetime" in model for model in models): + datetime_import = "import datetime\n\n" + + return ( + _header(package_name) + + "from __future__ import annotations\n\n" + + datetime_import + + "from typing import " + + ", ".join(typing_imports) + + "\n\n" + + "from pydantic import BaseModel, ConfigDict, Field\n\n\n" + + "class GeneratedModel(BaseModel):\n" + + ' model_config = ConfigDict(populate_by_name=True, extra="allow")\n\n\n' + + "\n\n".join(models) + + "\n" + ) + + +def _field_dump(field: Field) -> str: + return f"{field.alias!r}: request.{field.name}" + + +def _model_imports(operations: list[Operation]) -> list[str]: + names = {operation.request_model.name for operation in operations} + builtins = { + "Any", + "None", + "Literal", + "list", + "dict", + "str", + "int", + "float", + "bool", + } + for operation in operations: + names.update( + name + for name in re.findall( + r"\b[A-Za-z_][A-Za-z0-9_]*\b", operation.response_model + ) + if name not in builtins + ) + return sorted(names) + + +def render_async_client( + operations: list[Operation], + service: str, + package_name: str, +) -> str: + imports = _model_imports(operations) + model_imports = "\n".join(f" {name}," for name in imports) + lines = [ + _header(package_name), + "from typing import Any", + "", + "from pydantic import TypeAdapter, ValidationError", + "", + "from osc_sdk_python.exceptions import SdkResponseError, SdkValidationError", + "from osc_sdk_python.runtime.request import RequestSpec", + "from .models import (", + model_imports, + ")", + "", + "", + "def _dump_json_body(value: Any) -> Any:", + ' if hasattr(value, "model_dump"):', + " return value.model_dump(exclude_none=True, by_alias=True)", + " return value", + "", + "", + "def _validate_request(model: type, value: Any) -> Any:", + " try:", + " if value is None:", + " return model()", + " if isinstance(value, model):", + " return value", + " return TypeAdapter(model).validate_python(value)", + " except ValidationError as error:", + " raise SdkValidationError(str(error)) from error", + "", + "", + "def _validate_response(model: type, value: Any) -> Any:", + " try:", + " return TypeAdapter(model).validate_python(value)", + " except ValidationError as error:", + " raise SdkResponseError(str(error)) from error", + "", + "", + f"class {_mixin_name(package_name)}:", + ] + for operation in operations: + request_is_used = bool( + operation.uses_request_as_body + or operation.body_field is not None + or operation.path_fields + or operation.query_fields + ) + if operation.uses_request_as_body: + json_body = "_dump_json_body(request)" + elif operation.body_field is not None: + json_body = f"_dump_json_body(request.{operation.body_field.name})" + else: + json_body = "None" + lines.extend( + [ + f" async def {operation.method_name}(", + " self,", + f" request: {operation.request_model.name} | None = None,", + f" ) -> {operation.response_model}:", + ] + ) + if request_is_used: + lines.extend( + [ + f" request = _validate_request({operation.request_model.name}, request)", + "", + ] + ) + else: + lines.extend( + [ + " _ = request", + "", + ] + ) + lines.append(" path_params = {") + lines.extend( + f" {_field_dump(field)}," for field in operation.path_fields + ) + lines.extend( + [ + " }", + " query_params = {", + ] + ) + lines.extend( + f" {_field_dump(field)}," for field in operation.query_fields + ) + lines.extend( + [ + " }", + " response = await self.call.request(", + " RequestSpec(", + f' service="{service}",', + f' method="{operation.http_method}",', + f' path="{operation.path}",', + f" json_body={json_body},", + " query_params={", + " key: value", + " for key, value in query_params.items()", + " if value is not None", + " },", + " ),", + " path_params=path_params,", + " )", + f" return _validate_response({operation.response_model}, response)", + "", + ] + ) + return "\n".join(lines) + + +def render_init( + operations: list[Operation], + schema_models: list[Model], + package_name: str, +) -> str: + model_names = sorted( + {model.name for model in schema_models} + | {operation.request_model.name for operation in operations} + ) + mixin_name = _mixin_name(package_name) + lines = [ + "\"\"\"Generated typed SDK exports.", + "", + "Typed request and response models are async-first. Generated typed methods are", + "exposed on AsyncClient; synchronous clients use dynamic action methods.", + "\"\"\"", + "", + f"from .async_client import {mixin_name}", + "from .models import (", + ] + lines.extend(f" {name}," for name in model_names) + lines.extend( + [ + ")", + "", + "__all__ = [", + f' "{mixin_name}",', + ] + ) + lines.extend(f" {name!r}," for name in model_names) + lines.append("]\n") + return "\n".join(lines) + + +def generate( + spec_path: Path, + output_dir: Path, + service: str, + package_name: str | None = None, + skip_overlay: bool = False, +) -> None: + package_name = package_name or output_dir.name + spec = load_spec(spec_path, skip_overlay=skip_overlay) + adapter = PathOperationAdapter(spec, service=service) + operations = adapter.operations() + schema_models = adapter.schema_models() + + output_dir.mkdir(parents=True, exist_ok=True) + (output_dir / "models.py").write_text( + render_models(operations, schema_models, package_name) + ) + (output_dir / "async_client.py").write_text( + render_async_client(operations, service, package_name) + ) + (output_dir / "__init__.py").write_text( + render_init(operations, schema_models, package_name) + ) + + +def generate_all( + root: Path, + services: list[str] | None = None, + skip_overlay: bool = False, +) -> None: + resources_root = root / "resources" + service_dirs = [ + path + for path in sorted(resources_root.iterdir()) + if path.is_dir() and (path / "api.yaml").exists() + ] + selected = set(services or []) + for service_dir in service_dirs: + package_name = service_dir.name + if selected and package_name not in selected: + continue + generate( + service_dir / "cfg.yaml" + if (service_dir / "cfg.yaml").exists() + else service_dir / "api.yaml", + root / "generated" / package_name, + DEFAULT_SERVICE_NAMES.get(package_name, package_name), + package_name, + skip_overlay=skip_overlay, + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Generate typed SDK service slices.") + parser.add_argument( + "services", + nargs="*", + help="Service package names to generate, for example: osc oks. Defaults to all resources/*/api.yaml services.", + ) + parser.add_argument( + "--skip-overlay", + action="store_true", + help="When generating from cfg.yaml, ignore the overlay and use the base spec only.", + ) + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + generate_all(root, args.services or None, skip_overlay=args.skip_overlay) + + +if __name__ == "__main__": + main() diff --git a/osc_sdk_python/codegen/ir.py b/osc_sdk_python/codegen/ir.py new file mode 100644 index 0000000..c563135 --- /dev/null +++ b/osc_sdk_python/codegen/ir.py @@ -0,0 +1,30 @@ +from dataclasses import dataclass, field + + +@dataclass +class Field: + name: str + alias: str + annotation: str + required: bool = False + + +@dataclass +class Model: + name: str + fields: list[Field] = field(default_factory=list) + alias: str | None = None + + +@dataclass +class Operation: + operation_id: str + method_name: str + request_model: Model + response_model: str + http_method: str + path: str + path_fields: list[Field] = field(default_factory=list) + query_fields: list[Field] = field(default_factory=list) + body_field: Field | None = None + uses_request_as_body: bool = False diff --git a/osc_sdk_python/codegen/overlay.py b/osc_sdk_python/codegen/overlay.py new file mode 100644 index 0000000..868454c --- /dev/null +++ b/osc_sdk_python/codegen/overlay.py @@ -0,0 +1,135 @@ +from copy import deepcopy +from pathlib import Path +import re +from typing import Any + +import ruamel.yaml + + +FILTER_RE = re.compile(r"^(?:\*)?\[\?\(@\.([A-Za-z0-9_]+) == ['\"]([^'\"]+)['\"]\)\]$") + + +def deep_update(target: dict[str, Any], update: dict[str, Any]) -> None: + for key, value in update.items(): + if isinstance(value, dict) and isinstance(target.get(key), dict): + deep_update(target[key], value) + else: + target[key] = deepcopy(value) + + +def parse_target(target: str) -> list[str]: + if not target.startswith("$."): + raise ValueError(f"Unsupported overlay target: {target}") + + tokens = [] + i = 2 + while i < len(target): + if target[i] == ".": + i += 1 + continue + if target[i] == "[": + end = target.index("]", i) + value = target[i + 1 : end] + if ( + len(value) >= 2 + and value[0] in {"'", '"'} + and value[-1] == value[0] + ): + value = value[1:-1] + else: + value = "[" + value + "]" + if value.startswith("[?") and tokens and tokens[-1] == "*": + tokens[-1] += value + else: + tokens.append(value) + i = end + 1 + continue + + start = i + while i < len(target) and target[i] not in ".[": + i += 1 + tokens.append(target[start:i]) + return tokens + + +def iter_matches(node: Any, tokens: list[str]) -> list[tuple[Any, str | int | None]]: + if not tokens: + return [(None, None)] + + parents = [(None, None, node)] + for token in tokens: + next_parents = [] + filter_match = FILTER_RE.match(token) + for _parent, _key, current in parents: + if token == "*": + if isinstance(current, dict): + next_parents.extend((current, key, value) for key, value in current.items()) + elif isinstance(current, list): + next_parents.extend( + (current, index, value) for index, value in enumerate(current) + ) + elif filter_match: + field, expected = filter_match.groups() + if isinstance(current, dict): + for key, value in current.items(): + if isinstance(value, dict) and str(value.get(field)) == expected: + next_parents.append((current, key, value)) + elif isinstance(current, list): + for index, value in enumerate(current): + if isinstance(value, dict) and str(value.get(field)) == expected: + next_parents.append((current, index, value)) + elif isinstance(current, dict) and token in current: + next_parents.append((current, token, current[token])) + parents = next_parents + return [(parent, key) for parent, key, _current in parents] + + +def apply_overlay(spec: dict[str, Any], overlay: dict[str, Any]) -> dict[str, Any]: + patched = deepcopy(spec) + for action in overlay.get("actions", []): + target = action.get("target") + if not target: + continue + + matches = iter_matches(patched, parse_target(target)) + if action.get("remove"): + for parent, key in sorted( + matches, + key=lambda match: match[1] if isinstance(match[1], int) else -1, + reverse=True, + ): + if parent is None or key is None: + continue + if isinstance(parent, dict): + parent.pop(key, None) + elif isinstance(parent, list) and isinstance(key, int): + parent.pop(key) + continue + + for parent, key in matches: + if parent is None or key is None: + continue + update = action.get("update") + if update is None: + continue + if isinstance(parent[key], dict) and isinstance(update, dict): + deep_update(parent[key], update) + else: + parent[key] = deepcopy(update) + return patched + + +def load_spec(path: Path, skip_overlay: bool = False) -> dict[str, Any]: + yaml = ruamel.yaml.YAML(typ="safe") + document = yaml.load(path.read_text()) + + if not isinstance(document, dict) or "spec" not in document: + return document + + spec_path = (path.parent / document["spec"]).resolve() + spec = yaml.load(spec_path.read_text()) + overlay_path = document.get("overlay") + if overlay_path and not skip_overlay: + overlay = yaml.load((path.parent / overlay_path).resolve().read_text()) + spec = apply_overlay(spec, overlay) + return spec diff --git a/osc_sdk_python/credentials.py b/osc_sdk_python/credentials.py index 4070677..3d7983e 100644 --- a/osc_sdk_python/credentials.py +++ b/osc_sdk_python/credentials.py @@ -2,6 +2,8 @@ import os import warnings +from .exceptions import SdkConfigurationError + STD_PATH = os.path.join(os.path.expanduser("~"), ".osc/config.json") DEFAULT_REGION = "eu-west-2" DEFAULT_PROFILE = "default" @@ -19,8 +21,8 @@ def __init__(self, **kwargs): if kwargs: unexpected = ", ".join(f"'{k}'" for k in kwargs.keys()) - raise TypeError( - f"Endpoint() got unexpected keyword arguments: {unexpected}" + raise SdkConfigurationError( + "Endpoint() got unexpected keyword arguments: {}".format(unexpected) ) @@ -31,10 +33,6 @@ def __init__(self, **kwargs): self.access_key_v2: str = kwargs.pop("access_key_v2", None) self.secret_key_v2: str = kwargs.pop("secret_key_v2", None) self.iam_v2_services: list[str] = kwargs.pop("iam_v2_services", []) - self.x509_client_cert: str = kwargs.pop("x509_client_cert", None) - self.x509_client_cert_b64: str = kwargs.pop("x509_client_cert_b64", None) - self.x509_client_key: str = kwargs.pop("x509_client_key", None) - self.x509_client_key_b64: str = kwargs.pop("x509_client_key_b64", None) self.tls_skip_verify: bool = kwargs.pop("tls_skip_verify", False) self.login: str = kwargs.pop("login", None) or kwargs.pop("email", None) self.password: str = kwargs.pop("password", None) @@ -46,7 +44,9 @@ def __init__(self, **kwargs): if kwargs: unexpected = ", ".join(f"'{k}'" for k in kwargs.keys()) - raise TypeError(f"Profile() got unexpected keyword arguments: {unexpected}") + raise SdkConfigurationError( + "Profile() got unexpected keyword arguments: {}".format(unexpected) + ) @property def email(self) -> str: @@ -76,7 +76,7 @@ def get_default_endpoint(self, service: str) -> str: elif service == "directlink": return f"{self.protocol}://directlink.{self.region}.outscale.com" else: - raise ValueError("Unknown service") + raise SdkConfigurationError("Unknown service") @staticmethod def from_env() -> "Profile": @@ -95,12 +95,11 @@ def from_env() -> "Profile": "secret_key": os.environ.get("OSC_SECRET_KEY"), "access_key_v2": os.environ.get("OSC_ACCESS_KEY_V2"), "secret_key_v2": os.environ.get("OSC_SECRET_KEY_V2"), - "x509_client_cert": os.environ.get("OSC_X509_CLIENT_CERT"), - "x509_client_cert_b64": os.environ.get("OSC_X509_CLIENT_CERT_B64"), - "x509_client_key": os.environ.get("OSC_X509_CLIENT_KEY"), - "x509_client_key_b64": os.environ.get("OSC_X509_CLIENT_KEY_B64"), - "tls_skip_verify": os.environ.get("OSC_TLS_SKIP_VERIFY", "False").lower() - in ("true"), + "tls_skip_verify": ( + os.environ["OSC_TLS_SKIP_VERIFY"].lower() in ("true") + if "OSC_TLS_SKIP_VERIFY" in os.environ + else None + ), "login": os.environ.get("OSC_LOGIN"), "password": os.environ.get("OSC_PASSWORD"), "protocol": os.environ.get("OSC_PROTOCOL"), @@ -118,12 +117,28 @@ def from_env() -> "Profile": @staticmethod def __from_file(path: str, profile: str) -> "Profile": - with open(path) as f: - config = json.load(f) - kwargs_profile = config.get(profile) + try: + with open(path) as f: + config = json.load(f) + except Exception as error: + raise SdkConfigurationError( + "Could not load configuration file: {}".format(path) + ) from error + + kwargs_profile = config.get(profile) + if kwargs_profile is None: + raise SdkConfigurationError("Profile '{}' not found".format(profile)) + + try: kwargs_endpoints = kwargs_profile.get("endpoints", {}) kwargs_profile["endpoints"] = Endpoint(**kwargs_endpoints) return Profile(**kwargs_profile) + except SdkConfigurationError: + raise + except Exception as error: + raise SdkConfigurationError( + "Invalid profile '{}' in configuration file: {}".format(profile, path) + ) from error def merge(self, other: "Profile"): self.__dict__.update( @@ -139,10 +154,7 @@ def merge(self, other: "Profile"): @staticmethod def from_standard_configuration(path: str, profile: str) -> "Profile": - # 1. Load profile from environmental - merged_profile = Profile.from_env() - - # 2. Load additional config from environment + # 1. Resolve config path and profile name. if not profile: value = os.environ.get("OSC_PROFILE") if value: @@ -157,7 +169,8 @@ def from_standard_configuration(path: str, profile: str) -> "Profile": else: path = STD_PATH - # 3. Load profile for config file + # 2. Load profile from config file. + merged_profile = Profile() try: file_profile = Profile.__from_file(path, profile) merged_profile.merge(file_profile) @@ -165,7 +178,10 @@ def from_standard_configuration(path: str, profile: str) -> "Profile": if path != STD_PATH or profile != "default": raise e - # 4. Load default + # 3. Environment variables override config file values. + merged_profile.merge(Profile.from_env()) + + # 4. Apply SDK defaults for missing values. if not merged_profile.protocol: merged_profile.protocol = "https" diff --git a/osc_sdk_python/exceptions.py b/osc_sdk_python/exceptions.py new file mode 100644 index 0000000..642638c --- /dev/null +++ b/osc_sdk_python/exceptions.py @@ -0,0 +1,54 @@ +class SdkError(Exception): + """Base class for all public SDK exceptions.""" + + +class SdkUsageError(SdkError): + pass + + +class SdkConfigurationError(SdkError): + pass + + +class SdkValidationError(SdkError): + pass + + +class SdkOperationError(SdkValidationError): + pass + + +class SdkTransportError(SdkError): + def __init__(self, message, *, request=None, response=None): + super().__init__(message) + self.request = request + self.response = response + + +class SdkHttpError(SdkTransportError): + def __init__( + self, + message, + *, + status_code=None, + request=None, + response=None, + problem=None, + url=None, + ): + super().__init__(message, request=request, response=response) + self.status_code = status_code + self.problem = problem + self.url = url + + +class SdkClientError(SdkHttpError): + pass + + +class SdkServerError(SdkHttpError): + pass + + +class SdkResponseError(SdkError): + pass diff --git a/osc_sdk_python/generated/__init__.py b/osc_sdk_python/generated/__init__.py new file mode 100644 index 0000000..9c2c8c4 --- /dev/null +++ b/osc_sdk_python/generated/__init__.py @@ -0,0 +1,2 @@ +"""Generated typed SDK modules.""" + diff --git a/osc_sdk_python/generated/oks/__init__.py b/osc_sdk_python/generated/oks/__init__.py new file mode 100644 index 0000000..c97f220 --- /dev/null +++ b/osc_sdk_python/generated/oks/__init__.py @@ -0,0 +1,243 @@ +"""Generated typed SDK exports. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. +""" + +from .async_client import AsyncOksTypedMixin +from .models import ( + AccessKey, + AdmissionFlags, + AdmissionFlagsInput, + AdmissionPlugins, + AdmissionPluginsResponse, + AuthStrategy, + AutoMaintenances, + AutoUpgradeMaintenance, + CPSubregionsResponse, + Cluster, + ClusterInput, + ClusterInputTemplate, + ClusterResponse, + ClusterResponseList, + ClusterUpdate, + ControlPlanesResponse, + CreateClusterRequest, + CreateEimUserRequest, + CreateProjectRequest, + Cursor, + DeleteClusterRequest, + DeleteEimUserRequest, + DeleteProjectRequest, + DetailResponse, + DetailsResponse, + EimUser, + EimUserResponse, + EimUserType, + EimUserTypesResponse, + EimUsersResponse, + EnryptedResponse, + ErrorItem, + ErrorResponse, + GetAdmissionPluginsRequest, + GetCPSubregionsRequest, + GetClientIPRequest, + GetClusterRequest, + GetClusterTemplateRequest, + GetControlPlanePlansRequest, + GetEimUserTypesRequest, + GetEimUsersRequest, + GetKubeconfigRequest, + GetKubeconfigWithPubkeyNACLRequest, + GetKubernetesVersionsRequest, + GetNetPeeringAcceptanceTemplateRequest, + GetNetPeeringRequestTemplateRequest, + GetNodepoolTemplateRequest, + GetProjectNetsRequest, + GetProjectPublicIpsRequest, + GetProjectQuotasRequest, + GetProjectRequest, + GetProjectSnapshotsRequest, + GetProjectTemplateRequest, + GetQuotasRequest, + IPDetails, + IPResponse, + KubeconfigData, + KubeconfigResponse, + KubernetesVersionsResponse, + ListAllClustersRequest, + ListClustersByProjectIDRequest, + ListProjectsRequest, + Maintenance, + MaintenanceWindow, + Net, + NetPeeringAcceptance, + NetPeeringRequest, + NetSpecific, + NetsResponse, + Nodepool, + OKSQuotas, + Offset, + OpenIdConnectConfig, + Pagination, + PermissionsOnResource, + Project, + ProjectInput, + ProjectResponse, + ProjectResponseList, + ProjectUpdate, + PublicIp, + PublicIpsResponse, + Quotas, + QuotasData, + ResourceTag, + ResponseContext_Input, + Snapshot, + SnapshotsResponse, + Spec, + SpecNetPeeringAcceptance, + SpecNetPeeringRequest, + Statuses, + Subregion, + TemplateResponse_ClusterInputTemplate, + TemplateResponse_NetPeeringAcceptance, + TemplateResponse_NetPeeringRequest, + TemplateResponse_Nodepool, + TemplateResponse_ProjectInput, + UpdateClusterRequest, + UpdateProjectRequest, + UpgradeClusterRequest, + UpgradeStrategy, + ValidationDetail, + Volume, + clusters__cluster_schema__RPCResponse, + clusters__cluster_schema__ResponseContext, + myip__myip_schema__ResponseContext, + netpeerings__netpeering_schema__Metadata, + nodepools__nodepool_schema__Metadata, + projects__project_schema__QuotasResponse, + projects__project_schema__RPCResponse, + projects__project_schema__ResponseContext, + quotas__quota_schema__QuotasResponse, + quotas__quota_schema__ResponseContext, + templates__template_schema__ResponseContext, +) + +__all__ = [ + "AsyncOksTypedMixin", + 'AccessKey', + 'AdmissionFlags', + 'AdmissionFlagsInput', + 'AdmissionPlugins', + 'AdmissionPluginsResponse', + 'AuthStrategy', + 'AutoMaintenances', + 'AutoUpgradeMaintenance', + 'CPSubregionsResponse', + 'Cluster', + 'ClusterInput', + 'ClusterInputTemplate', + 'ClusterResponse', + 'ClusterResponseList', + 'ClusterUpdate', + 'ControlPlanesResponse', + 'CreateClusterRequest', + 'CreateEimUserRequest', + 'CreateProjectRequest', + 'Cursor', + 'DeleteClusterRequest', + 'DeleteEimUserRequest', + 'DeleteProjectRequest', + 'DetailResponse', + 'DetailsResponse', + 'EimUser', + 'EimUserResponse', + 'EimUserType', + 'EimUserTypesResponse', + 'EimUsersResponse', + 'EnryptedResponse', + 'ErrorItem', + 'ErrorResponse', + 'GetAdmissionPluginsRequest', + 'GetCPSubregionsRequest', + 'GetClientIPRequest', + 'GetClusterRequest', + 'GetClusterTemplateRequest', + 'GetControlPlanePlansRequest', + 'GetEimUserTypesRequest', + 'GetEimUsersRequest', + 'GetKubeconfigRequest', + 'GetKubeconfigWithPubkeyNACLRequest', + 'GetKubernetesVersionsRequest', + 'GetNetPeeringAcceptanceTemplateRequest', + 'GetNetPeeringRequestTemplateRequest', + 'GetNodepoolTemplateRequest', + 'GetProjectNetsRequest', + 'GetProjectPublicIpsRequest', + 'GetProjectQuotasRequest', + 'GetProjectRequest', + 'GetProjectSnapshotsRequest', + 'GetProjectTemplateRequest', + 'GetQuotasRequest', + 'IPDetails', + 'IPResponse', + 'KubeconfigData', + 'KubeconfigResponse', + 'KubernetesVersionsResponse', + 'ListAllClustersRequest', + 'ListClustersByProjectIDRequest', + 'ListProjectsRequest', + 'Maintenance', + 'MaintenanceWindow', + 'Net', + 'NetPeeringAcceptance', + 'NetPeeringRequest', + 'NetSpecific', + 'NetsResponse', + 'Nodepool', + 'OKSQuotas', + 'Offset', + 'OpenIdConnectConfig', + 'Pagination', + 'PermissionsOnResource', + 'Project', + 'ProjectInput', + 'ProjectResponse', + 'ProjectResponseList', + 'ProjectUpdate', + 'PublicIp', + 'PublicIpsResponse', + 'Quotas', + 'QuotasData', + 'ResourceTag', + 'ResponseContext_Input', + 'Snapshot', + 'SnapshotsResponse', + 'Spec', + 'SpecNetPeeringAcceptance', + 'SpecNetPeeringRequest', + 'Statuses', + 'Subregion', + 'TemplateResponse_ClusterInputTemplate', + 'TemplateResponse_NetPeeringAcceptance', + 'TemplateResponse_NetPeeringRequest', + 'TemplateResponse_Nodepool', + 'TemplateResponse_ProjectInput', + 'UpdateClusterRequest', + 'UpdateProjectRequest', + 'UpgradeClusterRequest', + 'UpgradeStrategy', + 'ValidationDetail', + 'Volume', + 'clusters__cluster_schema__RPCResponse', + 'clusters__cluster_schema__ResponseContext', + 'myip__myip_schema__ResponseContext', + 'netpeerings__netpeering_schema__Metadata', + 'nodepools__nodepool_schema__Metadata', + 'projects__project_schema__QuotasResponse', + 'projects__project_schema__RPCResponse', + 'projects__project_schema__ResponseContext', + 'quotas__quota_schema__QuotasResponse', + 'quotas__quota_schema__ResponseContext', + 'templates__template_schema__ResponseContext', +] diff --git a/osc_sdk_python/generated/oks/async_client.py b/osc_sdk_python/generated/oks/async_client.py new file mode 100644 index 0000000..53a87e8 --- /dev/null +++ b/osc_sdk_python/generated/oks/async_client.py @@ -0,0 +1,1011 @@ +"""Generated typed OKS client slice. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. + +Do not edit by hand. Regenerate with: + python -m osc_sdk_python.codegen.generator + + python -m osc_sdk_python.codegen.generator oks osc +""" + +from typing import Any + +from pydantic import TypeAdapter, ValidationError + +from osc_sdk_python.exceptions import SdkResponseError, SdkValidationError +from osc_sdk_python.runtime.request import RequestSpec +from .models import ( + AdmissionPluginsResponse, + CPSubregionsResponse, + ClusterResponse, + ClusterResponseList, + ControlPlanesResponse, + CreateClusterRequest, + CreateEimUserRequest, + CreateProjectRequest, + DeleteClusterRequest, + DeleteEimUserRequest, + DeleteProjectRequest, + DetailResponse, + DetailsResponse, + EimUserResponse, + EimUserTypesResponse, + EimUsersResponse, + EnryptedResponse, + GetAdmissionPluginsRequest, + GetCPSubregionsRequest, + GetClientIPRequest, + GetClusterRequest, + GetClusterTemplateRequest, + GetControlPlanePlansRequest, + GetEimUserTypesRequest, + GetEimUsersRequest, + GetKubeconfigRequest, + GetKubeconfigWithPubkeyNACLRequest, + GetKubernetesVersionsRequest, + GetNetPeeringAcceptanceTemplateRequest, + GetNetPeeringRequestTemplateRequest, + GetNodepoolTemplateRequest, + GetProjectNetsRequest, + GetProjectPublicIpsRequest, + GetProjectQuotasRequest, + GetProjectRequest, + GetProjectSnapshotsRequest, + GetProjectTemplateRequest, + GetQuotasRequest, + IPResponse, + KubeconfigResponse, + KubernetesVersionsResponse, + ListAllClustersRequest, + ListClustersByProjectIDRequest, + ListProjectsRequest, + NetsResponse, + ProjectResponse, + ProjectResponseList, + PublicIpsResponse, + SnapshotsResponse, + TemplateResponse_ClusterInputTemplate, + TemplateResponse_NetPeeringAcceptance, + TemplateResponse_NetPeeringRequest, + TemplateResponse_Nodepool, + TemplateResponse_ProjectInput, + UpdateClusterRequest, + UpdateProjectRequest, + UpgradeClusterRequest, + projects__project_schema__QuotasResponse, + quotas__quota_schema__QuotasResponse, +) + + +def _dump_json_body(value: Any) -> Any: + if hasattr(value, "model_dump"): + return value.model_dump(exclude_none=True, by_alias=True) + return value + + +def _validate_request(model: type, value: Any) -> Any: + try: + if value is None: + return model() + if isinstance(value, model): + return value + return TypeAdapter(model).validate_python(value) + except ValidationError as error: + raise SdkValidationError(str(error)) from error + + +def _validate_response(model: type, value: Any) -> Any: + try: + return TypeAdapter(model).validate_python(value) + except ValidationError as error: + raise SdkResponseError(str(error)) from error + + +class AsyncOksTypedMixin: + async def list_projects( + self, + request: ListProjectsRequest | None = None, + ) -> ProjectResponseList: + request = _validate_request(ListProjectsRequest, request) + + path_params = { + } + query_params = { + 'name': request.name, + 'status': request.status, + 'cidr': request.cidr, + 'deleted': request.deleted, + 'cursor': request.cursor, + 'page': request.page, + 'limit': request.limit, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ProjectResponseList, response) + + async def create_project( + self, + request: CreateProjectRequest | None = None, + ) -> ProjectResponse: + request = _validate_request(CreateProjectRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="POST", + path="/projects", + json_body=_dump_json_body(request.body), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ProjectResponse, response) + + async def get_project( + self, + request: GetProjectRequest | None = None, + ) -> ProjectResponse: + request = _validate_request(GetProjectRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ProjectResponse, response) + + async def update_project( + self, + request: UpdateProjectRequest | None = None, + ) -> ProjectResponse: + request = _validate_request(UpdateProjectRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="PATCH", + path="/projects/{project_id}", + json_body=_dump_json_body(request.body), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ProjectResponse, response) + + async def delete_project( + self, + request: DeleteProjectRequest | None = None, + ) -> DetailResponse: + request = _validate_request(DeleteProjectRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="DELETE", + path="/projects/{project_id}", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DetailResponse, response) + + async def get_project_quotas( + self, + request: GetProjectQuotasRequest | None = None, + ) -> projects__project_schema__QuotasResponse: + request = _validate_request(GetProjectQuotasRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/quotas", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(projects__project_schema__QuotasResponse, response) + + async def get_project_snapshots( + self, + request: GetProjectSnapshotsRequest | None = None, + ) -> SnapshotsResponse: + request = _validate_request(GetProjectSnapshotsRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/snapshots", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(SnapshotsResponse, response) + + async def get_project_public_ips( + self, + request: GetProjectPublicIpsRequest | None = None, + ) -> PublicIpsResponse: + request = _validate_request(GetProjectPublicIpsRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/public_ips", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(PublicIpsResponse, response) + + async def get_project_nets( + self, + request: GetProjectNetsRequest | None = None, + ) -> NetsResponse: + request = _validate_request(GetProjectNetsRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/nets", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(NetsResponse, response) + + async def get_eim_users( + self, + request: GetEimUsersRequest | None = None, + ) -> EimUsersResponse: + request = _validate_request(GetEimUsersRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/eim_users", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(EimUsersResponse, response) + + async def create_eim_user( + self, + request: CreateEimUserRequest | None = None, + ) -> EimUserResponse | EnryptedResponse: + request = _validate_request(CreateEimUserRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + 'user': request.user, + 'ttl': request.ttl, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="POST", + path="/projects/{project_id}/eim_users", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(EimUserResponse | EnryptedResponse, response) + + async def get_eim_user_types( + self, + request: GetEimUserTypesRequest | None = None, + ) -> EimUserTypesResponse: + request = _validate_request(GetEimUserTypesRequest, request) + + path_params = { + 'project_id': request.project_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/eim_users/types", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(EimUserTypesResponse, response) + + async def delete_eim_user( + self, + request: DeleteEimUserRequest | None = None, + ) -> DetailsResponse: + request = _validate_request(DeleteEimUserRequest, request) + + path_params = { + 'project_id': request.project_id, + 'user': request.user, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="DELETE", + path="/projects/{project_id}/eim_users/{user}", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DetailsResponse, response) + + async def list_clusters_by_project_id( + self, + request: ListClustersByProjectIDRequest | None = None, + ) -> ClusterResponseList: + request = _validate_request(ListClustersByProjectIDRequest, request) + + path_params = { + } + query_params = { + 'project_id': request.project_id, + 'name': request.name, + 'status': request.status, + 'version': request.version, + 'deleted': request.deleted, + 'cursor': request.cursor, + 'page': request.page, + 'limit': request.limit, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ClusterResponseList, response) + + async def create_cluster( + self, + request: CreateClusterRequest | None = None, + ) -> ClusterResponse: + request = _validate_request(CreateClusterRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="POST", + path="/clusters", + json_body=_dump_json_body(request.body), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ClusterResponse, response) + + async def list_all_clusters( + self, + request: ListAllClustersRequest | None = None, + ) -> ClusterResponseList: + request = _validate_request(ListAllClustersRequest, request) + + path_params = { + } + query_params = { + 'name': request.name, + 'status': request.status, + 'version': request.version, + 'deleted': request.deleted, + 'cursor': request.cursor, + 'page': request.page, + 'limit': request.limit, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/all", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ClusterResponseList, response) + + async def get_cluster( + self, + request: GetClusterRequest | None = None, + ) -> ClusterResponse: + request = _validate_request(GetClusterRequest, request) + + path_params = { + 'cluster_id': request.cluster_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/{cluster_id}", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ClusterResponse, response) + + async def update_cluster( + self, + request: UpdateClusterRequest | None = None, + ) -> ClusterResponse: + request = _validate_request(UpdateClusterRequest, request) + + path_params = { + 'cluster_id': request.cluster_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="PATCH", + path="/clusters/{cluster_id}", + json_body=_dump_json_body(request.body), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ClusterResponse, response) + + async def delete_cluster( + self, + request: DeleteClusterRequest | None = None, + ) -> DetailResponse: + request = _validate_request(DeleteClusterRequest, request) + + path_params = { + 'cluster_id': request.cluster_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="DELETE", + path="/clusters/{cluster_id}", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DetailResponse, response) + + async def get_kubeconfig( + self, + request: GetKubeconfigRequest | None = None, + ) -> KubeconfigResponse: + request = _validate_request(GetKubeconfigRequest, request) + + path_params = { + 'cluster_id': request.cluster_id, + } + query_params = { + 'user': request.user, + 'group': request.group, + 'ttl': request.ttl, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/{cluster_id}/kubeconfig", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(KubeconfigResponse, response) + + async def get_kubeconfig_with_pubkey_nacl( + self, + request: GetKubeconfigWithPubkeyNACLRequest | None = None, + ) -> KubeconfigResponse: + request = _validate_request(GetKubeconfigWithPubkeyNACLRequest, request) + + path_params = { + 'cluster_id': request.cluster_id, + } + query_params = { + 'user': request.user, + 'group': request.group, + 'ttl': request.ttl, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="POST", + path="/clusters/{cluster_id}/kubeconfig", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(KubeconfigResponse, response) + + async def upgrade_cluster( + self, + request: UpgradeClusterRequest | None = None, + ) -> ClusterResponse: + request = _validate_request(UpgradeClusterRequest, request) + + path_params = { + 'cluster_id': request.cluster_id, + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="PATCH", + path="/clusters/{cluster_id}/upgrade", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ClusterResponse, response) + + async def get_kubernetes_versions( + self, + request: GetKubernetesVersionsRequest | None = None, + ) -> KubernetesVersionsResponse: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/limits/kubernetes_versions", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(KubernetesVersionsResponse, response) + + async def get_cp_subregions( + self, + request: GetCPSubregionsRequest | None = None, + ) -> CPSubregionsResponse: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/limits/cp_subregions", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CPSubregionsResponse, response) + + async def get_control_plane_plans( + self, + request: GetControlPlanePlansRequest | None = None, + ) -> ControlPlanesResponse: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/limits/control_plane_plans", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ControlPlanesResponse, response) + + async def get_admission_plugins( + self, + request: GetAdmissionPluginsRequest | None = None, + ) -> AdmissionPluginsResponse: + request = _validate_request(GetAdmissionPluginsRequest, request) + + path_params = { + } + query_params = { + 'version': request.version, + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/clusters/limits/admission_plugins", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(AdmissionPluginsResponse, response) + + async def get_project_template( + self, + request: GetProjectTemplateRequest | None = None, + ) -> TemplateResponse_ProjectInput: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/templates/project", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(TemplateResponse_ProjectInput, response) + + async def get_cluster_template( + self, + request: GetClusterTemplateRequest | None = None, + ) -> TemplateResponse_ClusterInputTemplate: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/templates/cluster", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(TemplateResponse_ClusterInputTemplate, response) + + async def get_nodepool_template( + self, + request: GetNodepoolTemplateRequest | None = None, + ) -> TemplateResponse_Nodepool: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/templates/nodepool", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(TemplateResponse_Nodepool, response) + + async def get_net_peering_request_template( + self, + request: GetNetPeeringRequestTemplateRequest | None = None, + ) -> TemplateResponse_NetPeeringRequest: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/templates/netpeeringrequest", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(TemplateResponse_NetPeeringRequest, response) + + async def get_net_peering_acceptance_template( + self, + request: GetNetPeeringAcceptanceTemplateRequest | None = None, + ) -> TemplateResponse_NetPeeringAcceptance: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/templates/netpeeringacceptance", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(TemplateResponse_NetPeeringAcceptance, response) + + async def get_quotas( + self, + request: GetQuotasRequest | None = None, + ) -> quotas__quota_schema__QuotasResponse: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/quotas", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(quotas__quota_schema__QuotasResponse, response) + + async def get_client_ip( + self, + request: GetClientIPRequest | None = None, + ) -> IPResponse: + _ = request + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="oks", + method="GET", + path="/myip", + json_body=None, + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(IPResponse, response) diff --git a/osc_sdk_python/generated/oks/models.py b/osc_sdk_python/generated/oks/models.py new file mode 100644 index 0000000..b2548a0 --- /dev/null +++ b/osc_sdk_python/generated/oks/models.py @@ -0,0 +1,603 @@ +"""Generated typed OKS client slice. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. + +Do not edit by hand. Regenerate with: + python -m osc_sdk_python.codegen.generator + + python -m osc_sdk_python.codegen.generator oks osc +""" +from __future__ import annotations + +import datetime + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + + +class GeneratedModel(BaseModel): + model_config = ConfigDict(populate_by_name=True, extra="allow") + + +class AccessKey(GeneratedModel): + state: Literal['ACTIVE', 'INACTIVE'] = Field(alias='State') + access_key_id: str = Field(alias='AccessKeyId') + creation_date: str = Field(alias='CreationDate') + expiration_date: str | None = Field(default=None, alias='ExpirationDate') + secret_key: str | None = Field(default=None, alias='SecretKey') + +class AdmissionFlags(GeneratedModel): + disable_admission_plugins: list[str] | None = Field(default=None, alias='disable_admission_plugins') + enable_admission_plugins: list[str] | None = Field(default=None, alias='enable_admission_plugins') + applied_admission_plugins: list[str] | None = Field(default=None, alias='applied_admission_plugins') + +class AdmissionFlagsInput(GeneratedModel): + disable_admission_plugins: list[str] | None = Field(default=None, alias='disable_admission_plugins') + enable_admission_plugins: list[str] | None = Field(default=None, alias='enable_admission_plugins') + +class AdmissionPlugins(GeneratedModel): + enable_admission_plugins: list[str] = Field(alias='EnableAdmissionPlugins') + disable_admission_plugins: list[str] = Field(alias='DisableAdmissionPlugins') + default_admission_plugins: list[str] = Field(alias='DefaultAdmissionPlugins') + +class AdmissionPluginsResponse(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + admission_plugins: AdmissionPlugins = Field(alias='AdmissionPlugins') + +class AuthStrategy(GeneratedModel): + oidc: OpenIdConnectConfig | None = Field(default=None, alias='oidc') + +class AutoMaintenances(GeneratedModel): + minor_upgrade_maintenance: MaintenanceWindow = Field(alias='minor_upgrade_maintenance') + patch_upgrade_maintenance: MaintenanceWindow = Field(alias='patch_upgrade_maintenance') + +class AutoUpgradeMaintenance(GeneratedModel): + duration_hours: int = Field(alias='durationHours') + start_hour: int = Field(alias='startHour') + week_day: Literal['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] = Field(alias='weekDay') + +class CPSubregionsResponse(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + cp_subregions: list[str] = Field(alias='CPSubregions') + +class Cluster(GeneratedModel): + project_id: str = Field(alias='project_id') + id: str = Field(alias='id') + name: str = Field(alias='name') + description: str | None = Field(default=None, alias='description') + cp_multi_az: bool = Field(alias='cp_multi_az') + cp_subregions: list[str] = Field(alias='cp_subregions') + version: str = Field(alias='version') + expected_version: str | None = Field(default=None, alias='expected_version') + cni: str = Field(alias='cni') + admin_lbu: bool = Field(alias='admin_lbu') + admission_flags: AdmissionFlags = Field(alias='admission_flags') + cidr_pods: str = Field(alias='cidr_pods') + cidr_service: str = Field(alias='cidr_service') + cluster_dns: str = Field(alias='cluster_dns') + tags: dict[str, str] = Field(alias='tags') + auto_maintenances: AutoMaintenances | None = Field(default=None, alias='auto_maintenances') + maintenance_window: Maintenance | None = Field(default=None, alias='maintenance_window') + control_planes: str = Field(alias='control_planes') + expected_control_planes: str | None = Field(default=None, alias='expected_control_planes') + admin_whitelist: list[str] = Field(alias='admin_whitelist') + statuses: Statuses = Field(alias='statuses') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + auth: AuthStrategy | None = Field(default=None, alias='auth') + +class ClusterInput(GeneratedModel): + name: str = Field(alias='name') + project_id: str = Field(alias='project_id') + description: str | None = Field(default=None, alias='description') + cp_multi_az: bool | None = Field(default=None, alias='cp_multi_az') + cp_subregions: list[str] | None = Field(default=None, alias='cp_subregions') + version: str = Field(alias='version') + admin_lbu: bool | None = Field(default=None, alias='admin_lbu') + admission_flags: AdmissionFlagsInput | None = Field(default=None, alias='admission_flags') + cni: str | None = Field(default=None, alias='cni') + cidr_pods: str = Field(alias='cidr_pods') + cidr_service: str = Field(alias='cidr_service') + cluster_dns: str | None = Field(default=None, alias='cluster_dns') + tags: dict[str, str] | None = Field(default=None, alias='tags') + auto_maintenances: AutoMaintenances | None = Field(default=None, alias='auto_maintenances') + maintenance_window: Maintenance | None = Field(default=None, alias='maintenance_window') + control_planes: str | None = Field(default=None, alias='control_planes') + admin_whitelist: list[str] = Field(alias='admin_whitelist') + quirks: list[str] | None = Field(default=None, alias='quirks') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + auth: AuthStrategy | None = Field(default=None, alias='auth') + +class ClusterInputTemplate(GeneratedModel): + project_id: str = Field(alias='project_id') + description: str | None = Field(default=None, alias='description') + version: str = Field(alias='version') + admin_lbu: bool | None = Field(default=None, alias='admin_lbu') + admission_flags: AdmissionFlagsInput | None = Field(default=None, alias='admission_flags') + cidr_pods: str | None = Field(default=None, alias='cidr_pods') + cidr_service: str | None = Field(default=None, alias='cidr_service') + cluster_dns: str | None = Field(default=None, alias='cluster_dns') + tags: dict[str, str] | None = Field(default=None, alias='tags') + auto_maintenances: AutoMaintenances | None = Field(default=None, alias='auto_maintenances') + maintenance_window: Maintenance | None = Field(default=None, alias='maintenance_window') + control_planes: str | None = Field(default=None, alias='control_planes') + admin_whitelist: list[str] = Field(alias='admin_whitelist') + quirks: list[str] | None = Field(default=None, alias='quirks') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + +class ClusterResponse(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + cluster: Cluster = Field(alias='Cluster') + +class ClusterResponseList(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + pagination: Pagination = Field(alias='Pagination') + clusters: list[Cluster] = Field(alias='Clusters') + +class ClusterUpdate(GeneratedModel): + description: str | None = Field(default=None, alias='description') + admission_flags: AdmissionFlagsInput | None = Field(default=None, alias='admission_flags') + tags: dict[str, str] | None = Field(default=None, alias='tags') + auto_maintenances: AutoMaintenances | None = Field(default=None, alias='auto_maintenances') + maintenance_window: Maintenance | None = Field(default=None, alias='maintenance_window') + admin_whitelist: list[str] | None = Field(default=None, alias='admin_whitelist') + quirks: list[str] | None = Field(default=None, alias='quirks') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + version: str | None = Field(default=None, alias='version') + control_planes: str | None = Field(default=None, alias='control_planes') + auth: AuthStrategy | None = Field(default=None, alias='auth') + +class ControlPlanesResponse(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + control_planes: list[str] = Field(alias='ControlPlanes') + +class Cursor(GeneratedModel): + next_cursor: str | None = Field(default=None, alias='next_cursor') + +class DetailResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + detail: str = Field(alias='detail') + +class DetailsResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + details: str = Field(alias='Details') + +class EimUser(GeneratedModel): + user_name: str = Field(alias='UserName') + access_keys: list[AccessKey] | None = Field(default=None, alias='AccessKeys') + +class EimUserResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + eim_user: EimUser = Field(alias='EimUser') + +class EimUserType(GeneratedModel): + user_type: str = Field(alias='UserType') + description: str | None = Field(alias='Description') + +class EimUserTypesResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + eim_user_types: list[EimUserType] = Field(alias='EimUserTypes') + +class EimUsersResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + eim_users: list[EimUser] | None = Field(default=None, alias='EimUsers') + +class EnryptedResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + data: str = Field(alias='Data') + +class ErrorItem(GeneratedModel): + type: str = Field(alias='Type') + details: str | list[ValidationDetail] = Field(alias='Details') + code: str = Field(alias='Code') + +class ErrorResponse(GeneratedModel): + errors: list[ErrorItem] = Field(alias='Errors') + response_context: ResponseContext_Input = Field(alias='ResponseContext') + +class IPDetails(GeneratedModel): + x_real_ip: str | None = Field(default=None, alias='x_real_ip') + +class IPResponse(GeneratedModel): + response_context: myip__myip_schema__ResponseContext = Field(alias='ResponseContext') + ip: IPDetails = Field(alias='IP') + +class KubeconfigData(GeneratedModel): + kubeconfig: str = Field(alias='kubeconfig') + +class KubeconfigResponse(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + cluster: clusters__cluster_schema__RPCResponse = Field(alias='Cluster') + +class KubernetesVersionsResponse(GeneratedModel): + response_context: clusters__cluster_schema__ResponseContext = Field(alias='ResponseContext') + versions: list[str] = Field(alias='Versions') + +class Maintenance(GeneratedModel): + duration_hours: int = Field(alias='duration_hours') + start_hour: int = Field(alias='start_hour') + week_day: Literal['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'string'] = Field(alias='week_day') + tz: str | None = Field(default=None, alias='tz') + +class MaintenanceWindow(GeneratedModel): + enabled: bool | None = Field(default=None, alias='enabled') + duration_hours: int | None = Field(default=None, alias='duration_hours') + start_hour: int | None = Field(default=None, alias='start_hour') + week_day: Literal['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'string'] | None = Field(default=None, alias='week_day') + tz: str | None = Field(default=None, alias='tz') + +class Net(GeneratedModel): + dhcp_options_set_id: str = Field(alias='DhcpOptionsSetId') + ip_range: str = Field(alias='IpRange') + net_id: str = Field(alias='NetId') + state: str = Field(alias='State') + tenancy: str = Field(alias='Tenancy') + +class NetPeeringAcceptance(GeneratedModel): + api_version: str = Field(alias='apiVersion') + kind: str = Field(alias='kind') + metadata: netpeerings__netpeering_schema__Metadata = Field(alias='metadata') + spec: SpecNetPeeringAcceptance = Field(alias='spec') + +class NetPeeringRequest(GeneratedModel): + api_version: str = Field(alias='apiVersion') + kind: str = Field(alias='kind') + metadata: netpeerings__netpeering_schema__Metadata = Field(alias='metadata') + spec: SpecNetPeeringRequest = Field(alias='spec') + +class NetSpecific(GeneratedModel): + disable_lan_security_groups: bool = Field(alias='disable_lan_security_groups') + +class NetsResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + nets: list[Net] = Field(alias='Nets') + +class Nodepool(GeneratedModel): + api_version: str = Field(alias='apiVersion') + kind: str = Field(alias='kind') + metadata: nodepools__nodepool_schema__Metadata = Field(alias='metadata') + spec: Spec = Field(alias='spec') + +class OKSQuotas(GeneratedModel): + projects: int = Field(alias='Projects') + clusters_per_project: int = Field(alias='ClustersPerProject') + kube_versions: list[str] = Field(alias='KubeVersions') + cp_subregions: list[str] = Field(alias='CPSubregions') + +class Offset(GeneratedModel): + page: int | None = Field(default=None, alias='page') + limit: int | None = Field(default=None, alias='limit') + total: int | None = Field(default=None, alias='total') + +class OpenIdConnectConfig(GeneratedModel): + issuer_url: str = Field(alias='issuer-url') + client_id: str = Field(alias='client-id') + username_claim: str | None = Field(default=None, alias='username-claim') + username_prefix: str | None = Field(default=None, alias='username-prefix') + groups_claim: list[str] | None = Field(default=None, alias='groups-claim') + groups_prefix: str | None = Field(default=None, alias='groups-prefix') + required_claim: dict[str, str | bool | int | float] | None = Field(default=None, alias='required-claim') + +class Pagination(GeneratedModel): + cursor: Cursor | None = Field(default=None, alias='cursor') + offset: Offset | None = Field(default=None, alias='offset') + +class PermissionsOnResource(GeneratedModel): + global_permission: int = Field(alias='GlobalPermission') + account_ids: list[str] = Field(alias='AccountIds') + +class Project(GeneratedModel): + id: str = Field(alias='id') + name: str = Field(alias='name') + description: str | None = Field(default=None, alias='description') + cidr: str = Field(alias='cidr') + region: str = Field(alias='region') + status: str = Field(alias='status') + tags: dict[str, str] = Field(alias='tags') + net_specific: NetSpecific | None = Field(default=None, alias='net_specific') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + created_at: datetime.datetime = Field(alias='created_at') + updated_at: datetime.datetime = Field(alias='updated_at') + deleted_at: datetime.datetime | None = Field(default=None, alias='deleted_at') + +class ProjectInput(GeneratedModel): + name: str = Field(alias='name') + description: str | None = Field(default=None, alias='description') + cidr: str = Field(alias='cidr') + region: str = Field(alias='region') + tags: dict[str, str] | None = Field(default=None, alias='tags') + net_specific: NetSpecific | None = Field(default=None, alias='net_specific') + quirks: list[str] | None = Field(default=None, alias='quirks') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + +class ProjectResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + project: Project = Field(alias='Project') + +class ProjectResponseList(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + pagination: Pagination = Field(alias='Pagination') + projects: list[Project] = Field(alias='Projects') + +class ProjectUpdate(GeneratedModel): + description: str | None = Field(default=None, alias='description') + tags: dict[str, str] | None = Field(default=None, alias='tags') + quirks: list[str] | None = Field(default=None, alias='quirks') + disable_api_termination: bool | None = Field(default=None, alias='disable_api_termination') + +class PublicIp(GeneratedModel): + tags: list[ResourceTag] = Field(alias='Tags') + public_ip: str = Field(alias='PublicIp') + public_ip_id: str = Field(alias='PublicIpId') + +class PublicIpsResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + public_ips: list[PublicIp] = Field(alias='PublicIps') + +class Quotas(GeneratedModel): + short_description: str = Field(alias='ShortDescription') + quota_collection: str = Field(alias='QuotaCollection') + account_id: str = Field(alias='AccountId') + description: str = Field(alias='Description') + max_value: int = Field(alias='MaxValue') + used_value: int = Field(alias='UsedValue') + name: str = Field(alias='Name') + +class QuotasData(GeneratedModel): + quotas: list[Quotas] = Field(alias='quotas') + subregions: list[Subregion] = Field(alias='subregions') + +class ResourceTag(GeneratedModel): + key: str = Field(alias='Key') + value: str = Field(alias='Value') + +class ResponseContext_Input(GeneratedModel): + request_id: str = Field(alias='RequestId') + +class Snapshot(GeneratedModel): + volume_size: int = Field(alias='VolumeSize') + account_id: str = Field(alias='AccountId') + volume_id: str = Field(alias='VolumeId') + creation_date: str = Field(alias='CreationDate') + progress: int = Field(alias='Progress') + snapshot_id: str = Field(alias='SnapshotId') + state: str = Field(alias='State') + description: str = Field(alias='Description') + tags: list[ResourceTag] = Field(alias='Tags') + permissions_to_create_volume: PermissionsOnResource = Field(alias='PermissionsToCreateVolume') + +class SnapshotsResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + snapshots: list[Snapshot] = Field(alias='Snapshots') + +class Spec(GeneratedModel): + desired_nodes: str = Field(alias='desiredNodes') + node_type: str = Field(alias='nodeType') + zones: list[str] = Field(alias='zones') + volumes: list[Volume] = Field(alias='volumes') + upgrade_strategy: UpgradeStrategy = Field(alias='upgradeStrategy') + auto_healing: bool = Field(alias='autoHealing') + +class SpecNetPeeringAcceptance(GeneratedModel): + net_peering_id: str = Field(alias='netPeeringId') + +class SpecNetPeeringRequest(GeneratedModel): + accepter_net_id: str = Field(alias='accepterNetId') + accepter_owner_id: str = Field(alias='accepterOwnerId') + +class Statuses(GeneratedModel): + created_at: datetime.datetime = Field(alias='created_at') + deleted_at: datetime.datetime | None = Field(default=None, alias='deleted_at') + updated_at: datetime.datetime | None = Field(default=None, alias='updated_at') + status: str | None = Field(default=None, alias='status') + available_upgrade: str | None = Field(default=None, alias='available_upgrade') + +class Subregion(GeneratedModel): + state: str = Field(alias='State') + region_name: str = Field(alias='RegionName') + subregion_name: str = Field(alias='SubregionName') + location_code: str = Field(alias='LocationCode') + +class TemplateResponse_ClusterInputTemplate(GeneratedModel): + response_context: templates__template_schema__ResponseContext = Field(alias='ResponseContext') + template: ClusterInputTemplate = Field(alias='Template') + +class TemplateResponse_NetPeeringAcceptance(GeneratedModel): + response_context: templates__template_schema__ResponseContext = Field(alias='ResponseContext') + template: NetPeeringAcceptance = Field(alias='Template') + +class TemplateResponse_NetPeeringRequest(GeneratedModel): + response_context: templates__template_schema__ResponseContext = Field(alias='ResponseContext') + template: NetPeeringRequest = Field(alias='Template') + +class TemplateResponse_Nodepool(GeneratedModel): + response_context: templates__template_schema__ResponseContext = Field(alias='ResponseContext') + template: Nodepool = Field(alias='Template') + +class TemplateResponse_ProjectInput(GeneratedModel): + response_context: templates__template_schema__ResponseContext = Field(alias='ResponseContext') + template: ProjectInput = Field(alias='Template') + +class UpgradeStrategy(GeneratedModel): + max_unavailable: int = Field(alias='maxUnavailable') + max_surge: int = Field(alias='maxSurge') + auto_upgrade_enabled: bool = Field(alias='autoUpgradeEnabled') + auto_upgrade_maintenance: AutoUpgradeMaintenance = Field(alias='autoUpgradeMaintenance') + +class ValidationDetail(GeneratedModel): + loc: list[str | int] = Field(alias='loc') + msg: str = Field(alias='msg') + type: str = Field(alias='type') + +class Volume(GeneratedModel): + device: str = Field(alias='device') + type: str = Field(alias='type') + size: int = Field(alias='size') + dir: str = Field(alias='dir') + +class clusters__cluster_schema__RPCResponse(GeneratedModel): + request_id: str = Field(alias='request_id') + data: KubeconfigData = Field(alias='data') + +class clusters__cluster_schema__ResponseContext(GeneratedModel): + request_id: str = Field(alias='RequestId') + +class myip__myip_schema__ResponseContext(GeneratedModel): + request_id: str = Field(alias='RequestId') + +class netpeerings__netpeering_schema__Metadata(GeneratedModel): + name: str = Field(alias='name') + +class nodepools__nodepool_schema__Metadata(GeneratedModel): + name: str = Field(alias='name') + +class projects__project_schema__QuotasResponse(GeneratedModel): + response_context: projects__project_schema__ResponseContext = Field(alias='ResponseContext') + project: projects__project_schema__RPCResponse = Field(alias='Project') + +class projects__project_schema__RPCResponse(GeneratedModel): + request_id: str = Field(alias='request_id') + data: QuotasData = Field(alias='data') + +class projects__project_schema__ResponseContext(GeneratedModel): + request_id: str = Field(alias='RequestId') + +class quotas__quota_schema__QuotasResponse(GeneratedModel): + response_context: quotas__quota_schema__ResponseContext = Field(alias='ResponseContext') + quotas: OKSQuotas = Field(alias='Quotas') + +class quotas__quota_schema__ResponseContext(GeneratedModel): + request_id: str = Field(alias='RequestId') + +class templates__template_schema__ResponseContext(GeneratedModel): + request_id: str = Field(alias='RequestId') + +class ListProjectsRequest(GeneratedModel): + name: str | None = Field(default=None, alias='name') + status: str | None = Field(default=None, alias='status') + cidr: str | None = Field(default=None, alias='cidr') + deleted: bool | None = Field(default=None, alias='deleted') + cursor: str | None = Field(default=None, alias='cursor') + page: int | None = Field(default=None, alias='page') + limit: int | None = Field(default=None, alias='limit') + +class CreateProjectRequest(GeneratedModel): + body: ProjectInput = Field(alias='body') + +class GetProjectRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class UpdateProjectRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + body: ProjectUpdate = Field(alias='body') + +class DeleteProjectRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class GetProjectQuotasRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class GetProjectSnapshotsRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class GetProjectPublicIpsRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class GetProjectNetsRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class GetEimUsersRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class CreateEimUserRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + user: str = Field(alias='user') + ttl: str | None = Field(default=None, alias='ttl') + +class GetEimUserTypesRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + +class DeleteEimUserRequest(GeneratedModel): + project_id: str = Field(alias='project_id') + user: str = Field(alias='user') + +class ListClustersByProjectIDRequest(GeneratedModel): + project_id: str | None = Field(default=None, alias='project_id') + name: str | None = Field(default=None, alias='name') + status: str | None = Field(default=None, alias='status') + version: str | None = Field(default=None, alias='version') + deleted: bool | None = Field(default=None, alias='deleted') + cursor: str | None = Field(default=None, alias='cursor') + page: int | None = Field(default=None, alias='page') + limit: int | None = Field(default=None, alias='limit') + +class CreateClusterRequest(GeneratedModel): + body: ClusterInput = Field(alias='body') + +class ListAllClustersRequest(GeneratedModel): + name: str | None = Field(default=None, alias='name') + status: str | None = Field(default=None, alias='status') + version: str | None = Field(default=None, alias='version') + deleted: bool | None = Field(default=None, alias='deleted') + cursor: str | None = Field(default=None, alias='cursor') + page: int | None = Field(default=None, alias='page') + limit: int | None = Field(default=None, alias='limit') + +class GetClusterRequest(GeneratedModel): + cluster_id: str = Field(alias='cluster_id') + +class UpdateClusterRequest(GeneratedModel): + cluster_id: str = Field(alias='cluster_id') + body: ClusterUpdate = Field(alias='body') + +class DeleteClusterRequest(GeneratedModel): + cluster_id: str = Field(alias='cluster_id') + +class GetKubeconfigRequest(GeneratedModel): + cluster_id: str = Field(alias='cluster_id') + user: str | None = Field(default=None, alias='user') + group: str | None = Field(default=None, alias='group') + ttl: str | None = Field(default=None, alias='ttl') + +class GetKubeconfigWithPubkeyNACLRequest(GeneratedModel): + cluster_id: str = Field(alias='cluster_id') + user: str | None = Field(default=None, alias='user') + group: str | None = Field(default=None, alias='group') + ttl: str | None = Field(default=None, alias='ttl') + +class UpgradeClusterRequest(GeneratedModel): + cluster_id: str = Field(alias='cluster_id') + +class GetKubernetesVersionsRequest(GeneratedModel): + pass + +class GetCPSubregionsRequest(GeneratedModel): + pass + +class GetControlPlanePlansRequest(GeneratedModel): + pass + +class GetAdmissionPluginsRequest(GeneratedModel): + version: str = Field(alias='version') + +class GetProjectTemplateRequest(GeneratedModel): + pass + +class GetClusterTemplateRequest(GeneratedModel): + pass + +class GetNodepoolTemplateRequest(GeneratedModel): + pass + +class GetNetPeeringRequestTemplateRequest(GeneratedModel): + pass + +class GetNetPeeringAcceptanceTemplateRequest(GeneratedModel): + pass + +class GetQuotasRequest(GeneratedModel): + pass + +class GetClientIPRequest(GeneratedModel): + pass diff --git a/osc_sdk_python/generated/osc/__init__.py b/osc_sdk_python/generated/osc/__init__.py new file mode 100644 index 0000000..d58e3f2 --- /dev/null +++ b/osc_sdk_python/generated/osc/__init__.py @@ -0,0 +1,1323 @@ +"""Generated typed SDK exports. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. +""" + +from .async_client import AsyncOscTypedMixin +from .models import ( + AcceptNetPeeringRequest, + AcceptNetPeeringResponse, + AccepterNet, + AccessKey, + AccessKeySecretKey, + AccessLog, + Account, + ActionsOnNextBoot, + AddUserToUserGroupRequest, + AddUserToUserGroupResponse, + ApiAccessPolicy, + ApiAccessRule, + ApplicationStickyCookiePolicy, + BackendVmHealth, + BlockDeviceMappingCreated, + BlockDeviceMappingImage, + BlockDeviceMappingVmCreation, + BlockDeviceMappingVmUpdate, + BootMode, + BsuCreated, + BsuToCreate, + BsuToUpdateVm, + CO2CategoryDistribution, + CO2EmissionEntry, + CO2FactorDistribution, + Ca, + Catalog, + CatalogEntry, + Catalogs, + CheckAuthenticationRequest, + CheckAuthenticationResponse, + ClientGateway, + ConsumptionEntry, + CreateAccessKeyRequest, + CreateAccessKeyResponse, + CreateAccountRequest, + CreateAccountResponse, + CreateApiAccessRuleRequest, + CreateApiAccessRuleResponse, + CreateCaRequest, + CreateCaResponse, + CreateClientGatewayRequest, + CreateClientGatewayResponse, + CreateDedicatedGroupRequest, + CreateDedicatedGroupResponse, + CreateDhcpOptionsRequest, + CreateDhcpOptionsResponse, + CreateDirectLinkInterfaceRequest, + CreateDirectLinkInterfaceResponse, + CreateDirectLinkRequest, + CreateDirectLinkResponse, + CreateFlexibleGpuRequest, + CreateFlexibleGpuResponse, + CreateImageExportTaskRequest, + CreateImageExportTaskResponse, + CreateImageRequest, + CreateImageResponse, + CreateInternetServiceRequest, + CreateInternetServiceResponse, + CreateKeypairRequest, + CreateKeypairResponse, + CreateListenerRuleRequest, + CreateListenerRuleResponse, + CreateLoadBalancerListenersRequest, + CreateLoadBalancerListenersResponse, + CreateLoadBalancerPolicyRequest, + CreateLoadBalancerPolicyResponse, + CreateLoadBalancerRequest, + CreateLoadBalancerResponse, + CreateLoadBalancerTagsRequest, + CreateLoadBalancerTagsResponse, + CreateNatServiceRequest, + CreateNatServiceResponse, + CreateNetAccessPointRequest, + CreateNetAccessPointResponse, + CreateNetPeeringRequest, + CreateNetPeeringResponse, + CreateNetRequest, + CreateNetResponse, + CreateNicRequest, + CreateNicResponse, + CreatePolicyRequest, + CreatePolicyResponse, + CreatePolicyVersionRequest, + CreatePolicyVersionResponse, + CreateProductTypeRequest, + CreateProductTypeResponse, + CreatePublicIpRequest, + CreatePublicIpResponse, + CreateRouteRequest, + CreateRouteResponse, + CreateRouteTableRequest, + CreateRouteTableResponse, + CreateSecurityGroupRequest, + CreateSecurityGroupResponse, + CreateSecurityGroupRuleRequest, + CreateSecurityGroupRuleResponse, + CreateServerCertificateRequest, + CreateServerCertificateResponse, + CreateSnapshotExportTaskRequest, + CreateSnapshotExportTaskResponse, + CreateSnapshotRequest, + CreateSnapshotResponse, + CreateSubnetRequest, + CreateSubnetResponse, + CreateTagsRequest, + CreateTagsResponse, + CreateUserGroupRequest, + CreateUserGroupResponse, + CreateUserRequest, + CreateUserResponse, + CreateVirtualGatewayRequest, + CreateVirtualGatewayResponse, + CreateVmGroupRequest, + CreateVmGroupResponse, + CreateVmTemplateRequest, + CreateVmTemplateResponse, + CreateVmsRequest, + CreateVmsResponse, + CreateVolumeRequest, + CreateVolumeResponse, + CreateVpnConnectionRequest, + CreateVpnConnectionResponse, + CreateVpnConnectionRouteRequest, + CreateVpnConnectionRouteResponse, + DedicatedGroup, + DeleteAccessKeyRequest, + DeleteAccessKeyResponse, + DeleteApiAccessRuleRequest, + DeleteApiAccessRuleResponse, + DeleteCaRequest, + DeleteCaResponse, + DeleteClientGatewayRequest, + DeleteClientGatewayResponse, + DeleteDedicatedGroupRequest, + DeleteDedicatedGroupResponse, + DeleteDhcpOptionsRequest, + DeleteDhcpOptionsResponse, + DeleteDirectLinkInterfaceRequest, + DeleteDirectLinkInterfaceResponse, + DeleteDirectLinkRequest, + DeleteDirectLinkResponse, + DeleteExportTaskRequest, + DeleteExportTaskResponse, + DeleteFlexibleGpuRequest, + DeleteFlexibleGpuResponse, + DeleteImageRequest, + DeleteImageResponse, + DeleteInternetServiceRequest, + DeleteInternetServiceResponse, + DeleteKeypairRequest, + DeleteKeypairResponse, + DeleteListenerRuleRequest, + DeleteListenerRuleResponse, + DeleteLoadBalancerListenersRequest, + DeleteLoadBalancerListenersResponse, + DeleteLoadBalancerPolicyRequest, + DeleteLoadBalancerPolicyResponse, + DeleteLoadBalancerRequest, + DeleteLoadBalancerResponse, + DeleteLoadBalancerTagsRequest, + DeleteLoadBalancerTagsResponse, + DeleteNatServiceRequest, + DeleteNatServiceResponse, + DeleteNetAccessPointRequest, + DeleteNetAccessPointResponse, + DeleteNetPeeringRequest, + DeleteNetPeeringResponse, + DeleteNetRequest, + DeleteNetResponse, + DeleteNicRequest, + DeleteNicResponse, + DeletePolicyRequest, + DeletePolicyResponse, + DeletePolicyVersionRequest, + DeletePolicyVersionResponse, + DeleteProductTypeRequest, + DeleteProductTypeResponse, + DeletePublicIpRequest, + DeletePublicIpResponse, + DeleteRouteRequest, + DeleteRouteResponse, + DeleteRouteTableRequest, + DeleteRouteTableResponse, + DeleteSecurityGroupRequest, + DeleteSecurityGroupResponse, + DeleteSecurityGroupRuleRequest, + DeleteSecurityGroupRuleResponse, + DeleteServerCertificateRequest, + DeleteServerCertificateResponse, + DeleteSnapshotRequest, + DeleteSnapshotResponse, + DeleteSubnetRequest, + DeleteSubnetResponse, + DeleteTagsRequest, + DeleteTagsResponse, + DeleteUserGroupPolicyRequest, + DeleteUserGroupPolicyResponse, + DeleteUserGroupRequest, + DeleteUserGroupResponse, + DeleteUserPolicyRequest, + DeleteUserPolicyResponse, + DeleteUserRequest, + DeleteUserResponse, + DeleteVirtualGatewayRequest, + DeleteVirtualGatewayResponse, + DeleteVmGroupRequest, + DeleteVmGroupResponse, + DeleteVmTemplateRequest, + DeleteVmTemplateResponse, + DeleteVmsRequest, + DeleteVmsResponse, + DeleteVolumeRequest, + DeleteVolumeResponse, + DeleteVpnConnectionRequest, + DeleteVpnConnectionResponse, + DeleteVpnConnectionRouteRequest, + DeleteVpnConnectionRouteResponse, + DeregisterVmsInLoadBalancerRequest, + DeregisterVmsInLoadBalancerResponse, + DhcpOptionsSet, + DirectLink, + DirectLinkInterface, + DirectLinkInterfaces, + DisableOutscaleLoginForUsersRequest, + DisableOutscaleLoginForUsersResponse, + DisableOutscaleLoginPerUsersRequest, + DisableOutscaleLoginPerUsersResponse, + DisableOutscaleLoginRequest, + DisableOutscaleLoginResponse, + EnableOutscaleLoginForUsersRequest, + EnableOutscaleLoginForUsersResponse, + EnableOutscaleLoginPerUsersRequest, + EnableOutscaleLoginPerUsersResponse, + EnableOutscaleLoginRequest, + EnableOutscaleLoginResponse, + ErrorResponse, + Errors, + FiltersAccessKeys, + FiltersApiAccessRule, + FiltersApiLog, + FiltersCa, + FiltersCatalogs, + FiltersClientGateway, + FiltersDedicatedGroup, + FiltersDhcpOptions, + FiltersDirectLink, + FiltersDirectLinkInterface, + FiltersFlexibleGpu, + FiltersImage, + FiltersInternetService, + FiltersKeypair, + FiltersListenerRule, + FiltersLoadBalancer, + FiltersNatService, + FiltersNet, + FiltersNetAccessPoint, + FiltersNetPeering, + FiltersNic, + FiltersProductType, + FiltersPublicIp, + FiltersQuota, + FiltersReadImageExportTask, + FiltersReadVolumeUpdateTask, + FiltersRouteTable, + FiltersSecurityGroup, + FiltersServerCertificate, + FiltersService, + FiltersSnapshot, + FiltersSnapshotExportTask, + FiltersSubnet, + FiltersSubregion, + FiltersTag, + FiltersUserGroup, + FiltersUsers, + FiltersVirtualGateway, + FiltersVm, + FiltersVmGroup, + FiltersVmTemplate, + FiltersVmType, + FiltersVmsState, + FiltersVmsStopHistory, + FiltersVolume, + FiltersVpnConnection, + FlexibleGpu, + FlexibleGpuCatalog, + HealthCheck, + Image, + ImageExportTask, + InlinePolicy, + InternetService, + Keypair, + KeypairCreated, + LinkFlexibleGpuRequest, + LinkFlexibleGpuResponse, + LinkInternetServiceRequest, + LinkInternetServiceResponse, + LinkLoadBalancerBackendMachinesRequest, + LinkLoadBalancerBackendMachinesResponse, + LinkManagedPolicyToUserGroupRequest, + LinkManagedPolicyToUserGroupResponse, + LinkNic, + LinkNicLight, + LinkNicRequest, + LinkNicResponse, + LinkNicToUpdate, + LinkPolicyRequest, + LinkPolicyResponse, + LinkPrivateIpsRequest, + LinkPrivateIpsResponse, + LinkPublicIp, + LinkPublicIpLightForVm, + LinkPublicIpRequest, + LinkPublicIpResponse, + LinkRouteTable, + LinkRouteTableRequest, + LinkRouteTableResponse, + LinkVirtualGatewayRequest, + LinkVirtualGatewayResponse, + LinkVolumeRequest, + LinkVolumeResponse, + LinkedPolicy, + LinkedVolume, + Listener, + ListenerForCreation, + ListenerRule, + ListenerRuleForCreation, + LoadBalancer, + LoadBalancerLight, + LoadBalancerStickyCookiePolicy, + LoadBalancerTag, + Location, + Log, + MaintenanceEvent, + MinimalPolicy, + NatService, + Net, + NetAccessPoint, + NetPeering, + NetPeeringState, + NetToVirtualGatewayLink, + Nic, + NicForVmCreation, + NicLight, + OsuApiKey, + OsuExportImageExportTask, + OsuExportSnapshotExportTask, + OsuExportToCreate, + PermissionsOnResource, + PermissionsOnResourceCreation, + Phase1Options, + Phase2Options, + Placement, + Policy, + PolicyEntities, + PolicyVersion, + PrivateIp, + PrivateIpLight, + PrivateIpLightForVm, + ProductType, + PublicIp, + PublicIpLight, + PutUserGroupPolicyRequest, + PutUserGroupPolicyResponse, + PutUserPolicyRequest, + PutUserPolicyResponse, + Quota, + QuotaTypes, + ReadAccessKeysRequest, + ReadAccessKeysResponse, + ReadAccountsRequest, + ReadAccountsResponse, + ReadAdminPasswordRequest, + ReadAdminPasswordResponse, + ReadApiAccessPolicyRequest, + ReadApiAccessPolicyResponse, + ReadApiAccessRulesRequest, + ReadApiAccessRulesResponse, + ReadApiLogsRequest, + ReadApiLogsResponse, + ReadCO2EmissionAccountRequest, + ReadCO2EmissionAccountResponse, + ReadCasRequest, + ReadCasResponse, + ReadCatalogRequest, + ReadCatalogResponse, + ReadCatalogsRequest, + ReadCatalogsResponse, + ReadClientGatewaysRequest, + ReadClientGatewaysResponse, + ReadConsoleOutputRequest, + ReadConsoleOutputResponse, + ReadConsumptionAccountRequest, + ReadConsumptionAccountResponse, + ReadDedicatedGroupsRequest, + ReadDedicatedGroupsResponse, + ReadDhcpOptionsRequest, + ReadDhcpOptionsResponse, + ReadDirectLinkInterfacesRequest, + ReadDirectLinkInterfacesResponse, + ReadDirectLinksRequest, + ReadDirectLinksResponse, + ReadEntitiesLinkedToPolicyRequest, + ReadEntitiesLinkedToPolicyResponse, + ReadFlexibleGpuCatalogRequest, + ReadFlexibleGpuCatalogResponse, + ReadFlexibleGpusRequest, + ReadFlexibleGpusResponse, + ReadImageExportTasksRequest, + ReadImageExportTasksResponse, + ReadImagesRequest, + ReadImagesResponse, + ReadInternetServicesRequest, + ReadInternetServicesResponse, + ReadKeypairsRequest, + ReadKeypairsResponse, + ReadLinkedPoliciesFilters, + ReadLinkedPoliciesRequest, + ReadLinkedPoliciesResponse, + ReadListenerRulesRequest, + ReadListenerRulesResponse, + ReadLoadBalancerTagsRequest, + ReadLoadBalancerTagsResponse, + ReadLoadBalancersRequest, + ReadLoadBalancersResponse, + ReadLocationsRequest, + ReadLocationsResponse, + ReadManagedPoliciesLinkedToUserGroupRequest, + ReadManagedPoliciesLinkedToUserGroupResponse, + ReadNatServicesRequest, + ReadNatServicesResponse, + ReadNetAccessPointServicesRequest, + ReadNetAccessPointServicesResponse, + ReadNetAccessPointsRequest, + ReadNetAccessPointsResponse, + ReadNetPeeringsRequest, + ReadNetPeeringsResponse, + ReadNetsRequest, + ReadNetsResponse, + ReadNicsRequest, + ReadNicsResponse, + ReadPoliciesFilters, + ReadPoliciesRequest, + ReadPoliciesResponse, + ReadPolicyRequest, + ReadPolicyResponse, + ReadPolicyVersionRequest, + ReadPolicyVersionResponse, + ReadPolicyVersionsRequest, + ReadPolicyVersionsResponse, + ReadProductTypesRequest, + ReadProductTypesResponse, + ReadPublicCatalogRequest, + ReadPublicCatalogResponse, + ReadPublicIpRangesRequest, + ReadPublicIpRangesResponse, + ReadPublicIpsRequest, + ReadPublicIpsResponse, + ReadQuotasRequest, + ReadQuotasResponse, + ReadRegionsRequest, + ReadRegionsResponse, + ReadRouteTablesRequest, + ReadRouteTablesResponse, + ReadSecurityGroupsRequest, + ReadSecurityGroupsResponse, + ReadServerCertificatesRequest, + ReadServerCertificatesResponse, + ReadSnapshotExportTasksRequest, + ReadSnapshotExportTasksResponse, + ReadSnapshotsRequest, + ReadSnapshotsResponse, + ReadSubnetsRequest, + ReadSubnetsResponse, + ReadSubregionsRequest, + ReadSubregionsResponse, + ReadTagsRequest, + ReadTagsResponse, + ReadUnitPriceRequest, + ReadUnitPriceResponse, + ReadUserGroupPoliciesRequest, + ReadUserGroupPoliciesResponse, + ReadUserGroupPolicyRequest, + ReadUserGroupPolicyResponse, + ReadUserGroupRequest, + ReadUserGroupResponse, + ReadUserGroupsPerUserRequest, + ReadUserGroupsPerUserResponse, + ReadUserGroupsRequest, + ReadUserGroupsResponse, + ReadUserPoliciesRequest, + ReadUserPoliciesResponse, + ReadUserPolicyRequest, + ReadUserPolicyResponse, + ReadUsersRequest, + ReadUsersResponse, + ReadVirtualGatewaysRequest, + ReadVirtualGatewaysResponse, + ReadVmGroupsRequest, + ReadVmGroupsResponse, + ReadVmTemplatesRequest, + ReadVmTemplatesResponse, + ReadVmTypesRequest, + ReadVmTypesResponse, + ReadVmsHealthRequest, + ReadVmsHealthResponse, + ReadVmsRequest, + ReadVmsResponse, + ReadVmsStateRequest, + ReadVmsStateResponse, + ReadVmsStopHistoryRequest, + ReadVmsStopHistoryResponse, + ReadVolumeUpdateTasksRequest, + ReadVolumeUpdateTasksResponse, + ReadVolumesRequest, + ReadVolumesResponse, + ReadVpnConnectionsRequest, + ReadVpnConnectionsResponse, + RebootVmsRequest, + RebootVmsResponse, + Region, + RegisterVmsInLoadBalancerRequest, + RegisterVmsInLoadBalancerResponse, + RejectNetPeeringRequest, + RejectNetPeeringResponse, + RemoveUserFromUserGroupRequest, + RemoveUserFromUserGroupResponse, + ResourceLoadBalancerTag, + ResourceTag, + ResponseContext, + Route, + RouteLight, + RoutePropagatingVirtualGateway, + RouteTable, + ScaleDownVmGroupRequest, + ScaleDownVmGroupResponse, + ScaleUpVmGroupRequest, + ScaleUpVmGroupResponse, + SecureBootAction, + SecurityGroup, + SecurityGroupLight, + SecurityGroupRule, + SecurityGroupsMember, + ServerCertificate, + Service, + SetDefaultPolicyVersionRequest, + SetDefaultPolicyVersionResponse, + ShutdownBehaviorConfiguration, + Snapshot, + SnapshotExportTask, + SourceNet, + SourceSecurityGroup, + StartVmsRequest, + StartVmsResponse, + StateComment, + StopVmsRequest, + StopVmsResponse, + Subnet, + Subregion, + Tag, + UnitPriceEntry, + UnlinkFlexibleGpuRequest, + UnlinkFlexibleGpuResponse, + UnlinkInternetServiceRequest, + UnlinkInternetServiceResponse, + UnlinkLoadBalancerBackendMachinesRequest, + UnlinkLoadBalancerBackendMachinesResponse, + UnlinkManagedPolicyFromUserGroupRequest, + UnlinkManagedPolicyFromUserGroupResponse, + UnlinkNicRequest, + UnlinkNicResponse, + UnlinkPolicyRequest, + UnlinkPolicyResponse, + UnlinkPrivateIpsRequest, + UnlinkPrivateIpsResponse, + UnlinkPublicIpRequest, + UnlinkPublicIpResponse, + UnlinkRouteTableRequest, + UnlinkRouteTableResponse, + UnlinkVirtualGatewayRequest, + UnlinkVirtualGatewayResponse, + UnlinkVolumeRequest, + UnlinkVolumeResponse, + UpdateAccessKeyRequest, + UpdateAccessKeyResponse, + UpdateAccountRequest, + UpdateAccountResponse, + UpdateApiAccessPolicyRequest, + UpdateApiAccessPolicyResponse, + UpdateApiAccessRuleRequest, + UpdateApiAccessRuleResponse, + UpdateCaRequest, + UpdateCaResponse, + UpdateDedicatedGroupRequest, + UpdateDedicatedGroupResponse, + UpdateDirectLinkInterfaceRequest, + UpdateDirectLinkInterfaceResponse, + UpdateFlexibleGpuRequest, + UpdateFlexibleGpuResponse, + UpdateImageRequest, + UpdateImageResponse, + UpdateListenerRuleRequest, + UpdateListenerRuleResponse, + UpdateLoadBalancerRequest, + UpdateLoadBalancerResponse, + UpdateNetAccessPointRequest, + UpdateNetAccessPointResponse, + UpdateNetRequest, + UpdateNetResponse, + UpdateNicRequest, + UpdateNicResponse, + UpdateRoutePropagationRequest, + UpdateRoutePropagationResponse, + UpdateRouteRequest, + UpdateRouteResponse, + UpdateRouteTableLinkRequest, + UpdateRouteTableLinkResponse, + UpdateServerCertificateRequest, + UpdateServerCertificateResponse, + UpdateSnapshotRequest, + UpdateSnapshotResponse, + UpdateSubnetRequest, + UpdateSubnetResponse, + UpdateUserGroupRequest, + UpdateUserGroupResponse, + UpdateUserRequest, + UpdateUserResponse, + UpdateVmGroupRequest, + UpdateVmGroupResponse, + UpdateVmRequest, + UpdateVmResponse, + UpdateVmTemplateRequest, + UpdateVmTemplateResponse, + UpdateVolumeRequest, + UpdateVolumeResponse, + UpdateVpnConnectionRequest, + UpdateVpnConnectionResponse, + User, + UserGroup, + VgwTelemetry, + VirtualGateway, + Vm, + VmGroup, + VmState, + VmStates, + VmTemplate, + VmType, + VmsStopHistory, + Volume, + VolumeUpdate, + VolumeUpdateParameters, + VolumeUpdateTask, + VpnConnection, + VpnOptions, + With, +) + +__all__ = [ + "AsyncOscTypedMixin", + 'AcceptNetPeeringRequest', + 'AcceptNetPeeringResponse', + 'AccepterNet', + 'AccessKey', + 'AccessKeySecretKey', + 'AccessLog', + 'Account', + 'ActionsOnNextBoot', + 'AddUserToUserGroupRequest', + 'AddUserToUserGroupResponse', + 'ApiAccessPolicy', + 'ApiAccessRule', + 'ApplicationStickyCookiePolicy', + 'BackendVmHealth', + 'BlockDeviceMappingCreated', + 'BlockDeviceMappingImage', + 'BlockDeviceMappingVmCreation', + 'BlockDeviceMappingVmUpdate', + 'BootMode', + 'BsuCreated', + 'BsuToCreate', + 'BsuToUpdateVm', + 'CO2CategoryDistribution', + 'CO2EmissionEntry', + 'CO2FactorDistribution', + 'Ca', + 'Catalog', + 'CatalogEntry', + 'Catalogs', + 'CheckAuthenticationRequest', + 'CheckAuthenticationResponse', + 'ClientGateway', + 'ConsumptionEntry', + 'CreateAccessKeyRequest', + 'CreateAccessKeyResponse', + 'CreateAccountRequest', + 'CreateAccountResponse', + 'CreateApiAccessRuleRequest', + 'CreateApiAccessRuleResponse', + 'CreateCaRequest', + 'CreateCaResponse', + 'CreateClientGatewayRequest', + 'CreateClientGatewayResponse', + 'CreateDedicatedGroupRequest', + 'CreateDedicatedGroupResponse', + 'CreateDhcpOptionsRequest', + 'CreateDhcpOptionsResponse', + 'CreateDirectLinkInterfaceRequest', + 'CreateDirectLinkInterfaceResponse', + 'CreateDirectLinkRequest', + 'CreateDirectLinkResponse', + 'CreateFlexibleGpuRequest', + 'CreateFlexibleGpuResponse', + 'CreateImageExportTaskRequest', + 'CreateImageExportTaskResponse', + 'CreateImageRequest', + 'CreateImageResponse', + 'CreateInternetServiceRequest', + 'CreateInternetServiceResponse', + 'CreateKeypairRequest', + 'CreateKeypairResponse', + 'CreateListenerRuleRequest', + 'CreateListenerRuleResponse', + 'CreateLoadBalancerListenersRequest', + 'CreateLoadBalancerListenersResponse', + 'CreateLoadBalancerPolicyRequest', + 'CreateLoadBalancerPolicyResponse', + 'CreateLoadBalancerRequest', + 'CreateLoadBalancerResponse', + 'CreateLoadBalancerTagsRequest', + 'CreateLoadBalancerTagsResponse', + 'CreateNatServiceRequest', + 'CreateNatServiceResponse', + 'CreateNetAccessPointRequest', + 'CreateNetAccessPointResponse', + 'CreateNetPeeringRequest', + 'CreateNetPeeringResponse', + 'CreateNetRequest', + 'CreateNetResponse', + 'CreateNicRequest', + 'CreateNicResponse', + 'CreatePolicyRequest', + 'CreatePolicyResponse', + 'CreatePolicyVersionRequest', + 'CreatePolicyVersionResponse', + 'CreateProductTypeRequest', + 'CreateProductTypeResponse', + 'CreatePublicIpRequest', + 'CreatePublicIpResponse', + 'CreateRouteRequest', + 'CreateRouteResponse', + 'CreateRouteTableRequest', + 'CreateRouteTableResponse', + 'CreateSecurityGroupRequest', + 'CreateSecurityGroupResponse', + 'CreateSecurityGroupRuleRequest', + 'CreateSecurityGroupRuleResponse', + 'CreateServerCertificateRequest', + 'CreateServerCertificateResponse', + 'CreateSnapshotExportTaskRequest', + 'CreateSnapshotExportTaskResponse', + 'CreateSnapshotRequest', + 'CreateSnapshotResponse', + 'CreateSubnetRequest', + 'CreateSubnetResponse', + 'CreateTagsRequest', + 'CreateTagsResponse', + 'CreateUserGroupRequest', + 'CreateUserGroupResponse', + 'CreateUserRequest', + 'CreateUserResponse', + 'CreateVirtualGatewayRequest', + 'CreateVirtualGatewayResponse', + 'CreateVmGroupRequest', + 'CreateVmGroupResponse', + 'CreateVmTemplateRequest', + 'CreateVmTemplateResponse', + 'CreateVmsRequest', + 'CreateVmsResponse', + 'CreateVolumeRequest', + 'CreateVolumeResponse', + 'CreateVpnConnectionRequest', + 'CreateVpnConnectionResponse', + 'CreateVpnConnectionRouteRequest', + 'CreateVpnConnectionRouteResponse', + 'DedicatedGroup', + 'DeleteAccessKeyRequest', + 'DeleteAccessKeyResponse', + 'DeleteApiAccessRuleRequest', + 'DeleteApiAccessRuleResponse', + 'DeleteCaRequest', + 'DeleteCaResponse', + 'DeleteClientGatewayRequest', + 'DeleteClientGatewayResponse', + 'DeleteDedicatedGroupRequest', + 'DeleteDedicatedGroupResponse', + 'DeleteDhcpOptionsRequest', + 'DeleteDhcpOptionsResponse', + 'DeleteDirectLinkInterfaceRequest', + 'DeleteDirectLinkInterfaceResponse', + 'DeleteDirectLinkRequest', + 'DeleteDirectLinkResponse', + 'DeleteExportTaskRequest', + 'DeleteExportTaskResponse', + 'DeleteFlexibleGpuRequest', + 'DeleteFlexibleGpuResponse', + 'DeleteImageRequest', + 'DeleteImageResponse', + 'DeleteInternetServiceRequest', + 'DeleteInternetServiceResponse', + 'DeleteKeypairRequest', + 'DeleteKeypairResponse', + 'DeleteListenerRuleRequest', + 'DeleteListenerRuleResponse', + 'DeleteLoadBalancerListenersRequest', + 'DeleteLoadBalancerListenersResponse', + 'DeleteLoadBalancerPolicyRequest', + 'DeleteLoadBalancerPolicyResponse', + 'DeleteLoadBalancerRequest', + 'DeleteLoadBalancerResponse', + 'DeleteLoadBalancerTagsRequest', + 'DeleteLoadBalancerTagsResponse', + 'DeleteNatServiceRequest', + 'DeleteNatServiceResponse', + 'DeleteNetAccessPointRequest', + 'DeleteNetAccessPointResponse', + 'DeleteNetPeeringRequest', + 'DeleteNetPeeringResponse', + 'DeleteNetRequest', + 'DeleteNetResponse', + 'DeleteNicRequest', + 'DeleteNicResponse', + 'DeletePolicyRequest', + 'DeletePolicyResponse', + 'DeletePolicyVersionRequest', + 'DeletePolicyVersionResponse', + 'DeleteProductTypeRequest', + 'DeleteProductTypeResponse', + 'DeletePublicIpRequest', + 'DeletePublicIpResponse', + 'DeleteRouteRequest', + 'DeleteRouteResponse', + 'DeleteRouteTableRequest', + 'DeleteRouteTableResponse', + 'DeleteSecurityGroupRequest', + 'DeleteSecurityGroupResponse', + 'DeleteSecurityGroupRuleRequest', + 'DeleteSecurityGroupRuleResponse', + 'DeleteServerCertificateRequest', + 'DeleteServerCertificateResponse', + 'DeleteSnapshotRequest', + 'DeleteSnapshotResponse', + 'DeleteSubnetRequest', + 'DeleteSubnetResponse', + 'DeleteTagsRequest', + 'DeleteTagsResponse', + 'DeleteUserGroupPolicyRequest', + 'DeleteUserGroupPolicyResponse', + 'DeleteUserGroupRequest', + 'DeleteUserGroupResponse', + 'DeleteUserPolicyRequest', + 'DeleteUserPolicyResponse', + 'DeleteUserRequest', + 'DeleteUserResponse', + 'DeleteVirtualGatewayRequest', + 'DeleteVirtualGatewayResponse', + 'DeleteVmGroupRequest', + 'DeleteVmGroupResponse', + 'DeleteVmTemplateRequest', + 'DeleteVmTemplateResponse', + 'DeleteVmsRequest', + 'DeleteVmsResponse', + 'DeleteVolumeRequest', + 'DeleteVolumeResponse', + 'DeleteVpnConnectionRequest', + 'DeleteVpnConnectionResponse', + 'DeleteVpnConnectionRouteRequest', + 'DeleteVpnConnectionRouteResponse', + 'DeregisterVmsInLoadBalancerRequest', + 'DeregisterVmsInLoadBalancerResponse', + 'DhcpOptionsSet', + 'DirectLink', + 'DirectLinkInterface', + 'DirectLinkInterfaces', + 'DisableOutscaleLoginForUsersRequest', + 'DisableOutscaleLoginForUsersResponse', + 'DisableOutscaleLoginPerUsersRequest', + 'DisableOutscaleLoginPerUsersResponse', + 'DisableOutscaleLoginRequest', + 'DisableOutscaleLoginResponse', + 'EnableOutscaleLoginForUsersRequest', + 'EnableOutscaleLoginForUsersResponse', + 'EnableOutscaleLoginPerUsersRequest', + 'EnableOutscaleLoginPerUsersResponse', + 'EnableOutscaleLoginRequest', + 'EnableOutscaleLoginResponse', + 'ErrorResponse', + 'Errors', + 'FiltersAccessKeys', + 'FiltersApiAccessRule', + 'FiltersApiLog', + 'FiltersCa', + 'FiltersCatalogs', + 'FiltersClientGateway', + 'FiltersDedicatedGroup', + 'FiltersDhcpOptions', + 'FiltersDirectLink', + 'FiltersDirectLinkInterface', + 'FiltersFlexibleGpu', + 'FiltersImage', + 'FiltersInternetService', + 'FiltersKeypair', + 'FiltersListenerRule', + 'FiltersLoadBalancer', + 'FiltersNatService', + 'FiltersNet', + 'FiltersNetAccessPoint', + 'FiltersNetPeering', + 'FiltersNic', + 'FiltersProductType', + 'FiltersPublicIp', + 'FiltersQuota', + 'FiltersReadImageExportTask', + 'FiltersReadVolumeUpdateTask', + 'FiltersRouteTable', + 'FiltersSecurityGroup', + 'FiltersServerCertificate', + 'FiltersService', + 'FiltersSnapshot', + 'FiltersSnapshotExportTask', + 'FiltersSubnet', + 'FiltersSubregion', + 'FiltersTag', + 'FiltersUserGroup', + 'FiltersUsers', + 'FiltersVirtualGateway', + 'FiltersVm', + 'FiltersVmGroup', + 'FiltersVmTemplate', + 'FiltersVmType', + 'FiltersVmsState', + 'FiltersVmsStopHistory', + 'FiltersVolume', + 'FiltersVpnConnection', + 'FlexibleGpu', + 'FlexibleGpuCatalog', + 'HealthCheck', + 'Image', + 'ImageExportTask', + 'InlinePolicy', + 'InternetService', + 'Keypair', + 'KeypairCreated', + 'LinkFlexibleGpuRequest', + 'LinkFlexibleGpuResponse', + 'LinkInternetServiceRequest', + 'LinkInternetServiceResponse', + 'LinkLoadBalancerBackendMachinesRequest', + 'LinkLoadBalancerBackendMachinesResponse', + 'LinkManagedPolicyToUserGroupRequest', + 'LinkManagedPolicyToUserGroupResponse', + 'LinkNic', + 'LinkNicLight', + 'LinkNicRequest', + 'LinkNicResponse', + 'LinkNicToUpdate', + 'LinkPolicyRequest', + 'LinkPolicyResponse', + 'LinkPrivateIpsRequest', + 'LinkPrivateIpsResponse', + 'LinkPublicIp', + 'LinkPublicIpLightForVm', + 'LinkPublicIpRequest', + 'LinkPublicIpResponse', + 'LinkRouteTable', + 'LinkRouteTableRequest', + 'LinkRouteTableResponse', + 'LinkVirtualGatewayRequest', + 'LinkVirtualGatewayResponse', + 'LinkVolumeRequest', + 'LinkVolumeResponse', + 'LinkedPolicy', + 'LinkedVolume', + 'Listener', + 'ListenerForCreation', + 'ListenerRule', + 'ListenerRuleForCreation', + 'LoadBalancer', + 'LoadBalancerLight', + 'LoadBalancerStickyCookiePolicy', + 'LoadBalancerTag', + 'Location', + 'Log', + 'MaintenanceEvent', + 'MinimalPolicy', + 'NatService', + 'Net', + 'NetAccessPoint', + 'NetPeering', + 'NetPeeringState', + 'NetToVirtualGatewayLink', + 'Nic', + 'NicForVmCreation', + 'NicLight', + 'OsuApiKey', + 'OsuExportImageExportTask', + 'OsuExportSnapshotExportTask', + 'OsuExportToCreate', + 'PermissionsOnResource', + 'PermissionsOnResourceCreation', + 'Phase1Options', + 'Phase2Options', + 'Placement', + 'Policy', + 'PolicyEntities', + 'PolicyVersion', + 'PrivateIp', + 'PrivateIpLight', + 'PrivateIpLightForVm', + 'ProductType', + 'PublicIp', + 'PublicIpLight', + 'PutUserGroupPolicyRequest', + 'PutUserGroupPolicyResponse', + 'PutUserPolicyRequest', + 'PutUserPolicyResponse', + 'Quota', + 'QuotaTypes', + 'ReadAccessKeysRequest', + 'ReadAccessKeysResponse', + 'ReadAccountsRequest', + 'ReadAccountsResponse', + 'ReadAdminPasswordRequest', + 'ReadAdminPasswordResponse', + 'ReadApiAccessPolicyRequest', + 'ReadApiAccessPolicyResponse', + 'ReadApiAccessRulesRequest', + 'ReadApiAccessRulesResponse', + 'ReadApiLogsRequest', + 'ReadApiLogsResponse', + 'ReadCO2EmissionAccountRequest', + 'ReadCO2EmissionAccountResponse', + 'ReadCasRequest', + 'ReadCasResponse', + 'ReadCatalogRequest', + 'ReadCatalogResponse', + 'ReadCatalogsRequest', + 'ReadCatalogsResponse', + 'ReadClientGatewaysRequest', + 'ReadClientGatewaysResponse', + 'ReadConsoleOutputRequest', + 'ReadConsoleOutputResponse', + 'ReadConsumptionAccountRequest', + 'ReadConsumptionAccountResponse', + 'ReadDedicatedGroupsRequest', + 'ReadDedicatedGroupsResponse', + 'ReadDhcpOptionsRequest', + 'ReadDhcpOptionsResponse', + 'ReadDirectLinkInterfacesRequest', + 'ReadDirectLinkInterfacesResponse', + 'ReadDirectLinksRequest', + 'ReadDirectLinksResponse', + 'ReadEntitiesLinkedToPolicyRequest', + 'ReadEntitiesLinkedToPolicyResponse', + 'ReadFlexibleGpuCatalogRequest', + 'ReadFlexibleGpuCatalogResponse', + 'ReadFlexibleGpusRequest', + 'ReadFlexibleGpusResponse', + 'ReadImageExportTasksRequest', + 'ReadImageExportTasksResponse', + 'ReadImagesRequest', + 'ReadImagesResponse', + 'ReadInternetServicesRequest', + 'ReadInternetServicesResponse', + 'ReadKeypairsRequest', + 'ReadKeypairsResponse', + 'ReadLinkedPoliciesFilters', + 'ReadLinkedPoliciesRequest', + 'ReadLinkedPoliciesResponse', + 'ReadListenerRulesRequest', + 'ReadListenerRulesResponse', + 'ReadLoadBalancerTagsRequest', + 'ReadLoadBalancerTagsResponse', + 'ReadLoadBalancersRequest', + 'ReadLoadBalancersResponse', + 'ReadLocationsRequest', + 'ReadLocationsResponse', + 'ReadManagedPoliciesLinkedToUserGroupRequest', + 'ReadManagedPoliciesLinkedToUserGroupResponse', + 'ReadNatServicesRequest', + 'ReadNatServicesResponse', + 'ReadNetAccessPointServicesRequest', + 'ReadNetAccessPointServicesResponse', + 'ReadNetAccessPointsRequest', + 'ReadNetAccessPointsResponse', + 'ReadNetPeeringsRequest', + 'ReadNetPeeringsResponse', + 'ReadNetsRequest', + 'ReadNetsResponse', + 'ReadNicsRequest', + 'ReadNicsResponse', + 'ReadPoliciesFilters', + 'ReadPoliciesRequest', + 'ReadPoliciesResponse', + 'ReadPolicyRequest', + 'ReadPolicyResponse', + 'ReadPolicyVersionRequest', + 'ReadPolicyVersionResponse', + 'ReadPolicyVersionsRequest', + 'ReadPolicyVersionsResponse', + 'ReadProductTypesRequest', + 'ReadProductTypesResponse', + 'ReadPublicCatalogRequest', + 'ReadPublicCatalogResponse', + 'ReadPublicIpRangesRequest', + 'ReadPublicIpRangesResponse', + 'ReadPublicIpsRequest', + 'ReadPublicIpsResponse', + 'ReadQuotasRequest', + 'ReadQuotasResponse', + 'ReadRegionsRequest', + 'ReadRegionsResponse', + 'ReadRouteTablesRequest', + 'ReadRouteTablesResponse', + 'ReadSecurityGroupsRequest', + 'ReadSecurityGroupsResponse', + 'ReadServerCertificatesRequest', + 'ReadServerCertificatesResponse', + 'ReadSnapshotExportTasksRequest', + 'ReadSnapshotExportTasksResponse', + 'ReadSnapshotsRequest', + 'ReadSnapshotsResponse', + 'ReadSubnetsRequest', + 'ReadSubnetsResponse', + 'ReadSubregionsRequest', + 'ReadSubregionsResponse', + 'ReadTagsRequest', + 'ReadTagsResponse', + 'ReadUnitPriceRequest', + 'ReadUnitPriceResponse', + 'ReadUserGroupPoliciesRequest', + 'ReadUserGroupPoliciesResponse', + 'ReadUserGroupPolicyRequest', + 'ReadUserGroupPolicyResponse', + 'ReadUserGroupRequest', + 'ReadUserGroupResponse', + 'ReadUserGroupsPerUserRequest', + 'ReadUserGroupsPerUserResponse', + 'ReadUserGroupsRequest', + 'ReadUserGroupsResponse', + 'ReadUserPoliciesRequest', + 'ReadUserPoliciesResponse', + 'ReadUserPolicyRequest', + 'ReadUserPolicyResponse', + 'ReadUsersRequest', + 'ReadUsersResponse', + 'ReadVirtualGatewaysRequest', + 'ReadVirtualGatewaysResponse', + 'ReadVmGroupsRequest', + 'ReadVmGroupsResponse', + 'ReadVmTemplatesRequest', + 'ReadVmTemplatesResponse', + 'ReadVmTypesRequest', + 'ReadVmTypesResponse', + 'ReadVmsHealthRequest', + 'ReadVmsHealthResponse', + 'ReadVmsRequest', + 'ReadVmsResponse', + 'ReadVmsStateRequest', + 'ReadVmsStateResponse', + 'ReadVmsStopHistoryRequest', + 'ReadVmsStopHistoryResponse', + 'ReadVolumeUpdateTasksRequest', + 'ReadVolumeUpdateTasksResponse', + 'ReadVolumesRequest', + 'ReadVolumesResponse', + 'ReadVpnConnectionsRequest', + 'ReadVpnConnectionsResponse', + 'RebootVmsRequest', + 'RebootVmsResponse', + 'Region', + 'RegisterVmsInLoadBalancerRequest', + 'RegisterVmsInLoadBalancerResponse', + 'RejectNetPeeringRequest', + 'RejectNetPeeringResponse', + 'RemoveUserFromUserGroupRequest', + 'RemoveUserFromUserGroupResponse', + 'ResourceLoadBalancerTag', + 'ResourceTag', + 'ResponseContext', + 'Route', + 'RouteLight', + 'RoutePropagatingVirtualGateway', + 'RouteTable', + 'ScaleDownVmGroupRequest', + 'ScaleDownVmGroupResponse', + 'ScaleUpVmGroupRequest', + 'ScaleUpVmGroupResponse', + 'SecureBootAction', + 'SecurityGroup', + 'SecurityGroupLight', + 'SecurityGroupRule', + 'SecurityGroupsMember', + 'ServerCertificate', + 'Service', + 'SetDefaultPolicyVersionRequest', + 'SetDefaultPolicyVersionResponse', + 'ShutdownBehaviorConfiguration', + 'Snapshot', + 'SnapshotExportTask', + 'SourceNet', + 'SourceSecurityGroup', + 'StartVmsRequest', + 'StartVmsResponse', + 'StateComment', + 'StopVmsRequest', + 'StopVmsResponse', + 'Subnet', + 'Subregion', + 'Tag', + 'UnitPriceEntry', + 'UnlinkFlexibleGpuRequest', + 'UnlinkFlexibleGpuResponse', + 'UnlinkInternetServiceRequest', + 'UnlinkInternetServiceResponse', + 'UnlinkLoadBalancerBackendMachinesRequest', + 'UnlinkLoadBalancerBackendMachinesResponse', + 'UnlinkManagedPolicyFromUserGroupRequest', + 'UnlinkManagedPolicyFromUserGroupResponse', + 'UnlinkNicRequest', + 'UnlinkNicResponse', + 'UnlinkPolicyRequest', + 'UnlinkPolicyResponse', + 'UnlinkPrivateIpsRequest', + 'UnlinkPrivateIpsResponse', + 'UnlinkPublicIpRequest', + 'UnlinkPublicIpResponse', + 'UnlinkRouteTableRequest', + 'UnlinkRouteTableResponse', + 'UnlinkVirtualGatewayRequest', + 'UnlinkVirtualGatewayResponse', + 'UnlinkVolumeRequest', + 'UnlinkVolumeResponse', + 'UpdateAccessKeyRequest', + 'UpdateAccessKeyResponse', + 'UpdateAccountRequest', + 'UpdateAccountResponse', + 'UpdateApiAccessPolicyRequest', + 'UpdateApiAccessPolicyResponse', + 'UpdateApiAccessRuleRequest', + 'UpdateApiAccessRuleResponse', + 'UpdateCaRequest', + 'UpdateCaResponse', + 'UpdateDedicatedGroupRequest', + 'UpdateDedicatedGroupResponse', + 'UpdateDirectLinkInterfaceRequest', + 'UpdateDirectLinkInterfaceResponse', + 'UpdateFlexibleGpuRequest', + 'UpdateFlexibleGpuResponse', + 'UpdateImageRequest', + 'UpdateImageResponse', + 'UpdateListenerRuleRequest', + 'UpdateListenerRuleResponse', + 'UpdateLoadBalancerRequest', + 'UpdateLoadBalancerResponse', + 'UpdateNetAccessPointRequest', + 'UpdateNetAccessPointResponse', + 'UpdateNetRequest', + 'UpdateNetResponse', + 'UpdateNicRequest', + 'UpdateNicResponse', + 'UpdateRoutePropagationRequest', + 'UpdateRoutePropagationResponse', + 'UpdateRouteRequest', + 'UpdateRouteResponse', + 'UpdateRouteTableLinkRequest', + 'UpdateRouteTableLinkResponse', + 'UpdateServerCertificateRequest', + 'UpdateServerCertificateResponse', + 'UpdateSnapshotRequest', + 'UpdateSnapshotResponse', + 'UpdateSubnetRequest', + 'UpdateSubnetResponse', + 'UpdateUserGroupRequest', + 'UpdateUserGroupResponse', + 'UpdateUserRequest', + 'UpdateUserResponse', + 'UpdateVmGroupRequest', + 'UpdateVmGroupResponse', + 'UpdateVmRequest', + 'UpdateVmResponse', + 'UpdateVmTemplateRequest', + 'UpdateVmTemplateResponse', + 'UpdateVolumeRequest', + 'UpdateVolumeResponse', + 'UpdateVpnConnectionRequest', + 'UpdateVpnConnectionResponse', + 'User', + 'UserGroup', + 'VgwTelemetry', + 'VirtualGateway', + 'Vm', + 'VmGroup', + 'VmState', + 'VmStates', + 'VmTemplate', + 'VmType', + 'VmsStopHistory', + 'Volume', + 'VolumeUpdate', + 'VolumeUpdateParameters', + 'VolumeUpdateTask', + 'VpnConnection', + 'VpnOptions', + 'With', +] diff --git a/osc_sdk_python/generated/osc/async_client.py b/osc_sdk_python/generated/osc/async_client.py new file mode 100644 index 0000000..748e9c3 --- /dev/null +++ b/osc_sdk_python/generated/osc/async_client.py @@ -0,0 +1,6651 @@ +"""Generated typed OSC client slice. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. + +Do not edit by hand. Regenerate with: + python -m osc_sdk_python.codegen.generator + + python -m osc_sdk_python.codegen.generator oks osc +""" + +from typing import Any + +from pydantic import TypeAdapter, ValidationError + +from osc_sdk_python.exceptions import SdkResponseError, SdkValidationError +from osc_sdk_python.runtime.request import RequestSpec +from .models import ( + AcceptNetPeeringRequest, + AcceptNetPeeringResponse, + AddUserToUserGroupRequest, + AddUserToUserGroupResponse, + CheckAuthenticationRequest, + CheckAuthenticationResponse, + CreateAccessKeyRequest, + CreateAccessKeyResponse, + CreateAccountRequest, + CreateAccountResponse, + CreateApiAccessRuleRequest, + CreateApiAccessRuleResponse, + CreateCaRequest, + CreateCaResponse, + CreateClientGatewayRequest, + CreateClientGatewayResponse, + CreateDedicatedGroupRequest, + CreateDedicatedGroupResponse, + CreateDhcpOptionsRequest, + CreateDhcpOptionsResponse, + CreateDirectLinkInterfaceRequest, + CreateDirectLinkInterfaceResponse, + CreateDirectLinkRequest, + CreateDirectLinkResponse, + CreateFlexibleGpuRequest, + CreateFlexibleGpuResponse, + CreateImageExportTaskRequest, + CreateImageExportTaskResponse, + CreateImageRequest, + CreateImageResponse, + CreateInternetServiceRequest, + CreateInternetServiceResponse, + CreateKeypairRequest, + CreateKeypairResponse, + CreateListenerRuleRequest, + CreateListenerRuleResponse, + CreateLoadBalancerListenersRequest, + CreateLoadBalancerListenersResponse, + CreateLoadBalancerPolicyRequest, + CreateLoadBalancerPolicyResponse, + CreateLoadBalancerRequest, + CreateLoadBalancerResponse, + CreateLoadBalancerTagsRequest, + CreateLoadBalancerTagsResponse, + CreateNatServiceRequest, + CreateNatServiceResponse, + CreateNetAccessPointRequest, + CreateNetAccessPointResponse, + CreateNetPeeringRequest, + CreateNetPeeringResponse, + CreateNetRequest, + CreateNetResponse, + CreateNicRequest, + CreateNicResponse, + CreatePolicyRequest, + CreatePolicyResponse, + CreatePolicyVersionRequest, + CreatePolicyVersionResponse, + CreateProductTypeRequest, + CreateProductTypeResponse, + CreatePublicIpRequest, + CreatePublicIpResponse, + CreateRouteRequest, + CreateRouteResponse, + CreateRouteTableRequest, + CreateRouteTableResponse, + CreateSecurityGroupRequest, + CreateSecurityGroupResponse, + CreateSecurityGroupRuleRequest, + CreateSecurityGroupRuleResponse, + CreateServerCertificateRequest, + CreateServerCertificateResponse, + CreateSnapshotExportTaskRequest, + CreateSnapshotExportTaskResponse, + CreateSnapshotRequest, + CreateSnapshotResponse, + CreateSubnetRequest, + CreateSubnetResponse, + CreateTagsRequest, + CreateTagsResponse, + CreateUserGroupRequest, + CreateUserGroupResponse, + CreateUserRequest, + CreateUserResponse, + CreateVirtualGatewayRequest, + CreateVirtualGatewayResponse, + CreateVmGroupRequest, + CreateVmGroupResponse, + CreateVmTemplateRequest, + CreateVmTemplateResponse, + CreateVmsRequest, + CreateVmsResponse, + CreateVolumeRequest, + CreateVolumeResponse, + CreateVpnConnectionRequest, + CreateVpnConnectionResponse, + CreateVpnConnectionRouteRequest, + CreateVpnConnectionRouteResponse, + DeleteAccessKeyRequest, + DeleteAccessKeyResponse, + DeleteApiAccessRuleRequest, + DeleteApiAccessRuleResponse, + DeleteCaRequest, + DeleteCaResponse, + DeleteClientGatewayRequest, + DeleteClientGatewayResponse, + DeleteDedicatedGroupRequest, + DeleteDedicatedGroupResponse, + DeleteDhcpOptionsRequest, + DeleteDhcpOptionsResponse, + DeleteDirectLinkInterfaceRequest, + DeleteDirectLinkInterfaceResponse, + DeleteDirectLinkRequest, + DeleteDirectLinkResponse, + DeleteExportTaskRequest, + DeleteExportTaskResponse, + DeleteFlexibleGpuRequest, + DeleteFlexibleGpuResponse, + DeleteImageRequest, + DeleteImageResponse, + DeleteInternetServiceRequest, + DeleteInternetServiceResponse, + DeleteKeypairRequest, + DeleteKeypairResponse, + DeleteListenerRuleRequest, + DeleteListenerRuleResponse, + DeleteLoadBalancerListenersRequest, + DeleteLoadBalancerListenersResponse, + DeleteLoadBalancerPolicyRequest, + DeleteLoadBalancerPolicyResponse, + DeleteLoadBalancerRequest, + DeleteLoadBalancerResponse, + DeleteLoadBalancerTagsRequest, + DeleteLoadBalancerTagsResponse, + DeleteNatServiceRequest, + DeleteNatServiceResponse, + DeleteNetAccessPointRequest, + DeleteNetAccessPointResponse, + DeleteNetPeeringRequest, + DeleteNetPeeringResponse, + DeleteNetRequest, + DeleteNetResponse, + DeleteNicRequest, + DeleteNicResponse, + DeletePolicyRequest, + DeletePolicyResponse, + DeletePolicyVersionRequest, + DeletePolicyVersionResponse, + DeleteProductTypeRequest, + DeleteProductTypeResponse, + DeletePublicIpRequest, + DeletePublicIpResponse, + DeleteRouteRequest, + DeleteRouteResponse, + DeleteRouteTableRequest, + DeleteRouteTableResponse, + DeleteSecurityGroupRequest, + DeleteSecurityGroupResponse, + DeleteSecurityGroupRuleRequest, + DeleteSecurityGroupRuleResponse, + DeleteServerCertificateRequest, + DeleteServerCertificateResponse, + DeleteSnapshotRequest, + DeleteSnapshotResponse, + DeleteSubnetRequest, + DeleteSubnetResponse, + DeleteTagsRequest, + DeleteTagsResponse, + DeleteUserGroupPolicyRequest, + DeleteUserGroupPolicyResponse, + DeleteUserGroupRequest, + DeleteUserGroupResponse, + DeleteUserPolicyRequest, + DeleteUserPolicyResponse, + DeleteUserRequest, + DeleteUserResponse, + DeleteVirtualGatewayRequest, + DeleteVirtualGatewayResponse, + DeleteVmGroupRequest, + DeleteVmGroupResponse, + DeleteVmTemplateRequest, + DeleteVmTemplateResponse, + DeleteVmsRequest, + DeleteVmsResponse, + DeleteVolumeRequest, + DeleteVolumeResponse, + DeleteVpnConnectionRequest, + DeleteVpnConnectionResponse, + DeleteVpnConnectionRouteRequest, + DeleteVpnConnectionRouteResponse, + DeregisterVmsInLoadBalancerRequest, + DeregisterVmsInLoadBalancerResponse, + DisableOutscaleLoginPerUsersRequest, + DisableOutscaleLoginPerUsersResponse, + DisableOutscaleLoginRequest, + DisableOutscaleLoginResponse, + EnableOutscaleLoginForUsersRequest, + EnableOutscaleLoginForUsersResponse, + EnableOutscaleLoginPerUsersRequest, + EnableOutscaleLoginPerUsersResponse, + EnableOutscaleLoginRequest, + EnableOutscaleLoginResponse, + LinkFlexibleGpuRequest, + LinkFlexibleGpuResponse, + LinkInternetServiceRequest, + LinkInternetServiceResponse, + LinkLoadBalancerBackendMachinesRequest, + LinkLoadBalancerBackendMachinesResponse, + LinkManagedPolicyToUserGroupRequest, + LinkManagedPolicyToUserGroupResponse, + LinkNicRequest, + LinkNicResponse, + LinkPolicyRequest, + LinkPolicyResponse, + LinkPrivateIpsRequest, + LinkPrivateIpsResponse, + LinkPublicIpRequest, + LinkPublicIpResponse, + LinkRouteTableRequest, + LinkRouteTableResponse, + LinkVirtualGatewayRequest, + LinkVirtualGatewayResponse, + LinkVolumeRequest, + LinkVolumeResponse, + PutUserGroupPolicyRequest, + PutUserGroupPolicyResponse, + PutUserPolicyRequest, + PutUserPolicyResponse, + ReadAccessKeysRequest, + ReadAccessKeysResponse, + ReadAccountsRequest, + ReadAccountsResponse, + ReadAdminPasswordRequest, + ReadAdminPasswordResponse, + ReadApiAccessPolicyRequest, + ReadApiAccessPolicyResponse, + ReadApiAccessRulesRequest, + ReadApiAccessRulesResponse, + ReadApiLogsRequest, + ReadApiLogsResponse, + ReadCO2EmissionAccountRequest, + ReadCO2EmissionAccountResponse, + ReadCasRequest, + ReadCasResponse, + ReadCatalogRequest, + ReadCatalogResponse, + ReadCatalogsRequest, + ReadCatalogsResponse, + ReadClientGatewaysRequest, + ReadClientGatewaysResponse, + ReadConsoleOutputRequest, + ReadConsoleOutputResponse, + ReadConsumptionAccountRequest, + ReadConsumptionAccountResponse, + ReadDedicatedGroupsRequest, + ReadDedicatedGroupsResponse, + ReadDhcpOptionsRequest, + ReadDhcpOptionsResponse, + ReadDirectLinkInterfacesRequest, + ReadDirectLinkInterfacesResponse, + ReadDirectLinksRequest, + ReadDirectLinksResponse, + ReadEntitiesLinkedToPolicyRequest, + ReadEntitiesLinkedToPolicyResponse, + ReadFlexibleGpuCatalogRequest, + ReadFlexibleGpuCatalogResponse, + ReadFlexibleGpusRequest, + ReadFlexibleGpusResponse, + ReadImageExportTasksRequest, + ReadImageExportTasksResponse, + ReadImagesRequest, + ReadImagesResponse, + ReadInternetServicesRequest, + ReadInternetServicesResponse, + ReadKeypairsRequest, + ReadKeypairsResponse, + ReadLinkedPoliciesRequest, + ReadLinkedPoliciesResponse, + ReadListenerRulesRequest, + ReadListenerRulesResponse, + ReadLoadBalancerTagsRequest, + ReadLoadBalancerTagsResponse, + ReadLoadBalancersRequest, + ReadLoadBalancersResponse, + ReadLocationsRequest, + ReadLocationsResponse, + ReadManagedPoliciesLinkedToUserGroupRequest, + ReadManagedPoliciesLinkedToUserGroupResponse, + ReadNatServicesRequest, + ReadNatServicesResponse, + ReadNetAccessPointServicesRequest, + ReadNetAccessPointServicesResponse, + ReadNetAccessPointsRequest, + ReadNetAccessPointsResponse, + ReadNetPeeringsRequest, + ReadNetPeeringsResponse, + ReadNetsRequest, + ReadNetsResponse, + ReadNicsRequest, + ReadNicsResponse, + ReadPoliciesRequest, + ReadPoliciesResponse, + ReadPolicyRequest, + ReadPolicyResponse, + ReadPolicyVersionRequest, + ReadPolicyVersionResponse, + ReadPolicyVersionsRequest, + ReadPolicyVersionsResponse, + ReadProductTypesRequest, + ReadProductTypesResponse, + ReadPublicCatalogRequest, + ReadPublicCatalogResponse, + ReadPublicIpRangesRequest, + ReadPublicIpRangesResponse, + ReadPublicIpsRequest, + ReadPublicIpsResponse, + ReadQuotasRequest, + ReadQuotasResponse, + ReadRegionsRequest, + ReadRegionsResponse, + ReadRouteTablesRequest, + ReadRouteTablesResponse, + ReadSecurityGroupsRequest, + ReadSecurityGroupsResponse, + ReadServerCertificatesRequest, + ReadServerCertificatesResponse, + ReadSnapshotExportTasksRequest, + ReadSnapshotExportTasksResponse, + ReadSnapshotsRequest, + ReadSnapshotsResponse, + ReadSubnetsRequest, + ReadSubnetsResponse, + ReadSubregionsRequest, + ReadSubregionsResponse, + ReadTagsRequest, + ReadTagsResponse, + ReadUnitPriceRequest, + ReadUnitPriceResponse, + ReadUserGroupPoliciesRequest, + ReadUserGroupPoliciesResponse, + ReadUserGroupPolicyRequest, + ReadUserGroupPolicyResponse, + ReadUserGroupRequest, + ReadUserGroupResponse, + ReadUserGroupsPerUserRequest, + ReadUserGroupsPerUserResponse, + ReadUserGroupsRequest, + ReadUserGroupsResponse, + ReadUserPoliciesRequest, + ReadUserPoliciesResponse, + ReadUserPolicyRequest, + ReadUserPolicyResponse, + ReadUsersRequest, + ReadUsersResponse, + ReadVirtualGatewaysRequest, + ReadVirtualGatewaysResponse, + ReadVmGroupsRequest, + ReadVmGroupsResponse, + ReadVmTemplatesRequest, + ReadVmTemplatesResponse, + ReadVmTypesRequest, + ReadVmTypesResponse, + ReadVmsHealthRequest, + ReadVmsHealthResponse, + ReadVmsRequest, + ReadVmsResponse, + ReadVmsStateRequest, + ReadVmsStateResponse, + ReadVmsStopHistoryRequest, + ReadVmsStopHistoryResponse, + ReadVolumeUpdateTasksRequest, + ReadVolumeUpdateTasksResponse, + ReadVolumesRequest, + ReadVolumesResponse, + ReadVpnConnectionsRequest, + ReadVpnConnectionsResponse, + RebootVmsRequest, + RebootVmsResponse, + RegisterVmsInLoadBalancerRequest, + RegisterVmsInLoadBalancerResponse, + RejectNetPeeringRequest, + RejectNetPeeringResponse, + RemoveUserFromUserGroupRequest, + RemoveUserFromUserGroupResponse, + ScaleDownVmGroupRequest, + ScaleDownVmGroupResponse, + ScaleUpVmGroupRequest, + ScaleUpVmGroupResponse, + SetDefaultPolicyVersionRequest, + SetDefaultPolicyVersionResponse, + StartVmsRequest, + StartVmsResponse, + StopVmsRequest, + StopVmsResponse, + UnlinkFlexibleGpuRequest, + UnlinkFlexibleGpuResponse, + UnlinkInternetServiceRequest, + UnlinkInternetServiceResponse, + UnlinkLoadBalancerBackendMachinesRequest, + UnlinkLoadBalancerBackendMachinesResponse, + UnlinkManagedPolicyFromUserGroupRequest, + UnlinkManagedPolicyFromUserGroupResponse, + UnlinkNicRequest, + UnlinkNicResponse, + UnlinkPolicyRequest, + UnlinkPolicyResponse, + UnlinkPrivateIpsRequest, + UnlinkPrivateIpsResponse, + UnlinkPublicIpRequest, + UnlinkPublicIpResponse, + UnlinkRouteTableRequest, + UnlinkRouteTableResponse, + UnlinkVirtualGatewayRequest, + UnlinkVirtualGatewayResponse, + UnlinkVolumeRequest, + UnlinkVolumeResponse, + UpdateAccessKeyRequest, + UpdateAccessKeyResponse, + UpdateAccountRequest, + UpdateAccountResponse, + UpdateApiAccessPolicyRequest, + UpdateApiAccessPolicyResponse, + UpdateApiAccessRuleRequest, + UpdateApiAccessRuleResponse, + UpdateCaRequest, + UpdateCaResponse, + UpdateDedicatedGroupRequest, + UpdateDedicatedGroupResponse, + UpdateDirectLinkInterfaceRequest, + UpdateDirectLinkInterfaceResponse, + UpdateFlexibleGpuRequest, + UpdateFlexibleGpuResponse, + UpdateImageRequest, + UpdateImageResponse, + UpdateListenerRuleRequest, + UpdateListenerRuleResponse, + UpdateLoadBalancerRequest, + UpdateLoadBalancerResponse, + UpdateNetAccessPointRequest, + UpdateNetAccessPointResponse, + UpdateNetRequest, + UpdateNetResponse, + UpdateNicRequest, + UpdateNicResponse, + UpdateRoutePropagationRequest, + UpdateRoutePropagationResponse, + UpdateRouteRequest, + UpdateRouteResponse, + UpdateRouteTableLinkRequest, + UpdateRouteTableLinkResponse, + UpdateServerCertificateRequest, + UpdateServerCertificateResponse, + UpdateSnapshotRequest, + UpdateSnapshotResponse, + UpdateSubnetRequest, + UpdateSubnetResponse, + UpdateUserGroupRequest, + UpdateUserGroupResponse, + UpdateUserRequest, + UpdateUserResponse, + UpdateVmGroupRequest, + UpdateVmGroupResponse, + UpdateVmRequest, + UpdateVmResponse, + UpdateVmTemplateRequest, + UpdateVmTemplateResponse, + UpdateVolumeRequest, + UpdateVolumeResponse, + UpdateVpnConnectionRequest, + UpdateVpnConnectionResponse, +) + + +def _dump_json_body(value: Any) -> Any: + if hasattr(value, "model_dump"): + return value.model_dump(exclude_none=True, by_alias=True) + return value + + +def _validate_request(model: type, value: Any) -> Any: + try: + if value is None: + return model() + if isinstance(value, model): + return value + return TypeAdapter(model).validate_python(value) + except ValidationError as error: + raise SdkValidationError(str(error)) from error + + +def _validate_response(model: type, value: Any) -> Any: + try: + return TypeAdapter(model).validate_python(value) + except ValidationError as error: + raise SdkResponseError(str(error)) from error + + +class AsyncOscTypedMixin: + async def accept_net_peering( + self, + request: AcceptNetPeeringRequest | None = None, + ) -> AcceptNetPeeringResponse: + request = _validate_request(AcceptNetPeeringRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/AcceptNetPeering", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(AcceptNetPeeringResponse, response) + + async def add_user_to_user_group( + self, + request: AddUserToUserGroupRequest | None = None, + ) -> AddUserToUserGroupResponse: + request = _validate_request(AddUserToUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/AddUserToUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(AddUserToUserGroupResponse, response) + + async def check_authentication( + self, + request: CheckAuthenticationRequest | None = None, + ) -> CheckAuthenticationResponse: + request = _validate_request(CheckAuthenticationRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CheckAuthentication", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CheckAuthenticationResponse, response) + + async def create_access_key( + self, + request: CreateAccessKeyRequest | None = None, + ) -> CreateAccessKeyResponse: + request = _validate_request(CreateAccessKeyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateAccessKey", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateAccessKeyResponse, response) + + async def create_account( + self, + request: CreateAccountRequest | None = None, + ) -> CreateAccountResponse: + request = _validate_request(CreateAccountRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateAccount", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateAccountResponse, response) + + async def create_api_access_rule( + self, + request: CreateApiAccessRuleRequest | None = None, + ) -> CreateApiAccessRuleResponse: + request = _validate_request(CreateApiAccessRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateApiAccessRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateApiAccessRuleResponse, response) + + async def create_ca( + self, + request: CreateCaRequest | None = None, + ) -> CreateCaResponse: + request = _validate_request(CreateCaRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateCa", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateCaResponse, response) + + async def create_client_gateway( + self, + request: CreateClientGatewayRequest | None = None, + ) -> CreateClientGatewayResponse: + request = _validate_request(CreateClientGatewayRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateClientGateway", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateClientGatewayResponse, response) + + async def create_dedicated_group( + self, + request: CreateDedicatedGroupRequest | None = None, + ) -> CreateDedicatedGroupResponse: + request = _validate_request(CreateDedicatedGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateDedicatedGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateDedicatedGroupResponse, response) + + async def create_dhcp_options( + self, + request: CreateDhcpOptionsRequest | None = None, + ) -> CreateDhcpOptionsResponse: + request = _validate_request(CreateDhcpOptionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateDhcpOptions", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateDhcpOptionsResponse, response) + + async def create_direct_link( + self, + request: CreateDirectLinkRequest | None = None, + ) -> CreateDirectLinkResponse: + request = _validate_request(CreateDirectLinkRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateDirectLink", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateDirectLinkResponse, response) + + async def create_direct_link_interface( + self, + request: CreateDirectLinkInterfaceRequest | None = None, + ) -> CreateDirectLinkInterfaceResponse: + request = _validate_request(CreateDirectLinkInterfaceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateDirectLinkInterface", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateDirectLinkInterfaceResponse, response) + + async def create_flexible_gpu( + self, + request: CreateFlexibleGpuRequest | None = None, + ) -> CreateFlexibleGpuResponse: + request = _validate_request(CreateFlexibleGpuRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateFlexibleGpu", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateFlexibleGpuResponse, response) + + async def create_image( + self, + request: CreateImageRequest | None = None, + ) -> CreateImageResponse: + request = _validate_request(CreateImageRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateImage", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateImageResponse, response) + + async def create_image_export_task( + self, + request: CreateImageExportTaskRequest | None = None, + ) -> CreateImageExportTaskResponse: + request = _validate_request(CreateImageExportTaskRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateImageExportTask", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateImageExportTaskResponse, response) + + async def create_internet_service( + self, + request: CreateInternetServiceRequest | None = None, + ) -> CreateInternetServiceResponse: + request = _validate_request(CreateInternetServiceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateInternetService", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateInternetServiceResponse, response) + + async def create_keypair( + self, + request: CreateKeypairRequest | None = None, + ) -> CreateKeypairResponse: + request = _validate_request(CreateKeypairRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateKeypair", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateKeypairResponse, response) + + async def create_listener_rule( + self, + request: CreateListenerRuleRequest | None = None, + ) -> CreateListenerRuleResponse: + request = _validate_request(CreateListenerRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateListenerRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateListenerRuleResponse, response) + + async def create_load_balancer( + self, + request: CreateLoadBalancerRequest | None = None, + ) -> CreateLoadBalancerResponse: + request = _validate_request(CreateLoadBalancerRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateLoadBalancer", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateLoadBalancerResponse, response) + + async def create_load_balancer_listeners( + self, + request: CreateLoadBalancerListenersRequest | None = None, + ) -> CreateLoadBalancerListenersResponse: + request = _validate_request(CreateLoadBalancerListenersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateLoadBalancerListeners", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateLoadBalancerListenersResponse, response) + + async def create_load_balancer_policy( + self, + request: CreateLoadBalancerPolicyRequest | None = None, + ) -> CreateLoadBalancerPolicyResponse: + request = _validate_request(CreateLoadBalancerPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateLoadBalancerPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateLoadBalancerPolicyResponse, response) + + async def create_load_balancer_tags( + self, + request: CreateLoadBalancerTagsRequest | None = None, + ) -> CreateLoadBalancerTagsResponse: + request = _validate_request(CreateLoadBalancerTagsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateLoadBalancerTags", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateLoadBalancerTagsResponse, response) + + async def create_nat_service( + self, + request: CreateNatServiceRequest | None = None, + ) -> CreateNatServiceResponse: + request = _validate_request(CreateNatServiceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateNatService", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateNatServiceResponse, response) + + async def create_net( + self, + request: CreateNetRequest | None = None, + ) -> CreateNetResponse: + request = _validate_request(CreateNetRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateNet", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateNetResponse, response) + + async def create_net_access_point( + self, + request: CreateNetAccessPointRequest | None = None, + ) -> CreateNetAccessPointResponse: + request = _validate_request(CreateNetAccessPointRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateNetAccessPoint", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateNetAccessPointResponse, response) + + async def create_net_peering( + self, + request: CreateNetPeeringRequest | None = None, + ) -> CreateNetPeeringResponse: + request = _validate_request(CreateNetPeeringRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateNetPeering", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateNetPeeringResponse, response) + + async def create_nic( + self, + request: CreateNicRequest | None = None, + ) -> CreateNicResponse: + request = _validate_request(CreateNicRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateNic", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateNicResponse, response) + + async def create_policy( + self, + request: CreatePolicyRequest | None = None, + ) -> CreatePolicyResponse: + request = _validate_request(CreatePolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreatePolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreatePolicyResponse, response) + + async def create_policy_version( + self, + request: CreatePolicyVersionRequest | None = None, + ) -> CreatePolicyVersionResponse: + request = _validate_request(CreatePolicyVersionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreatePolicyVersion", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreatePolicyVersionResponse, response) + + async def create_product_type( + self, + request: CreateProductTypeRequest | None = None, + ) -> CreateProductTypeResponse: + request = _validate_request(CreateProductTypeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateProductType", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateProductTypeResponse, response) + + async def create_public_ip( + self, + request: CreatePublicIpRequest | None = None, + ) -> CreatePublicIpResponse: + request = _validate_request(CreatePublicIpRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreatePublicIp", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreatePublicIpResponse, response) + + async def create_route( + self, + request: CreateRouteRequest | None = None, + ) -> CreateRouteResponse: + request = _validate_request(CreateRouteRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateRoute", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateRouteResponse, response) + + async def create_route_table( + self, + request: CreateRouteTableRequest | None = None, + ) -> CreateRouteTableResponse: + request = _validate_request(CreateRouteTableRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateRouteTable", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateRouteTableResponse, response) + + async def create_security_group( + self, + request: CreateSecurityGroupRequest | None = None, + ) -> CreateSecurityGroupResponse: + request = _validate_request(CreateSecurityGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateSecurityGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateSecurityGroupResponse, response) + + async def create_security_group_rule( + self, + request: CreateSecurityGroupRuleRequest | None = None, + ) -> CreateSecurityGroupRuleResponse: + request = _validate_request(CreateSecurityGroupRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateSecurityGroupRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateSecurityGroupRuleResponse, response) + + async def create_server_certificate( + self, + request: CreateServerCertificateRequest | None = None, + ) -> CreateServerCertificateResponse: + request = _validate_request(CreateServerCertificateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateServerCertificate", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateServerCertificateResponse, response) + + async def create_snapshot( + self, + request: CreateSnapshotRequest | None = None, + ) -> CreateSnapshotResponse: + request = _validate_request(CreateSnapshotRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateSnapshot", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateSnapshotResponse, response) + + async def create_snapshot_export_task( + self, + request: CreateSnapshotExportTaskRequest | None = None, + ) -> CreateSnapshotExportTaskResponse: + request = _validate_request(CreateSnapshotExportTaskRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateSnapshotExportTask", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateSnapshotExportTaskResponse, response) + + async def create_subnet( + self, + request: CreateSubnetRequest | None = None, + ) -> CreateSubnetResponse: + request = _validate_request(CreateSubnetRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateSubnet", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateSubnetResponse, response) + + async def create_tags( + self, + request: CreateTagsRequest | None = None, + ) -> CreateTagsResponse: + request = _validate_request(CreateTagsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateTags", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateTagsResponse, response) + + async def create_user( + self, + request: CreateUserRequest | None = None, + ) -> CreateUserResponse: + request = _validate_request(CreateUserRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateUser", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateUserResponse, response) + + async def create_user_group( + self, + request: CreateUserGroupRequest | None = None, + ) -> CreateUserGroupResponse: + request = _validate_request(CreateUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateUserGroupResponse, response) + + async def create_virtual_gateway( + self, + request: CreateVirtualGatewayRequest | None = None, + ) -> CreateVirtualGatewayResponse: + request = _validate_request(CreateVirtualGatewayRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVirtualGateway", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVirtualGatewayResponse, response) + + async def create_vm_group( + self, + request: CreateVmGroupRequest | None = None, + ) -> CreateVmGroupResponse: + request = _validate_request(CreateVmGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVmGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVmGroupResponse, response) + + async def create_vm_template( + self, + request: CreateVmTemplateRequest | None = None, + ) -> CreateVmTemplateResponse: + request = _validate_request(CreateVmTemplateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVmTemplate", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVmTemplateResponse, response) + + async def create_vms( + self, + request: CreateVmsRequest | None = None, + ) -> CreateVmsResponse: + request = _validate_request(CreateVmsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVms", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVmsResponse, response) + + async def create_volume( + self, + request: CreateVolumeRequest | None = None, + ) -> CreateVolumeResponse: + request = _validate_request(CreateVolumeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVolume", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVolumeResponse, response) + + async def create_vpn_connection( + self, + request: CreateVpnConnectionRequest | None = None, + ) -> CreateVpnConnectionResponse: + request = _validate_request(CreateVpnConnectionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVpnConnection", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVpnConnectionResponse, response) + + async def create_vpn_connection_route( + self, + request: CreateVpnConnectionRouteRequest | None = None, + ) -> CreateVpnConnectionRouteResponse: + request = _validate_request(CreateVpnConnectionRouteRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/CreateVpnConnectionRoute", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(CreateVpnConnectionRouteResponse, response) + + async def delete_access_key( + self, + request: DeleteAccessKeyRequest | None = None, + ) -> DeleteAccessKeyResponse: + request = _validate_request(DeleteAccessKeyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteAccessKey", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteAccessKeyResponse, response) + + async def delete_api_access_rule( + self, + request: DeleteApiAccessRuleRequest | None = None, + ) -> DeleteApiAccessRuleResponse: + request = _validate_request(DeleteApiAccessRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteApiAccessRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteApiAccessRuleResponse, response) + + async def delete_ca( + self, + request: DeleteCaRequest | None = None, + ) -> DeleteCaResponse: + request = _validate_request(DeleteCaRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteCa", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteCaResponse, response) + + async def delete_client_gateway( + self, + request: DeleteClientGatewayRequest | None = None, + ) -> DeleteClientGatewayResponse: + request = _validate_request(DeleteClientGatewayRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteClientGateway", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteClientGatewayResponse, response) + + async def delete_dedicated_group( + self, + request: DeleteDedicatedGroupRequest | None = None, + ) -> DeleteDedicatedGroupResponse: + request = _validate_request(DeleteDedicatedGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteDedicatedGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteDedicatedGroupResponse, response) + + async def delete_dhcp_options( + self, + request: DeleteDhcpOptionsRequest | None = None, + ) -> DeleteDhcpOptionsResponse: + request = _validate_request(DeleteDhcpOptionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteDhcpOptions", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteDhcpOptionsResponse, response) + + async def delete_direct_link( + self, + request: DeleteDirectLinkRequest | None = None, + ) -> DeleteDirectLinkResponse: + request = _validate_request(DeleteDirectLinkRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteDirectLink", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteDirectLinkResponse, response) + + async def delete_direct_link_interface( + self, + request: DeleteDirectLinkInterfaceRequest | None = None, + ) -> DeleteDirectLinkInterfaceResponse: + request = _validate_request(DeleteDirectLinkInterfaceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteDirectLinkInterface", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteDirectLinkInterfaceResponse, response) + + async def delete_export_task( + self, + request: DeleteExportTaskRequest | None = None, + ) -> DeleteExportTaskResponse: + request = _validate_request(DeleteExportTaskRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteExportTask", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteExportTaskResponse, response) + + async def delete_flexible_gpu( + self, + request: DeleteFlexibleGpuRequest | None = None, + ) -> DeleteFlexibleGpuResponse: + request = _validate_request(DeleteFlexibleGpuRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteFlexibleGpu", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteFlexibleGpuResponse, response) + + async def delete_image( + self, + request: DeleteImageRequest | None = None, + ) -> DeleteImageResponse: + request = _validate_request(DeleteImageRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteImage", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteImageResponse, response) + + async def delete_internet_service( + self, + request: DeleteInternetServiceRequest | None = None, + ) -> DeleteInternetServiceResponse: + request = _validate_request(DeleteInternetServiceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteInternetService", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteInternetServiceResponse, response) + + async def delete_keypair( + self, + request: DeleteKeypairRequest | None = None, + ) -> DeleteKeypairResponse: + request = _validate_request(DeleteKeypairRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteKeypair", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteKeypairResponse, response) + + async def delete_listener_rule( + self, + request: DeleteListenerRuleRequest | None = None, + ) -> DeleteListenerRuleResponse: + request = _validate_request(DeleteListenerRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteListenerRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteListenerRuleResponse, response) + + async def delete_load_balancer( + self, + request: DeleteLoadBalancerRequest | None = None, + ) -> DeleteLoadBalancerResponse: + request = _validate_request(DeleteLoadBalancerRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteLoadBalancer", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteLoadBalancerResponse, response) + + async def delete_load_balancer_listeners( + self, + request: DeleteLoadBalancerListenersRequest | None = None, + ) -> DeleteLoadBalancerListenersResponse: + request = _validate_request(DeleteLoadBalancerListenersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteLoadBalancerListeners", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteLoadBalancerListenersResponse, response) + + async def delete_load_balancer_policy( + self, + request: DeleteLoadBalancerPolicyRequest | None = None, + ) -> DeleteLoadBalancerPolicyResponse: + request = _validate_request(DeleteLoadBalancerPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteLoadBalancerPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteLoadBalancerPolicyResponse, response) + + async def delete_load_balancer_tags( + self, + request: DeleteLoadBalancerTagsRequest | None = None, + ) -> DeleteLoadBalancerTagsResponse: + request = _validate_request(DeleteLoadBalancerTagsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteLoadBalancerTags", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteLoadBalancerTagsResponse, response) + + async def delete_nat_service( + self, + request: DeleteNatServiceRequest | None = None, + ) -> DeleteNatServiceResponse: + request = _validate_request(DeleteNatServiceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteNatService", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteNatServiceResponse, response) + + async def delete_net( + self, + request: DeleteNetRequest | None = None, + ) -> DeleteNetResponse: + request = _validate_request(DeleteNetRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteNet", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteNetResponse, response) + + async def delete_net_access_point( + self, + request: DeleteNetAccessPointRequest | None = None, + ) -> DeleteNetAccessPointResponse: + request = _validate_request(DeleteNetAccessPointRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteNetAccessPoint", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteNetAccessPointResponse, response) + + async def delete_net_peering( + self, + request: DeleteNetPeeringRequest | None = None, + ) -> DeleteNetPeeringResponse: + request = _validate_request(DeleteNetPeeringRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteNetPeering", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteNetPeeringResponse, response) + + async def delete_nic( + self, + request: DeleteNicRequest | None = None, + ) -> DeleteNicResponse: + request = _validate_request(DeleteNicRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteNic", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteNicResponse, response) + + async def delete_policy( + self, + request: DeletePolicyRequest | None = None, + ) -> DeletePolicyResponse: + request = _validate_request(DeletePolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeletePolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeletePolicyResponse, response) + + async def delete_policy_version( + self, + request: DeletePolicyVersionRequest | None = None, + ) -> DeletePolicyVersionResponse: + request = _validate_request(DeletePolicyVersionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeletePolicyVersion", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeletePolicyVersionResponse, response) + + async def delete_product_type( + self, + request: DeleteProductTypeRequest | None = None, + ) -> DeleteProductTypeResponse: + request = _validate_request(DeleteProductTypeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteProductType", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteProductTypeResponse, response) + + async def delete_public_ip( + self, + request: DeletePublicIpRequest | None = None, + ) -> DeletePublicIpResponse: + request = _validate_request(DeletePublicIpRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeletePublicIp", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeletePublicIpResponse, response) + + async def delete_route( + self, + request: DeleteRouteRequest | None = None, + ) -> DeleteRouteResponse: + request = _validate_request(DeleteRouteRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteRoute", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteRouteResponse, response) + + async def delete_route_table( + self, + request: DeleteRouteTableRequest | None = None, + ) -> DeleteRouteTableResponse: + request = _validate_request(DeleteRouteTableRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteRouteTable", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteRouteTableResponse, response) + + async def delete_security_group( + self, + request: DeleteSecurityGroupRequest | None = None, + ) -> DeleteSecurityGroupResponse: + request = _validate_request(DeleteSecurityGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteSecurityGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteSecurityGroupResponse, response) + + async def delete_security_group_rule( + self, + request: DeleteSecurityGroupRuleRequest | None = None, + ) -> DeleteSecurityGroupRuleResponse: + request = _validate_request(DeleteSecurityGroupRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteSecurityGroupRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteSecurityGroupRuleResponse, response) + + async def delete_server_certificate( + self, + request: DeleteServerCertificateRequest | None = None, + ) -> DeleteServerCertificateResponse: + request = _validate_request(DeleteServerCertificateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteServerCertificate", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteServerCertificateResponse, response) + + async def delete_snapshot( + self, + request: DeleteSnapshotRequest | None = None, + ) -> DeleteSnapshotResponse: + request = _validate_request(DeleteSnapshotRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteSnapshot", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteSnapshotResponse, response) + + async def delete_subnet( + self, + request: DeleteSubnetRequest | None = None, + ) -> DeleteSubnetResponse: + request = _validate_request(DeleteSubnetRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteSubnet", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteSubnetResponse, response) + + async def delete_tags( + self, + request: DeleteTagsRequest | None = None, + ) -> DeleteTagsResponse: + request = _validate_request(DeleteTagsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteTags", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteTagsResponse, response) + + async def delete_user( + self, + request: DeleteUserRequest | None = None, + ) -> DeleteUserResponse: + request = _validate_request(DeleteUserRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteUser", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteUserResponse, response) + + async def delete_user_group( + self, + request: DeleteUserGroupRequest | None = None, + ) -> DeleteUserGroupResponse: + request = _validate_request(DeleteUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteUserGroupResponse, response) + + async def delete_user_group_policy( + self, + request: DeleteUserGroupPolicyRequest | None = None, + ) -> DeleteUserGroupPolicyResponse: + request = _validate_request(DeleteUserGroupPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteUserGroupPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteUserGroupPolicyResponse, response) + + async def delete_user_policy( + self, + request: DeleteUserPolicyRequest | None = None, + ) -> DeleteUserPolicyResponse: + request = _validate_request(DeleteUserPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteUserPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteUserPolicyResponse, response) + + async def delete_virtual_gateway( + self, + request: DeleteVirtualGatewayRequest | None = None, + ) -> DeleteVirtualGatewayResponse: + request = _validate_request(DeleteVirtualGatewayRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVirtualGateway", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVirtualGatewayResponse, response) + + async def delete_vm_group( + self, + request: DeleteVmGroupRequest | None = None, + ) -> DeleteVmGroupResponse: + request = _validate_request(DeleteVmGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVmGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVmGroupResponse, response) + + async def delete_vm_template( + self, + request: DeleteVmTemplateRequest | None = None, + ) -> DeleteVmTemplateResponse: + request = _validate_request(DeleteVmTemplateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVmTemplate", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVmTemplateResponse, response) + + async def delete_vms( + self, + request: DeleteVmsRequest | None = None, + ) -> DeleteVmsResponse: + request = _validate_request(DeleteVmsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVms", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVmsResponse, response) + + async def delete_volume( + self, + request: DeleteVolumeRequest | None = None, + ) -> DeleteVolumeResponse: + request = _validate_request(DeleteVolumeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVolume", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVolumeResponse, response) + + async def delete_vpn_connection( + self, + request: DeleteVpnConnectionRequest | None = None, + ) -> DeleteVpnConnectionResponse: + request = _validate_request(DeleteVpnConnectionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVpnConnection", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVpnConnectionResponse, response) + + async def delete_vpn_connection_route( + self, + request: DeleteVpnConnectionRouteRequest | None = None, + ) -> DeleteVpnConnectionRouteResponse: + request = _validate_request(DeleteVpnConnectionRouteRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeleteVpnConnectionRoute", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeleteVpnConnectionRouteResponse, response) + + async def deregister_vms_in_load_balancer( + self, + request: DeregisterVmsInLoadBalancerRequest | None = None, + ) -> DeregisterVmsInLoadBalancerResponse: + request = _validate_request(DeregisterVmsInLoadBalancerRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DeregisterVmsInLoadBalancer", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DeregisterVmsInLoadBalancerResponse, response) + + async def disable_outscale_login( + self, + request: DisableOutscaleLoginRequest | None = None, + ) -> DisableOutscaleLoginResponse: + request = _validate_request(DisableOutscaleLoginRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DisableOutscaleLogin", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DisableOutscaleLoginResponse, response) + + async def disable_outscale_login_for_users( + self, + request: DisableOutscaleLoginRequest | None = None, + ) -> DisableOutscaleLoginResponse: + request = _validate_request(DisableOutscaleLoginRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DisableOutscaleLoginForUsers", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DisableOutscaleLoginResponse, response) + + async def disable_outscale_login_per_users( + self, + request: DisableOutscaleLoginPerUsersRequest | None = None, + ) -> DisableOutscaleLoginPerUsersResponse: + request = _validate_request(DisableOutscaleLoginPerUsersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/DisableOutscaleLoginPerUsers", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(DisableOutscaleLoginPerUsersResponse, response) + + async def enable_outscale_login( + self, + request: EnableOutscaleLoginRequest | None = None, + ) -> EnableOutscaleLoginResponse: + request = _validate_request(EnableOutscaleLoginRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/EnableOutscaleLogin", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(EnableOutscaleLoginResponse, response) + + async def enable_outscale_login_for_users( + self, + request: EnableOutscaleLoginForUsersRequest | None = None, + ) -> EnableOutscaleLoginForUsersResponse: + request = _validate_request(EnableOutscaleLoginForUsersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/EnableOutscaleLoginForUsers", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(EnableOutscaleLoginForUsersResponse, response) + + async def enable_outscale_login_per_users( + self, + request: EnableOutscaleLoginPerUsersRequest | None = None, + ) -> EnableOutscaleLoginPerUsersResponse: + request = _validate_request(EnableOutscaleLoginPerUsersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/EnableOutscaleLoginPerUsers", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(EnableOutscaleLoginPerUsersResponse, response) + + async def link_flexible_gpu( + self, + request: LinkFlexibleGpuRequest | None = None, + ) -> LinkFlexibleGpuResponse: + request = _validate_request(LinkFlexibleGpuRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkFlexibleGpu", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkFlexibleGpuResponse, response) + + async def link_internet_service( + self, + request: LinkInternetServiceRequest | None = None, + ) -> LinkInternetServiceResponse: + request = _validate_request(LinkInternetServiceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkInternetService", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkInternetServiceResponse, response) + + async def link_load_balancer_backend_machines( + self, + request: LinkLoadBalancerBackendMachinesRequest | None = None, + ) -> LinkLoadBalancerBackendMachinesResponse: + request = _validate_request(LinkLoadBalancerBackendMachinesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkLoadBalancerBackendMachines", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkLoadBalancerBackendMachinesResponse, response) + + async def link_managed_policy_to_user_group( + self, + request: LinkManagedPolicyToUserGroupRequest | None = None, + ) -> LinkManagedPolicyToUserGroupResponse: + request = _validate_request(LinkManagedPolicyToUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkManagedPolicyToUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkManagedPolicyToUserGroupResponse, response) + + async def link_nic( + self, + request: LinkNicRequest | None = None, + ) -> LinkNicResponse: + request = _validate_request(LinkNicRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkNic", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkNicResponse, response) + + async def link_policy( + self, + request: LinkPolicyRequest | None = None, + ) -> LinkPolicyResponse: + request = _validate_request(LinkPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkPolicyResponse, response) + + async def link_private_ips( + self, + request: LinkPrivateIpsRequest | None = None, + ) -> LinkPrivateIpsResponse: + request = _validate_request(LinkPrivateIpsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkPrivateIps", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkPrivateIpsResponse, response) + + async def link_public_ip( + self, + request: LinkPublicIpRequest | None = None, + ) -> LinkPublicIpResponse: + request = _validate_request(LinkPublicIpRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkPublicIp", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkPublicIpResponse, response) + + async def link_route_table( + self, + request: LinkRouteTableRequest | None = None, + ) -> LinkRouteTableResponse: + request = _validate_request(LinkRouteTableRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkRouteTable", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkRouteTableResponse, response) + + async def link_virtual_gateway( + self, + request: LinkVirtualGatewayRequest | None = None, + ) -> LinkVirtualGatewayResponse: + request = _validate_request(LinkVirtualGatewayRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkVirtualGateway", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkVirtualGatewayResponse, response) + + async def link_volume( + self, + request: LinkVolumeRequest | None = None, + ) -> LinkVolumeResponse: + request = _validate_request(LinkVolumeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/LinkVolume", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(LinkVolumeResponse, response) + + async def put_user_group_policy( + self, + request: PutUserGroupPolicyRequest | None = None, + ) -> PutUserGroupPolicyResponse: + request = _validate_request(PutUserGroupPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/PutUserGroupPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(PutUserGroupPolicyResponse, response) + + async def put_user_policy( + self, + request: PutUserPolicyRequest | None = None, + ) -> PutUserPolicyResponse: + request = _validate_request(PutUserPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/PutUserPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(PutUserPolicyResponse, response) + + async def read_access_keys( + self, + request: ReadAccessKeysRequest | None = None, + ) -> ReadAccessKeysResponse: + request = _validate_request(ReadAccessKeysRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadAccessKeys", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadAccessKeysResponse, response) + + async def read_accounts( + self, + request: ReadAccountsRequest | None = None, + ) -> ReadAccountsResponse: + request = _validate_request(ReadAccountsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadAccounts", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadAccountsResponse, response) + + async def read_admin_password( + self, + request: ReadAdminPasswordRequest | None = None, + ) -> ReadAdminPasswordResponse: + request = _validate_request(ReadAdminPasswordRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadAdminPassword", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadAdminPasswordResponse, response) + + async def read_api_access_policy( + self, + request: ReadApiAccessPolicyRequest | None = None, + ) -> ReadApiAccessPolicyResponse: + request = _validate_request(ReadApiAccessPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadApiAccessPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadApiAccessPolicyResponse, response) + + async def read_api_access_rules( + self, + request: ReadApiAccessRulesRequest | None = None, + ) -> ReadApiAccessRulesResponse: + request = _validate_request(ReadApiAccessRulesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadApiAccessRules", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadApiAccessRulesResponse, response) + + async def read_api_logs( + self, + request: ReadApiLogsRequest | None = None, + ) -> ReadApiLogsResponse: + request = _validate_request(ReadApiLogsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadApiLogs", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadApiLogsResponse, response) + + async def read_co2_emission_account( + self, + request: ReadCO2EmissionAccountRequest | None = None, + ) -> ReadCO2EmissionAccountResponse: + request = _validate_request(ReadCO2EmissionAccountRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadCO2EmissionAccount", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadCO2EmissionAccountResponse, response) + + async def read_cas( + self, + request: ReadCasRequest | None = None, + ) -> ReadCasResponse: + request = _validate_request(ReadCasRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadCas", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadCasResponse, response) + + async def read_catalog( + self, + request: ReadCatalogRequest | None = None, + ) -> ReadCatalogResponse: + request = _validate_request(ReadCatalogRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadCatalog", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadCatalogResponse, response) + + async def read_catalogs( + self, + request: ReadCatalogsRequest | None = None, + ) -> ReadCatalogsResponse: + request = _validate_request(ReadCatalogsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadCatalogs", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadCatalogsResponse, response) + + async def read_client_gateways( + self, + request: ReadClientGatewaysRequest | None = None, + ) -> ReadClientGatewaysResponse: + request = _validate_request(ReadClientGatewaysRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadClientGateways", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadClientGatewaysResponse, response) + + async def read_console_output( + self, + request: ReadConsoleOutputRequest | None = None, + ) -> ReadConsoleOutputResponse: + request = _validate_request(ReadConsoleOutputRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadConsoleOutput", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadConsoleOutputResponse, response) + + async def read_consumption_account( + self, + request: ReadConsumptionAccountRequest | None = None, + ) -> ReadConsumptionAccountResponse: + request = _validate_request(ReadConsumptionAccountRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadConsumptionAccount", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadConsumptionAccountResponse, response) + + async def read_dedicated_groups( + self, + request: ReadDedicatedGroupsRequest | None = None, + ) -> ReadDedicatedGroupsResponse: + request = _validate_request(ReadDedicatedGroupsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadDedicatedGroups", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadDedicatedGroupsResponse, response) + + async def read_dhcp_options( + self, + request: ReadDhcpOptionsRequest | None = None, + ) -> ReadDhcpOptionsResponse: + request = _validate_request(ReadDhcpOptionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadDhcpOptions", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadDhcpOptionsResponse, response) + + async def read_direct_link_interfaces( + self, + request: ReadDirectLinkInterfacesRequest | None = None, + ) -> ReadDirectLinkInterfacesResponse: + request = _validate_request(ReadDirectLinkInterfacesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadDirectLinkInterfaces", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadDirectLinkInterfacesResponse, response) + + async def read_direct_links( + self, + request: ReadDirectLinksRequest | None = None, + ) -> ReadDirectLinksResponse: + request = _validate_request(ReadDirectLinksRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadDirectLinks", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadDirectLinksResponse, response) + + async def read_entities_linked_to_policy( + self, + request: ReadEntitiesLinkedToPolicyRequest | None = None, + ) -> ReadEntitiesLinkedToPolicyResponse: + request = _validate_request(ReadEntitiesLinkedToPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadEntitiesLinkedToPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadEntitiesLinkedToPolicyResponse, response) + + async def read_flexible_gpu_catalog( + self, + request: ReadFlexibleGpuCatalogRequest | None = None, + ) -> ReadFlexibleGpuCatalogResponse: + request = _validate_request(ReadFlexibleGpuCatalogRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadFlexibleGpuCatalog", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadFlexibleGpuCatalogResponse, response) + + async def read_flexible_gpus( + self, + request: ReadFlexibleGpusRequest | None = None, + ) -> ReadFlexibleGpusResponse: + request = _validate_request(ReadFlexibleGpusRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadFlexibleGpus", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadFlexibleGpusResponse, response) + + async def read_image_export_tasks( + self, + request: ReadImageExportTasksRequest | None = None, + ) -> ReadImageExportTasksResponse: + request = _validate_request(ReadImageExportTasksRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadImageExportTasks", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadImageExportTasksResponse, response) + + async def read_images( + self, + request: ReadImagesRequest | None = None, + ) -> ReadImagesResponse: + request = _validate_request(ReadImagesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadImages", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadImagesResponse, response) + + async def read_internet_services( + self, + request: ReadInternetServicesRequest | None = None, + ) -> ReadInternetServicesResponse: + request = _validate_request(ReadInternetServicesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadInternetServices", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadInternetServicesResponse, response) + + async def read_keypairs( + self, + request: ReadKeypairsRequest | None = None, + ) -> ReadKeypairsResponse: + request = _validate_request(ReadKeypairsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadKeypairs", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadKeypairsResponse, response) + + async def read_linked_policies( + self, + request: ReadLinkedPoliciesRequest | None = None, + ) -> ReadLinkedPoliciesResponse: + request = _validate_request(ReadLinkedPoliciesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadLinkedPolicies", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadLinkedPoliciesResponse, response) + + async def read_listener_rules( + self, + request: ReadListenerRulesRequest | None = None, + ) -> ReadListenerRulesResponse: + request = _validate_request(ReadListenerRulesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadListenerRules", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadListenerRulesResponse, response) + + async def read_load_balancer_tags( + self, + request: ReadLoadBalancerTagsRequest | None = None, + ) -> ReadLoadBalancerTagsResponse: + request = _validate_request(ReadLoadBalancerTagsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadLoadBalancerTags", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadLoadBalancerTagsResponse, response) + + async def read_load_balancers( + self, + request: ReadLoadBalancersRequest | None = None, + ) -> ReadLoadBalancersResponse: + request = _validate_request(ReadLoadBalancersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadLoadBalancers", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadLoadBalancersResponse, response) + + async def read_locations( + self, + request: ReadLocationsRequest | None = None, + ) -> ReadLocationsResponse: + request = _validate_request(ReadLocationsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadLocations", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadLocationsResponse, response) + + async def read_managed_policies_linked_to_user_group( + self, + request: ReadManagedPoliciesLinkedToUserGroupRequest | None = None, + ) -> ReadManagedPoliciesLinkedToUserGroupResponse: + request = _validate_request(ReadManagedPoliciesLinkedToUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadManagedPoliciesLinkedToUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadManagedPoliciesLinkedToUserGroupResponse, response) + + async def read_nat_services( + self, + request: ReadNatServicesRequest | None = None, + ) -> ReadNatServicesResponse: + request = _validate_request(ReadNatServicesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadNatServices", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadNatServicesResponse, response) + + async def read_net_access_point_services( + self, + request: ReadNetAccessPointServicesRequest | None = None, + ) -> ReadNetAccessPointServicesResponse: + request = _validate_request(ReadNetAccessPointServicesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadNetAccessPointServices", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadNetAccessPointServicesResponse, response) + + async def read_net_access_points( + self, + request: ReadNetAccessPointsRequest | None = None, + ) -> ReadNetAccessPointsResponse: + request = _validate_request(ReadNetAccessPointsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadNetAccessPoints", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadNetAccessPointsResponse, response) + + async def read_net_peerings( + self, + request: ReadNetPeeringsRequest | None = None, + ) -> ReadNetPeeringsResponse: + request = _validate_request(ReadNetPeeringsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadNetPeerings", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadNetPeeringsResponse, response) + + async def read_nets( + self, + request: ReadNetsRequest | None = None, + ) -> ReadNetsResponse: + request = _validate_request(ReadNetsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadNets", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadNetsResponse, response) + + async def read_nics( + self, + request: ReadNicsRequest | None = None, + ) -> ReadNicsResponse: + request = _validate_request(ReadNicsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadNics", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadNicsResponse, response) + + async def read_policies( + self, + request: ReadPoliciesRequest | None = None, + ) -> ReadPoliciesResponse: + request = _validate_request(ReadPoliciesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPolicies", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPoliciesResponse, response) + + async def read_policy( + self, + request: ReadPolicyRequest | None = None, + ) -> ReadPolicyResponse: + request = _validate_request(ReadPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPolicyResponse, response) + + async def read_policy_version( + self, + request: ReadPolicyVersionRequest | None = None, + ) -> ReadPolicyVersionResponse: + request = _validate_request(ReadPolicyVersionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPolicyVersion", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPolicyVersionResponse, response) + + async def read_policy_versions( + self, + request: ReadPolicyVersionsRequest | None = None, + ) -> ReadPolicyVersionsResponse: + request = _validate_request(ReadPolicyVersionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPolicyVersions", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPolicyVersionsResponse, response) + + async def read_product_types( + self, + request: ReadProductTypesRequest | None = None, + ) -> ReadProductTypesResponse: + request = _validate_request(ReadProductTypesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadProductTypes", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadProductTypesResponse, response) + + async def read_public_catalog( + self, + request: ReadPublicCatalogRequest | None = None, + ) -> ReadPublicCatalogResponse: + request = _validate_request(ReadPublicCatalogRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPublicCatalog", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPublicCatalogResponse, response) + + async def read_public_ip_ranges( + self, + request: ReadPublicIpRangesRequest | None = None, + ) -> ReadPublicIpRangesResponse: + request = _validate_request(ReadPublicIpRangesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPublicIpRanges", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPublicIpRangesResponse, response) + + async def read_public_ips( + self, + request: ReadPublicIpsRequest | None = None, + ) -> ReadPublicIpsResponse: + request = _validate_request(ReadPublicIpsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadPublicIps", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadPublicIpsResponse, response) + + async def read_quotas( + self, + request: ReadQuotasRequest | None = None, + ) -> ReadQuotasResponse: + request = _validate_request(ReadQuotasRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadQuotas", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadQuotasResponse, response) + + async def read_regions( + self, + request: ReadRegionsRequest | None = None, + ) -> ReadRegionsResponse: + request = _validate_request(ReadRegionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadRegions", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadRegionsResponse, response) + + async def read_route_tables( + self, + request: ReadRouteTablesRequest | None = None, + ) -> ReadRouteTablesResponse: + request = _validate_request(ReadRouteTablesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadRouteTables", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadRouteTablesResponse, response) + + async def read_security_groups( + self, + request: ReadSecurityGroupsRequest | None = None, + ) -> ReadSecurityGroupsResponse: + request = _validate_request(ReadSecurityGroupsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadSecurityGroups", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadSecurityGroupsResponse, response) + + async def read_server_certificates( + self, + request: ReadServerCertificatesRequest | None = None, + ) -> ReadServerCertificatesResponse: + request = _validate_request(ReadServerCertificatesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadServerCertificates", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadServerCertificatesResponse, response) + + async def read_snapshot_export_tasks( + self, + request: ReadSnapshotExportTasksRequest | None = None, + ) -> ReadSnapshotExportTasksResponse: + request = _validate_request(ReadSnapshotExportTasksRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadSnapshotExportTasks", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadSnapshotExportTasksResponse, response) + + async def read_snapshots( + self, + request: ReadSnapshotsRequest | None = None, + ) -> ReadSnapshotsResponse: + request = _validate_request(ReadSnapshotsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadSnapshots", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadSnapshotsResponse, response) + + async def read_subnets( + self, + request: ReadSubnetsRequest | None = None, + ) -> ReadSubnetsResponse: + request = _validate_request(ReadSubnetsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadSubnets", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadSubnetsResponse, response) + + async def read_subregions( + self, + request: ReadSubregionsRequest | None = None, + ) -> ReadSubregionsResponse: + request = _validate_request(ReadSubregionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadSubregions", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadSubregionsResponse, response) + + async def read_tags( + self, + request: ReadTagsRequest | None = None, + ) -> ReadTagsResponse: + request = _validate_request(ReadTagsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadTags", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadTagsResponse, response) + + async def read_unit_price( + self, + request: ReadUnitPriceRequest | None = None, + ) -> ReadUnitPriceResponse: + request = _validate_request(ReadUnitPriceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUnitPrice", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUnitPriceResponse, response) + + async def read_user_group( + self, + request: ReadUserGroupRequest | None = None, + ) -> ReadUserGroupResponse: + request = _validate_request(ReadUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserGroupResponse, response) + + async def read_user_group_policies( + self, + request: ReadUserGroupPoliciesRequest | None = None, + ) -> ReadUserGroupPoliciesResponse: + request = _validate_request(ReadUserGroupPoliciesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserGroupPolicies", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserGroupPoliciesResponse, response) + + async def read_user_group_policy( + self, + request: ReadUserGroupPolicyRequest | None = None, + ) -> ReadUserGroupPolicyResponse: + request = _validate_request(ReadUserGroupPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserGroupPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserGroupPolicyResponse, response) + + async def read_user_groups( + self, + request: ReadUserGroupsRequest | None = None, + ) -> ReadUserGroupsResponse: + request = _validate_request(ReadUserGroupsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserGroups", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserGroupsResponse, response) + + async def read_user_groups_per_user( + self, + request: ReadUserGroupsPerUserRequest | None = None, + ) -> ReadUserGroupsPerUserResponse: + request = _validate_request(ReadUserGroupsPerUserRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserGroupsPerUser", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserGroupsPerUserResponse, response) + + async def read_user_policies( + self, + request: ReadUserPoliciesRequest | None = None, + ) -> ReadUserPoliciesResponse: + request = _validate_request(ReadUserPoliciesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserPolicies", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserPoliciesResponse, response) + + async def read_user_policy( + self, + request: ReadUserPolicyRequest | None = None, + ) -> ReadUserPolicyResponse: + request = _validate_request(ReadUserPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUserPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUserPolicyResponse, response) + + async def read_users( + self, + request: ReadUsersRequest | None = None, + ) -> ReadUsersResponse: + request = _validate_request(ReadUsersRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadUsers", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadUsersResponse, response) + + async def read_virtual_gateways( + self, + request: ReadVirtualGatewaysRequest | None = None, + ) -> ReadVirtualGatewaysResponse: + request = _validate_request(ReadVirtualGatewaysRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVirtualGateways", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVirtualGatewaysResponse, response) + + async def read_vm_groups( + self, + request: ReadVmGroupsRequest | None = None, + ) -> ReadVmGroupsResponse: + request = _validate_request(ReadVmGroupsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVmGroups", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmGroupsResponse, response) + + async def read_vm_templates( + self, + request: ReadVmTemplatesRequest | None = None, + ) -> ReadVmTemplatesResponse: + request = _validate_request(ReadVmTemplatesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVmTemplates", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmTemplatesResponse, response) + + async def read_vm_types( + self, + request: ReadVmTypesRequest | None = None, + ) -> ReadVmTypesResponse: + request = _validate_request(ReadVmTypesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVmTypes", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmTypesResponse, response) + + async def read_vms( + self, + request: ReadVmsRequest | None = None, + ) -> ReadVmsResponse: + request = _validate_request(ReadVmsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVms", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmsResponse, response) + + async def read_vms_health( + self, + request: ReadVmsHealthRequest | None = None, + ) -> ReadVmsHealthResponse: + request = _validate_request(ReadVmsHealthRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVmsHealth", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmsHealthResponse, response) + + async def read_vms_state( + self, + request: ReadVmsStateRequest | None = None, + ) -> ReadVmsStateResponse: + request = _validate_request(ReadVmsStateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVmsState", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmsStateResponse, response) + + async def read_vms_stop_history( + self, + request: ReadVmsStopHistoryRequest | None = None, + ) -> ReadVmsStopHistoryResponse: + request = _validate_request(ReadVmsStopHistoryRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVmsStopHistory", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVmsStopHistoryResponse, response) + + async def read_volume_update_tasks( + self, + request: ReadVolumeUpdateTasksRequest | None = None, + ) -> ReadVolumeUpdateTasksResponse: + request = _validate_request(ReadVolumeUpdateTasksRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVolumeUpdateTasks", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVolumeUpdateTasksResponse, response) + + async def read_volumes( + self, + request: ReadVolumesRequest | None = None, + ) -> ReadVolumesResponse: + request = _validate_request(ReadVolumesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVolumes", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVolumesResponse, response) + + async def read_vpn_connections( + self, + request: ReadVpnConnectionsRequest | None = None, + ) -> ReadVpnConnectionsResponse: + request = _validate_request(ReadVpnConnectionsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ReadVpnConnections", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ReadVpnConnectionsResponse, response) + + async def reboot_vms( + self, + request: RebootVmsRequest | None = None, + ) -> RebootVmsResponse: + request = _validate_request(RebootVmsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/RebootVms", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(RebootVmsResponse, response) + + async def register_vms_in_load_balancer( + self, + request: RegisterVmsInLoadBalancerRequest | None = None, + ) -> RegisterVmsInLoadBalancerResponse: + request = _validate_request(RegisterVmsInLoadBalancerRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/RegisterVmsInLoadBalancer", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(RegisterVmsInLoadBalancerResponse, response) + + async def reject_net_peering( + self, + request: RejectNetPeeringRequest | None = None, + ) -> RejectNetPeeringResponse: + request = _validate_request(RejectNetPeeringRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/RejectNetPeering", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(RejectNetPeeringResponse, response) + + async def remove_user_from_user_group( + self, + request: RemoveUserFromUserGroupRequest | None = None, + ) -> RemoveUserFromUserGroupResponse: + request = _validate_request(RemoveUserFromUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/RemoveUserFromUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(RemoveUserFromUserGroupResponse, response) + + async def scale_down_vm_group( + self, + request: ScaleDownVmGroupRequest | None = None, + ) -> ScaleDownVmGroupResponse: + request = _validate_request(ScaleDownVmGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ScaleDownVmGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ScaleDownVmGroupResponse, response) + + async def scale_up_vm_group( + self, + request: ScaleUpVmGroupRequest | None = None, + ) -> ScaleUpVmGroupResponse: + request = _validate_request(ScaleUpVmGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/ScaleUpVmGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(ScaleUpVmGroupResponse, response) + + async def set_default_policy_version( + self, + request: SetDefaultPolicyVersionRequest | None = None, + ) -> SetDefaultPolicyVersionResponse: + request = _validate_request(SetDefaultPolicyVersionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/SetDefaultPolicyVersion", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(SetDefaultPolicyVersionResponse, response) + + async def start_vms( + self, + request: StartVmsRequest | None = None, + ) -> StartVmsResponse: + request = _validate_request(StartVmsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/StartVms", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(StartVmsResponse, response) + + async def stop_vms( + self, + request: StopVmsRequest | None = None, + ) -> StopVmsResponse: + request = _validate_request(StopVmsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/StopVms", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(StopVmsResponse, response) + + async def unlink_flexible_gpu( + self, + request: UnlinkFlexibleGpuRequest | None = None, + ) -> UnlinkFlexibleGpuResponse: + request = _validate_request(UnlinkFlexibleGpuRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkFlexibleGpu", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkFlexibleGpuResponse, response) + + async def unlink_internet_service( + self, + request: UnlinkInternetServiceRequest | None = None, + ) -> UnlinkInternetServiceResponse: + request = _validate_request(UnlinkInternetServiceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkInternetService", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkInternetServiceResponse, response) + + async def unlink_load_balancer_backend_machines( + self, + request: UnlinkLoadBalancerBackendMachinesRequest | None = None, + ) -> UnlinkLoadBalancerBackendMachinesResponse: + request = _validate_request(UnlinkLoadBalancerBackendMachinesRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkLoadBalancerBackendMachines", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkLoadBalancerBackendMachinesResponse, response) + + async def unlink_managed_policy_from_user_group( + self, + request: UnlinkManagedPolicyFromUserGroupRequest | None = None, + ) -> UnlinkManagedPolicyFromUserGroupResponse: + request = _validate_request(UnlinkManagedPolicyFromUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkManagedPolicyFromUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkManagedPolicyFromUserGroupResponse, response) + + async def unlink_nic( + self, + request: UnlinkNicRequest | None = None, + ) -> UnlinkNicResponse: + request = _validate_request(UnlinkNicRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkNic", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkNicResponse, response) + + async def unlink_policy( + self, + request: UnlinkPolicyRequest | None = None, + ) -> UnlinkPolicyResponse: + request = _validate_request(UnlinkPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkPolicyResponse, response) + + async def unlink_private_ips( + self, + request: UnlinkPrivateIpsRequest | None = None, + ) -> UnlinkPrivateIpsResponse: + request = _validate_request(UnlinkPrivateIpsRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkPrivateIps", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkPrivateIpsResponse, response) + + async def unlink_public_ip( + self, + request: UnlinkPublicIpRequest | None = None, + ) -> UnlinkPublicIpResponse: + request = _validate_request(UnlinkPublicIpRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkPublicIp", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkPublicIpResponse, response) + + async def unlink_route_table( + self, + request: UnlinkRouteTableRequest | None = None, + ) -> UnlinkRouteTableResponse: + request = _validate_request(UnlinkRouteTableRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkRouteTable", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkRouteTableResponse, response) + + async def unlink_virtual_gateway( + self, + request: UnlinkVirtualGatewayRequest | None = None, + ) -> UnlinkVirtualGatewayResponse: + request = _validate_request(UnlinkVirtualGatewayRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkVirtualGateway", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkVirtualGatewayResponse, response) + + async def unlink_volume( + self, + request: UnlinkVolumeRequest | None = None, + ) -> UnlinkVolumeResponse: + request = _validate_request(UnlinkVolumeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UnlinkVolume", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UnlinkVolumeResponse, response) + + async def update_access_key( + self, + request: UpdateAccessKeyRequest | None = None, + ) -> UpdateAccessKeyResponse: + request = _validate_request(UpdateAccessKeyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateAccessKey", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateAccessKeyResponse, response) + + async def update_account( + self, + request: UpdateAccountRequest | None = None, + ) -> UpdateAccountResponse: + request = _validate_request(UpdateAccountRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateAccount", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateAccountResponse, response) + + async def update_api_access_policy( + self, + request: UpdateApiAccessPolicyRequest | None = None, + ) -> UpdateApiAccessPolicyResponse: + request = _validate_request(UpdateApiAccessPolicyRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateApiAccessPolicy", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateApiAccessPolicyResponse, response) + + async def update_api_access_rule( + self, + request: UpdateApiAccessRuleRequest | None = None, + ) -> UpdateApiAccessRuleResponse: + request = _validate_request(UpdateApiAccessRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateApiAccessRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateApiAccessRuleResponse, response) + + async def update_ca( + self, + request: UpdateCaRequest | None = None, + ) -> UpdateCaResponse: + request = _validate_request(UpdateCaRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateCa", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateCaResponse, response) + + async def update_dedicated_group( + self, + request: UpdateDedicatedGroupRequest | None = None, + ) -> UpdateDedicatedGroupResponse: + request = _validate_request(UpdateDedicatedGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateDedicatedGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateDedicatedGroupResponse, response) + + async def update_direct_link_interface( + self, + request: UpdateDirectLinkInterfaceRequest | None = None, + ) -> UpdateDirectLinkInterfaceResponse: + request = _validate_request(UpdateDirectLinkInterfaceRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateDirectLinkInterface", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateDirectLinkInterfaceResponse, response) + + async def update_flexible_gpu( + self, + request: UpdateFlexibleGpuRequest | None = None, + ) -> UpdateFlexibleGpuResponse: + request = _validate_request(UpdateFlexibleGpuRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateFlexibleGpu", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateFlexibleGpuResponse, response) + + async def update_image( + self, + request: UpdateImageRequest | None = None, + ) -> UpdateImageResponse: + request = _validate_request(UpdateImageRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateImage", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateImageResponse, response) + + async def update_listener_rule( + self, + request: UpdateListenerRuleRequest | None = None, + ) -> UpdateListenerRuleResponse: + request = _validate_request(UpdateListenerRuleRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateListenerRule", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateListenerRuleResponse, response) + + async def update_load_balancer( + self, + request: UpdateLoadBalancerRequest | None = None, + ) -> UpdateLoadBalancerResponse: + request = _validate_request(UpdateLoadBalancerRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateLoadBalancer", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateLoadBalancerResponse, response) + + async def update_net( + self, + request: UpdateNetRequest | None = None, + ) -> UpdateNetResponse: + request = _validate_request(UpdateNetRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateNet", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateNetResponse, response) + + async def update_net_access_point( + self, + request: UpdateNetAccessPointRequest | None = None, + ) -> UpdateNetAccessPointResponse: + request = _validate_request(UpdateNetAccessPointRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateNetAccessPoint", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateNetAccessPointResponse, response) + + async def update_nic( + self, + request: UpdateNicRequest | None = None, + ) -> UpdateNicResponse: + request = _validate_request(UpdateNicRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateNic", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateNicResponse, response) + + async def update_route( + self, + request: UpdateRouteRequest | None = None, + ) -> UpdateRouteResponse: + request = _validate_request(UpdateRouteRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateRoute", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateRouteResponse, response) + + async def update_route_propagation( + self, + request: UpdateRoutePropagationRequest | None = None, + ) -> UpdateRoutePropagationResponse: + request = _validate_request(UpdateRoutePropagationRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateRoutePropagation", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateRoutePropagationResponse, response) + + async def update_route_table_link( + self, + request: UpdateRouteTableLinkRequest | None = None, + ) -> UpdateRouteTableLinkResponse: + request = _validate_request(UpdateRouteTableLinkRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateRouteTableLink", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateRouteTableLinkResponse, response) + + async def update_server_certificate( + self, + request: UpdateServerCertificateRequest | None = None, + ) -> UpdateServerCertificateResponse: + request = _validate_request(UpdateServerCertificateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateServerCertificate", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateServerCertificateResponse, response) + + async def update_snapshot( + self, + request: UpdateSnapshotRequest | None = None, + ) -> UpdateSnapshotResponse: + request = _validate_request(UpdateSnapshotRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateSnapshot", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateSnapshotResponse, response) + + async def update_subnet( + self, + request: UpdateSubnetRequest | None = None, + ) -> UpdateSubnetResponse: + request = _validate_request(UpdateSubnetRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateSubnet", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateSubnetResponse, response) + + async def update_user( + self, + request: UpdateUserRequest | None = None, + ) -> UpdateUserResponse: + request = _validate_request(UpdateUserRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateUser", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateUserResponse, response) + + async def update_user_group( + self, + request: UpdateUserGroupRequest | None = None, + ) -> UpdateUserGroupResponse: + request = _validate_request(UpdateUserGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateUserGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateUserGroupResponse, response) + + async def update_vm( + self, + request: UpdateVmRequest | None = None, + ) -> UpdateVmResponse: + request = _validate_request(UpdateVmRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateVm", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateVmResponse, response) + + async def update_vm_group( + self, + request: UpdateVmGroupRequest | None = None, + ) -> UpdateVmGroupResponse: + request = _validate_request(UpdateVmGroupRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateVmGroup", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateVmGroupResponse, response) + + async def update_vm_template( + self, + request: UpdateVmTemplateRequest | None = None, + ) -> UpdateVmTemplateResponse: + request = _validate_request(UpdateVmTemplateRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateVmTemplate", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateVmTemplateResponse, response) + + async def update_volume( + self, + request: UpdateVolumeRequest | None = None, + ) -> UpdateVolumeResponse: + request = _validate_request(UpdateVolumeRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateVolume", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateVolumeResponse, response) + + async def update_vpn_connection( + self, + request: UpdateVpnConnectionRequest | None = None, + ) -> UpdateVpnConnectionResponse: + request = _validate_request(UpdateVpnConnectionRequest, request) + + path_params = { + } + query_params = { + } + response = await self.call.request( + RequestSpec( + service="api", + method="POST", + path="/UpdateVpnConnection", + json_body=_dump_json_body(request), + query_params={ + key: value + for key, value in query_params.items() + if value is not None + }, + ), + path_params=path_params, + ) + return _validate_response(UpdateVpnConnectionResponse, response) diff --git a/osc_sdk_python/generated/osc/models.py b/osc_sdk_python/generated/osc/models.py new file mode 100644 index 0000000..86c2272 --- /dev/null +++ b/osc_sdk_python/generated/osc/models.py @@ -0,0 +1,3839 @@ +"""Generated typed OSC client slice. + +Typed request and response models are async-first. Generated typed methods are +exposed on AsyncClient; synchronous clients use dynamic action methods. + +Do not edit by hand. Regenerate with: + python -m osc_sdk_python.codegen.generator + + python -m osc_sdk_python.codegen.generator oks osc +""" +from __future__ import annotations + +import datetime + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + + +class GeneratedModel(BaseModel): + model_config = ConfigDict(populate_by_name=True, extra="allow") + + +class AcceptNetPeeringRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_peering_id: str = Field(alias='NetPeeringId') + +class AcceptNetPeeringResponse(GeneratedModel): + net_peering: NetPeering | None = Field(default=None, alias='NetPeering') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class AccepterNet(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + ip_range: str | None = Field(default=None, alias='IpRange') + net_id: str | None = Field(default=None, alias='NetId') + +class AccessKey(GeneratedModel): + access_key_id: str | None = Field(default=None, alias='AccessKeyId') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + expiration_date: datetime.datetime | None = Field(default=None, alias='ExpirationDate') + last_modification_date: datetime.datetime | None = Field(default=None, alias='LastModificationDate') + state: str | None = Field(default=None, alias='State') + tag: str | None = Field(default=None, alias='Tag') + +class AccessKeySecretKey(GeneratedModel): + access_key_id: str | None = Field(default=None, alias='AccessKeyId') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + expiration_date: datetime.datetime | None = Field(default=None, alias='ExpirationDate') + last_modification_date: datetime.datetime | None = Field(default=None, alias='LastModificationDate') + secret_key: str | None = Field(default=None, alias='SecretKey') + state: str | None = Field(default=None, alias='State') + tag: str | None = Field(default=None, alias='Tag') + +class AccessLog(GeneratedModel): + is_enabled: bool | None = Field(default=None, alias='IsEnabled') + osu_bucket_name: str | None = Field(default=None, alias='OsuBucketName') + osu_bucket_prefix: str | None = Field(default=None, alias='OsuBucketPrefix') + publication_interval: int | None = Field(default=None, alias='PublicationInterval') + +class Account(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + additional_emails: list[str] | None = Field(default=None, alias='AdditionalEmails') + city: str | None = Field(default=None, alias='City') + company_name: str | None = Field(default=None, alias='CompanyName') + country: str | None = Field(default=None, alias='Country') + customer_id: str | None = Field(default=None, alias='CustomerId') + email: str | None = Field(default=None, alias='Email') + first_name: str | None = Field(default=None, alias='FirstName') + job_title: str | None = Field(default=None, alias='JobTitle') + last_name: str | None = Field(default=None, alias='LastName') + mobile_number: str | None = Field(default=None, alias='MobileNumber') + outscale_login_allowed: bool | None = Field(default=None, alias='OutscaleLoginAllowed') + phone_number: str | None = Field(default=None, alias='PhoneNumber') + state_province: str | None = Field(default=None, alias='StateProvince') + vat_number: str | None = Field(default=None, alias='VatNumber') + zip_code: str | None = Field(default=None, alias='ZipCode') + +class ActionsOnNextBoot(GeneratedModel): + secure_boot: SecureBootAction | None = Field(default=None, alias='SecureBoot') + +class AddUserToUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_group_name: str = Field(alias='UserGroupName') + user_group_path: str | None = Field(default=None, alias='UserGroupPath') + user_name: str = Field(alias='UserName') + user_path: str | None = Field(default=None, alias='UserPath') + +class AddUserToUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ApiAccessPolicy(GeneratedModel): + max_access_key_expiration_seconds: int | None = Field(default=None, alias='MaxAccessKeyExpirationSeconds') + require_trusted_env: bool | None = Field(default=None, alias='RequireTrustedEnv') + +class ApiAccessRule(GeneratedModel): + api_access_rule_id: str | None = Field(default=None, alias='ApiAccessRuleId') + ca_ids: list[str] | None = Field(default=None, alias='CaIds') + cns: list[str] | None = Field(default=None, alias='Cns') + description: str | None = Field(default=None, alias='Description') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + +class ApplicationStickyCookiePolicy(GeneratedModel): + cookie_name: str | None = Field(default=None, alias='CookieName') + policy_name: str | None = Field(default=None, alias='PolicyName') + +class BackendVmHealth(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + state: str | None = Field(default=None, alias='State') + state_reason: str | None = Field(default=None, alias='StateReason') + vm_id: str | None = Field(default=None, alias='VmId') + +class BlockDeviceMappingCreated(GeneratedModel): + bsu: BsuCreated | None = Field(default=None, alias='Bsu') + device_name: str | None = Field(default=None, alias='DeviceName') + +class BlockDeviceMappingImage(GeneratedModel): + bsu: BsuToCreate | None = Field(default=None, alias='Bsu') + device_name: str | None = Field(default=None, alias='DeviceName') + virtual_device_name: str | None = Field(default=None, alias='VirtualDeviceName') + +class BlockDeviceMappingVmCreation(GeneratedModel): + bsu: BsuToCreate | None = Field(default=None, alias='Bsu') + device_name: str | None = Field(default=None, alias='DeviceName') + no_device: str | None = Field(default=None, alias='NoDevice') + virtual_device_name: str | None = Field(default=None, alias='VirtualDeviceName') + +class BlockDeviceMappingVmUpdate(GeneratedModel): + bsu: BsuToUpdateVm | None = Field(default=None, alias='Bsu') + device_name: str | None = Field(default=None, alias='DeviceName') + no_device: str | None = Field(default=None, alias='NoDevice') + virtual_device_name: str | None = Field(default=None, alias='VirtualDeviceName') + +BootMode = Literal['uefi', 'legacy'] + +class BsuCreated(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + link_date: datetime.datetime | None = Field(default=None, alias='LinkDate') + state: str | None = Field(default=None, alias='State') + volume_id: str | None = Field(default=None, alias='VolumeId') + +class BsuToCreate(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + iops: int | None = Field(default=None, alias='Iops') + snapshot_id: str | None = Field(default=None, alias='SnapshotId') + volume_size: int | None = Field(default=None, alias='VolumeSize') + volume_type: str | None = Field(default=None, alias='VolumeType') + +class BsuToUpdateVm(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + volume_id: str | None = Field(default=None, alias='VolumeId') + +class CO2CategoryDistribution(GeneratedModel): + category: str | None = Field(default=None, alias='Category') + value: float | None = Field(default=None, alias='Value') + +class CO2EmissionEntry(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + category_distribution: list[CO2CategoryDistribution] | None = Field(default=None, alias='CategoryDistribution') + factor_distribution: list[CO2FactorDistribution] | None = Field(default=None, alias='FactorDistribution') + month: str | None = Field(default=None, alias='Month') + paying_account_id: str | None = Field(default=None, alias='PayingAccountId') + value: float | None = Field(default=None, alias='Value') + +class CO2FactorDistribution(GeneratedModel): + factor: str | None = Field(default=None, alias='Factor') + value: float | None = Field(default=None, alias='Value') + +class Ca(GeneratedModel): + ca_fingerprint: str | None = Field(default=None, alias='CaFingerprint') + ca_id: str | None = Field(default=None, alias='CaId') + description: str | None = Field(default=None, alias='Description') + +class Catalog(GeneratedModel): + entries: list[CatalogEntry] | None = Field(default=None, alias='Entries') + +class CatalogEntry(GeneratedModel): + category: str | None = Field(default=None, alias='Category') + flags: str | None = Field(default=None, alias='Flags') + operation: str | None = Field(default=None, alias='Operation') + service: str | None = Field(default=None, alias='Service') + subregion_name: str | None = Field(default=None, alias='SubregionName') + title: str | None = Field(default=None, alias='Title') + type: str | None = Field(default=None, alias='Type') + unit_price: float | None = Field(default=None, alias='UnitPrice') + +class Catalogs(GeneratedModel): + entries: list[CatalogEntry] | None = Field(default=None, alias='Entries') + from_date: datetime.datetime | None = Field(default=None, alias='FromDate') + state: Literal['CURRENT', 'OBSOLETE'] | None = Field(default=None, alias='State') + to_date: datetime.datetime | None = Field(default=None, alias='ToDate') + +class CheckAuthenticationRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + login: str = Field(alias='Login') + password: str = Field(alias='Password') + +class CheckAuthenticationResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ClientGateway(GeneratedModel): + bgp_asn: int | None = Field(default=None, alias='BgpAsn') + client_gateway_id: str | None = Field(default=None, alias='ClientGatewayId') + connection_type: str | None = Field(default=None, alias='ConnectionType') + public_ip: str | None = Field(default=None, alias='PublicIp') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class ConsumptionEntry(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + category: str | None = Field(default=None, alias='Category') + from_date: datetime.datetime | None = Field(default=None, alias='FromDate') + operation: str | None = Field(default=None, alias='Operation') + paying_account_id: str | None = Field(default=None, alias='PayingAccountId') + price: float | None = Field(default=None, alias='Price') + resource_id: str | None = Field(default=None, alias='ResourceId') + service: str | None = Field(default=None, alias='Service') + subregion_name: str | None = Field(default=None, alias='SubregionName') + title: str | None = Field(default=None, alias='Title') + to_date: datetime.datetime | None = Field(default=None, alias='ToDate') + type: str | None = Field(default=None, alias='Type') + unit_price: float | None = Field(default=None, alias='UnitPrice') + value: float | None = Field(default=None, alias='Value') + +class CreateAccessKeyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + expiration_date: datetime.datetime | str | None = Field(default=None, alias='ExpirationDate') + tag: str | None = Field(default=None, alias='Tag') + user_name: str | None = Field(default=None, alias='UserName') + +class CreateAccessKeyResponse(GeneratedModel): + access_key: AccessKeySecretKey | None = Field(default=None, alias='AccessKey') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateAccountRequest(GeneratedModel): + additional_emails: list[str] | None = Field(default=None, alias='AdditionalEmails') + city: str = Field(alias='City') + company_name: str = Field(alias='CompanyName') + country: str = Field(alias='Country') + customer_id: str = Field(alias='CustomerId') + dry_run: bool | None = Field(default=None, alias='DryRun') + email: str = Field(alias='Email') + first_name: str = Field(alias='FirstName') + job_title: str | None = Field(default=None, alias='JobTitle') + last_name: str = Field(alias='LastName') + mobile_number: str | None = Field(default=None, alias='MobileNumber') + phone_number: str | None = Field(default=None, alias='PhoneNumber') + state_province: str | None = Field(default=None, alias='StateProvince') + vat_number: str | None = Field(default=None, alias='VatNumber') + zip_code: str = Field(alias='ZipCode') + +class CreateAccountResponse(GeneratedModel): + account: Account | None = Field(default=None, alias='Account') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateApiAccessRuleRequest(GeneratedModel): + ca_ids: list[str] | None = Field(default=None, alias='CaIds') + cns: list[str] | None = Field(default=None, alias='Cns') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + +class CreateApiAccessRuleResponse(GeneratedModel): + api_access_rule: ApiAccessRule | None = Field(default=None, alias='ApiAccessRule') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateCaRequest(GeneratedModel): + ca_pem: str = Field(alias='CaPem') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class CreateCaResponse(GeneratedModel): + ca: Ca | None = Field(default=None, alias='Ca') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateClientGatewayRequest(GeneratedModel): + bgp_asn: int = Field(alias='BgpAsn') + connection_type: str = Field(alias='ConnectionType') + dry_run: bool | None = Field(default=None, alias='DryRun') + public_ip: str = Field(alias='PublicIp') + +class CreateClientGatewayResponse(GeneratedModel): + client_gateway: ClientGateway | None = Field(default=None, alias='ClientGateway') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateDedicatedGroupRequest(GeneratedModel): + cpu_generation: int = Field(alias='CpuGeneration') + dry_run: bool | None = Field(default=None, alias='DryRun') + name: str = Field(alias='Name') + subregion_name: str = Field(alias='SubregionName') + +class CreateDedicatedGroupResponse(GeneratedModel): + dedicated_group: DedicatedGroup | None = Field(default=None, alias='DedicatedGroup') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateDhcpOptionsRequest(GeneratedModel): + domain_name: str | None = Field(default=None, alias='DomainName') + domain_name_servers: list[str] | None = Field(default=None, alias='DomainNameServers') + dry_run: bool | None = Field(default=None, alias='DryRun') + log_servers: list[str] | None = Field(default=None, alias='LogServers') + ntp_servers: list[str] | None = Field(default=None, alias='NtpServers') + +class CreateDhcpOptionsResponse(GeneratedModel): + dhcp_options_set: DhcpOptionsSet | None = Field(default=None, alias='DhcpOptionsSet') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateDirectLinkInterfaceRequest(GeneratedModel): + direct_link_id: str = Field(alias='DirectLinkId') + direct_link_interface: DirectLinkInterface = Field(alias='DirectLinkInterface') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class CreateDirectLinkInterfaceResponse(GeneratedModel): + direct_link_interface: DirectLinkInterfaces | None = Field(default=None, alias='DirectLinkInterface') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateDirectLinkRequest(GeneratedModel): + bandwidth: str = Field(alias='Bandwidth') + direct_link_name: str = Field(alias='DirectLinkName') + dry_run: bool | None = Field(default=None, alias='DryRun') + location: str = Field(alias='Location') + +class CreateDirectLinkResponse(GeneratedModel): + direct_link: DirectLink | None = Field(default=None, alias='DirectLink') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateFlexibleGpuRequest(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + dry_run: bool | None = Field(default=None, alias='DryRun') + generation: str | None = Field(default=None, alias='Generation') + model_name: str = Field(alias='ModelName') + subregion_name: str = Field(alias='SubregionName') + +class CreateFlexibleGpuResponse(GeneratedModel): + flexible_gpu: FlexibleGpu | None = Field(default=None, alias='FlexibleGpu') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateImageExportTaskRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + image_id: str = Field(alias='ImageId') + osu_export: OsuExportToCreate = Field(alias='OsuExport') + +class CreateImageExportTaskResponse(GeneratedModel): + image_export_task: ImageExportTask | None = Field(default=None, alias='ImageExportTask') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateImageRequest(GeneratedModel): + architecture: str | None = Field(default=None, alias='Architecture') + block_device_mappings: list[BlockDeviceMappingImage] | None = Field(default=None, alias='BlockDeviceMappings') + boot_modes: list[BootMode] | None = Field(default=None, alias='BootModes') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + file_location: str | None = Field(default=None, alias='FileLocation') + image_name: str | None = Field(default=None, alias='ImageName') + no_reboot: bool | None = Field(default=None, alias='NoReboot') + product_codes: list[str] | None = Field(default=None, alias='ProductCodes') + root_device_name: str | None = Field(default=None, alias='RootDeviceName') + source_image_id: str | None = Field(default=None, alias='SourceImageId') + source_region_name: str | None = Field(default=None, alias='SourceRegionName') + tpm_mandatory: bool | None = Field(default=None, alias='TpmMandatory') + vm_id: str | None = Field(default=None, alias='VmId') + +class CreateImageResponse(GeneratedModel): + image: Image | None = Field(default=None, alias='Image') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateInternetServiceRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class CreateInternetServiceResponse(GeneratedModel): + internet_service: InternetService | None = Field(default=None, alias='InternetService') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateKeypairRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + keypair_name: str = Field(alias='KeypairName') + public_key: str | None = Field(default=None, alias='PublicKey') + +class CreateKeypairResponse(GeneratedModel): + keypair: KeypairCreated | None = Field(default=None, alias='Keypair') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateListenerRuleRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + listener: LoadBalancerLight = Field(alias='Listener') + listener_rule: ListenerRuleForCreation = Field(alias='ListenerRule') + vm_ids: list[str] = Field(alias='VmIds') + +class CreateListenerRuleResponse(GeneratedModel): + listener_rule: ListenerRule | None = Field(default=None, alias='ListenerRule') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateLoadBalancerListenersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + listeners: list[ListenerForCreation] = Field(alias='Listeners') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class CreateLoadBalancerListenersResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateLoadBalancerPolicyRequest(GeneratedModel): + cookie_expiration_period: int | None = Field(default=None, alias='CookieExpirationPeriod') + cookie_name: str | None = Field(default=None, alias='CookieName') + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + policy_name: str = Field(alias='PolicyName') + policy_type: str = Field(alias='PolicyType') + +class CreateLoadBalancerPolicyResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateLoadBalancerRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + listeners: list[ListenerForCreation] = Field(alias='Listeners') + load_balancer_name: str = Field(alias='LoadBalancerName') + load_balancer_type: str | None = Field(default=None, alias='LoadBalancerType') + public_ip: str | None = Field(default=None, alias='PublicIp') + security_groups: list[str] | None = Field(default=None, alias='SecurityGroups') + subnets: list[str] | None = Field(default=None, alias='Subnets') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class CreateLoadBalancerResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateLoadBalancerTagsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_names: list[str] = Field(alias='LoadBalancerNames') + tags: list[ResourceTag] = Field(alias='Tags') + +class CreateLoadBalancerTagsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateNatServiceRequest(GeneratedModel): + client_token: str | None = Field(default=None, alias='ClientToken') + dry_run: bool | None = Field(default=None, alias='DryRun') + public_ip_id: str = Field(alias='PublicIpId') + subnet_id: str = Field(alias='SubnetId') + +class CreateNatServiceResponse(GeneratedModel): + nat_service: NatService | None = Field(default=None, alias='NatService') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateNetAccessPointRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str = Field(alias='NetId') + route_table_ids: list[str] | None = Field(default=None, alias='RouteTableIds') + service_name: str = Field(alias='ServiceName') + +class CreateNetAccessPointResponse(GeneratedModel): + net_access_point: NetAccessPoint | None = Field(default=None, alias='NetAccessPoint') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateNetPeeringRequest(GeneratedModel): + accepter_net_id: str = Field(alias='AccepterNetId') + accepter_owner_id: str | None = Field(default=None, alias='AccepterOwnerId') + dry_run: bool | None = Field(default=None, alias='DryRun') + source_net_id: str = Field(alias='SourceNetId') + +class CreateNetPeeringResponse(GeneratedModel): + net_peering: NetPeering | None = Field(default=None, alias='NetPeering') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateNetRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + ip_range: str = Field(alias='IpRange') + tenancy: str | None = Field(default=None, alias='Tenancy') + +class CreateNetResponse(GeneratedModel): + net: Net | None = Field(default=None, alias='Net') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateNicRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + private_ips: list[PrivateIpLight] | None = Field(default=None, alias='PrivateIps') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + subnet_id: str = Field(alias='SubnetId') + +class CreateNicResponse(GeneratedModel): + nic: Nic | None = Field(default=None, alias='Nic') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreatePolicyRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + document: str = Field(alias='Document') + dry_run: bool | None = Field(default=None, alias='DryRun') + path: str | None = Field(default=None, alias='Path') + policy_name: str = Field(alias='PolicyName') + +class CreatePolicyResponse(GeneratedModel): + policy: Policy | None = Field(default=None, alias='Policy') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreatePolicyVersionRequest(GeneratedModel): + document: str = Field(alias='Document') + policy_orn: str = Field(alias='PolicyOrn') + set_as_default: bool | None = Field(default=None, alias='SetAsDefault') + +class CreatePolicyVersionResponse(GeneratedModel): + policy_version: PolicyVersion | None = Field(default=None, alias='PolicyVersion') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateProductTypeRequest(GeneratedModel): + description: str = Field(alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + vendor: str | None = Field(default=None, alias='Vendor') + +class CreateProductTypeResponse(GeneratedModel): + product_type: ProductType | None = Field(default=None, alias='ProductType') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreatePublicIpRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class CreatePublicIpResponse(GeneratedModel): + public_ip: PublicIp | None = Field(default=None, alias='PublicIp') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateRouteRequest(GeneratedModel): + destination_ip_range: str = Field(alias='DestinationIpRange') + dry_run: bool | None = Field(default=None, alias='DryRun') + gateway_id: str | None = Field(default=None, alias='GatewayId') + nat_service_id: str | None = Field(default=None, alias='NatServiceId') + net_peering_id: str | None = Field(default=None, alias='NetPeeringId') + nic_id: str | None = Field(default=None, alias='NicId') + route_table_id: str = Field(alias='RouteTableId') + vm_id: str | None = Field(default=None, alias='VmId') + +class CreateRouteResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + route_table: RouteTable | None = Field(default=None, alias='RouteTable') + +class CreateRouteTableRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str = Field(alias='NetId') + +class CreateRouteTableResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + route_table: RouteTable | None = Field(default=None, alias='RouteTable') + +class CreateSecurityGroupRequest(GeneratedModel): + description: str = Field(alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str | None = Field(default=None, alias='NetId') + security_group_name: str = Field(alias='SecurityGroupName') + +class CreateSecurityGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + security_group: SecurityGroup | None = Field(default=None, alias='SecurityGroup') + +class CreateSecurityGroupRuleRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + flow: str = Field(alias='Flow') + from_port_range: int | None = Field(default=None, alias='FromPortRange') + ip_protocol: str | None = Field(default=None, alias='IpProtocol') + ip_range: str | None = Field(default=None, alias='IpRange') + rules: list[SecurityGroupRule] | None = Field(default=None, alias='Rules') + security_group_account_id_to_link: str | None = Field(default=None, alias='SecurityGroupAccountIdToLink') + security_group_id: str = Field(alias='SecurityGroupId') + security_group_name_to_link: str | None = Field(default=None, alias='SecurityGroupNameToLink') + to_port_range: int | None = Field(default=None, alias='ToPortRange') + +class CreateSecurityGroupRuleResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + security_group: SecurityGroup | None = Field(default=None, alias='SecurityGroup') + +class CreateServerCertificateRequest(GeneratedModel): + body: str = Field(alias='Body') + chain: str | None = Field(default=None, alias='Chain') + dry_run: bool | None = Field(default=None, alias='DryRun') + name: str = Field(alias='Name') + path: str | None = Field(default=None, alias='Path') + private_key: str = Field(alias='PrivateKey') + +class CreateServerCertificateResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + server_certificate: ServerCertificate | None = Field(default=None, alias='ServerCertificate') + +class CreateSnapshotExportTaskRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + osu_export: OsuExportToCreate = Field(alias='OsuExport') + snapshot_id: str = Field(alias='SnapshotId') + +class CreateSnapshotExportTaskResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + snapshot_export_task: SnapshotExportTask | None = Field(default=None, alias='SnapshotExportTask') + +class CreateSnapshotRequest(GeneratedModel): + client_token: str | None = Field(default=None, alias='ClientToken') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + file_location: str | None = Field(default=None, alias='FileLocation') + snapshot_size: int | None = Field(default=None, alias='SnapshotSize') + source_region_name: str | None = Field(default=None, alias='SourceRegionName') + source_snapshot_id: str | None = Field(default=None, alias='SourceSnapshotId') + volume_id: str | None = Field(default=None, alias='VolumeId') + +class CreateSnapshotResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + snapshot: Snapshot | None = Field(default=None, alias='Snapshot') + +class CreateSubnetRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + ip_range: str = Field(alias='IpRange') + net_id: str = Field(alias='NetId') + subregion_name: str | None = Field(default=None, alias='SubregionName') + +class CreateSubnetResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + subnet: Subnet | None = Field(default=None, alias='Subnet') + +class CreateTagsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + resource_ids: list[str] = Field(alias='ResourceIds') + tags: list[ResourceTag] = Field(alias='Tags') + +class CreateTagsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class CreateUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + path: str | None = Field(default=None, alias='Path') + user_group_name: str = Field(alias='UserGroupName') + +class CreateUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user_group: UserGroup | None = Field(default=None, alias='UserGroup') + +class CreateUserRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + path: str | None = Field(default=None, alias='Path') + user_email: str | None = Field(default=None, alias='UserEmail') + user_name: str = Field(alias='UserName') + +class CreateUserResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user: User | None = Field(default=None, alias='User') + +class CreateVirtualGatewayRequest(GeneratedModel): + connection_type: str = Field(alias='ConnectionType') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class CreateVirtualGatewayResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + virtual_gateway: VirtualGateway | None = Field(default=None, alias='VirtualGateway') + +class CreateVmGroupRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + positioning_strategy: Literal['attract', 'no-strategy', 'repulse'] | None = Field(default=None, alias='PositioningStrategy') + security_group_ids: list[str] = Field(alias='SecurityGroupIds') + subnet_id: str = Field(alias='SubnetId') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_count: int = Field(alias='VmCount') + vm_group_name: str = Field(alias='VmGroupName') + vm_template_id: str = Field(alias='VmTemplateId') + +class CreateVmGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_group: VmGroup | None = Field(default=None, alias='VmGroup') + +class CreateVmTemplateRequest(GeneratedModel): + cpu_cores: int = Field(alias='CpuCores') + cpu_generation: str = Field(alias='CpuGeneration') + cpu_performance: Literal['medium', 'high', 'highest'] | None = Field(default=None, alias='CpuPerformance') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + image_id: str = Field(alias='ImageId') + keypair_name: str | None = Field(default=None, alias='KeypairName') + ram: int = Field(alias='Ram') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_template_name: str = Field(alias='VmTemplateName') + +class CreateVmTemplateResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_template: VmTemplate | None = Field(default=None, alias='VmTemplate') + +class CreateVmsRequest(GeneratedModel): + actions_on_next_boot: ActionsOnNextBoot | None = Field(default=None, alias='ActionsOnNextBoot') + block_device_mappings: list[BlockDeviceMappingVmCreation] | None = Field(default=None, alias='BlockDeviceMappings') + boot_mode: BootMode | None = Field(default=None, alias='BootMode') + boot_on_creation: bool | None = Field(default=None, alias='BootOnCreation') + bsu_optimized: bool | None = Field(default=None, alias='BsuOptimized') + client_token: str | None = Field(default=None, alias='ClientToken') + deletion_protection: bool | None = Field(default=None, alias='DeletionProtection') + dry_run: bool | None = Field(default=None, alias='DryRun') + image_id: str = Field(alias='ImageId') + keypair_name: str | None = Field(default=None, alias='KeypairName') + max_vms_count: int | None = Field(default=None, alias='MaxVmsCount') + min_vms_count: int | None = Field(default=None, alias='MinVmsCount') + nested_virtualization: bool | None = Field(default=None, alias='NestedVirtualization') + nics: list[NicForVmCreation] | None = Field(default=None, alias='Nics') + performance: Literal['medium', 'high', 'highest'] | None = Field(default=None, alias='Performance') + placement: Placement | None = Field(default=None, alias='Placement') + private_ips: list[str] | None = Field(default=None, alias='PrivateIps') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + security_groups: list[str] | None = Field(default=None, alias='SecurityGroups') + shutdown_behavior_configuration: ShutdownBehaviorConfiguration | None = Field(default=None, alias='ShutdownBehaviorConfiguration') + subnet_id: str | None = Field(default=None, alias='SubnetId') + tpm_enabled: bool | None = Field(default=None, alias='TpmEnabled') + user_data: str | None = Field(default=None, alias='UserData') + vm_initiated_shutdown_behavior: str | None = Field(default=None, alias='VmInitiatedShutdownBehavior') + vm_type: str | None = Field(default=None, alias='VmType') + +class CreateVmsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vms: list[Vm] | None = Field(default=None, alias='Vms') + +class CreateVolumeRequest(GeneratedModel): + client_token: str | None = Field(default=None, alias='ClientToken') + dry_run: bool | None = Field(default=None, alias='DryRun') + iops: int | None = Field(default=None, alias='Iops') + size: int | None = Field(default=None, alias='Size') + snapshot_id: str | None = Field(default=None, alias='SnapshotId') + subregion_name: str = Field(alias='SubregionName') + volume_type: str | None = Field(default=None, alias='VolumeType') + +class CreateVolumeResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + volume: Volume | None = Field(default=None, alias='Volume') + +class CreateVpnConnectionRequest(GeneratedModel): + client_gateway_id: str = Field(alias='ClientGatewayId') + connection_type: str = Field(alias='ConnectionType') + dry_run: bool | None = Field(default=None, alias='DryRun') + static_routes_only: bool | None = Field(default=None, alias='StaticRoutesOnly') + virtual_gateway_id: str = Field(alias='VirtualGatewayId') + +class CreateVpnConnectionResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vpn_connection: VpnConnection | None = Field(default=None, alias='VpnConnection') + +class CreateVpnConnectionRouteRequest(GeneratedModel): + destination_ip_range: str = Field(alias='DestinationIpRange') + dry_run: bool | None = Field(default=None, alias='DryRun') + vpn_connection_id: str = Field(alias='VpnConnectionId') + +class CreateVpnConnectionRouteResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DedicatedGroup(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + cpu_generation: int | None = Field(default=None, alias='CpuGeneration') + dedicated_group_id: str | None = Field(default=None, alias='DedicatedGroupId') + name: str | None = Field(default=None, alias='Name') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + subregion_name: str | None = Field(default=None, alias='SubregionName') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + +class DeleteAccessKeyRequest(GeneratedModel): + access_key_id: str = Field(alias='AccessKeyId') + dry_run: bool | None = Field(default=None, alias='DryRun') + user_name: str | None = Field(default=None, alias='UserName') + +class DeleteAccessKeyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteApiAccessRuleRequest(GeneratedModel): + api_access_rule_id: str = Field(alias='ApiAccessRuleId') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DeleteApiAccessRuleResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteCaRequest(GeneratedModel): + ca_id: str = Field(alias='CaId') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DeleteCaResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteClientGatewayRequest(GeneratedModel): + client_gateway_id: str = Field(alias='ClientGatewayId') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DeleteClientGatewayResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteDedicatedGroupRequest(GeneratedModel): + dedicated_group_id: str = Field(alias='DedicatedGroupId') + dry_run: bool | None = Field(default=None, alias='DryRun') + force: bool | None = Field(default=None, alias='Force') + +class DeleteDedicatedGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteDhcpOptionsRequest(GeneratedModel): + dhcp_options_set_id: str = Field(alias='DhcpOptionsSetId') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DeleteDhcpOptionsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteDirectLinkInterfaceRequest(GeneratedModel): + direct_link_interface_id: str = Field(alias='DirectLinkInterfaceId') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DeleteDirectLinkInterfaceResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteDirectLinkRequest(GeneratedModel): + direct_link_id: str = Field(alias='DirectLinkId') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DeleteDirectLinkResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteExportTaskRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + export_task_id: str = Field(alias='ExportTaskId') + +class DeleteExportTaskResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteFlexibleGpuRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + flexible_gpu_id: str = Field(alias='FlexibleGpuId') + +class DeleteFlexibleGpuResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteImageRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + image_id: str = Field(alias='ImageId') + +class DeleteImageResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteInternetServiceRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + internet_service_id: str = Field(alias='InternetServiceId') + +class DeleteInternetServiceResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteKeypairRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + keypair_id: str | None = Field(default=None, alias='KeypairId') + keypair_name: str | None = Field(default=None, alias='KeypairName') + +class DeleteKeypairResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteListenerRuleRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + listener_rule_name: str = Field(alias='ListenerRuleName') + +class DeleteListenerRuleResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteLoadBalancerListenersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + load_balancer_ports: list[int] = Field(alias='LoadBalancerPorts') + +class DeleteLoadBalancerListenersResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteLoadBalancerPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + policy_name: str = Field(alias='PolicyName') + +class DeleteLoadBalancerPolicyResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteLoadBalancerRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class DeleteLoadBalancerResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteLoadBalancerTagsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_names: list[str] = Field(alias='LoadBalancerNames') + tags: list[ResourceLoadBalancerTag] = Field(alias='Tags') + +class DeleteLoadBalancerTagsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteNatServiceRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + nat_service_id: str = Field(alias='NatServiceId') + +class DeleteNatServiceResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteNetAccessPointRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_access_point_id: str = Field(alias='NetAccessPointId') + +class DeleteNetAccessPointResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteNetPeeringRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_peering_id: str = Field(alias='NetPeeringId') + +class DeleteNetPeeringResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteNetRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str = Field(alias='NetId') + +class DeleteNetResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteNicRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + nic_id: str = Field(alias='NicId') + +class DeleteNicResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeletePolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_orn: str = Field(alias='PolicyOrn') + +class DeletePolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeletePolicyVersionRequest(GeneratedModel): + policy_orn: str = Field(alias='PolicyOrn') + version_id: str = Field(alias='VersionId') + +class DeletePolicyVersionResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteProductTypeRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + force: bool | None = Field(default=None, alias='Force') + product_type_id: str = Field(alias='ProductTypeId') + +class DeleteProductTypeResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeletePublicIpRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + public_ip: str | None = Field(default=None, alias='PublicIp') + public_ip_id: str | None = Field(default=None, alias='PublicIpId') + +class DeletePublicIpResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteRouteRequest(GeneratedModel): + destination_ip_range: str = Field(alias='DestinationIpRange') + dry_run: bool | None = Field(default=None, alias='DryRun') + route_table_id: str = Field(alias='RouteTableId') + +class DeleteRouteResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + route_table: RouteTable | None = Field(default=None, alias='RouteTable') + +class DeleteRouteTableRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + route_table_id: str = Field(alias='RouteTableId') + +class DeleteRouteTableResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteSecurityGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + security_group_id: str | None = Field(default=None, alias='SecurityGroupId') + security_group_name: str | None = Field(default=None, alias='SecurityGroupName') + +class DeleteSecurityGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteSecurityGroupRuleRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + flow: str = Field(alias='Flow') + from_port_range: int | None = Field(default=None, alias='FromPortRange') + ip_protocol: str | None = Field(default=None, alias='IpProtocol') + ip_range: str | None = Field(default=None, alias='IpRange') + rules: list[SecurityGroupRule] | None = Field(default=None, alias='Rules') + security_group_account_id_to_unlink: str | None = Field(default=None, alias='SecurityGroupAccountIdToUnlink') + security_group_id: str = Field(alias='SecurityGroupId') + security_group_name_to_unlink: str | None = Field(default=None, alias='SecurityGroupNameToUnlink') + to_port_range: int | None = Field(default=None, alias='ToPortRange') + +class DeleteSecurityGroupRuleResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + security_group: SecurityGroup | None = Field(default=None, alias='SecurityGroup') + +class DeleteServerCertificateRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + name: str = Field(alias='Name') + +class DeleteServerCertificateResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteSnapshotRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + snapshot_id: str = Field(alias='SnapshotId') + +class DeleteSnapshotResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteSubnetRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + subnet_id: str = Field(alias='SubnetId') + +class DeleteSubnetResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteTagsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + resource_ids: list[str] = Field(alias='ResourceIds') + tags: list[ResourceTag] = Field(alias='Tags') + +class DeleteTagsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteUserGroupPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_name: str = Field(alias='PolicyName') + user_group_name: str = Field(alias='UserGroupName') + user_group_path: str | None = Field(default=None, alias='UserGroupPath') + +class DeleteUserGroupPolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + force: bool | None = Field(default=None, alias='Force') + path: str | None = Field(default=None, alias='Path') + user_group_name: str = Field(alias='UserGroupName') + +class DeleteUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteUserPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_name: str = Field(alias='PolicyName') + user_name: str = Field(alias='UserName') + +class DeleteUserPolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteUserRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_name: str = Field(alias='UserName') + +class DeleteUserResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteVirtualGatewayRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + virtual_gateway_id: str = Field(alias='VirtualGatewayId') + +class DeleteVirtualGatewayResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteVmGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_group_id: str = Field(alias='VmGroupId') + +class DeleteVmGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteVmTemplateRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_template_id: str = Field(alias='VmTemplateId') + +class DeleteVmTemplateResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteVmsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_ids: list[str] = Field(alias='VmIds') + +class DeleteVmsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vms: list[VmState] | None = Field(default=None, alias='Vms') + +class DeleteVolumeRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + volume_id: str = Field(alias='VolumeId') + +class DeleteVolumeResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteVpnConnectionRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vpn_connection_id: str = Field(alias='VpnConnectionId') + +class DeleteVpnConnectionResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeleteVpnConnectionRouteRequest(GeneratedModel): + destination_ip_range: str = Field(alias='DestinationIpRange') + dry_run: bool | None = Field(default=None, alias='DryRun') + vpn_connection_id: str = Field(alias='VpnConnectionId') + +class DeleteVpnConnectionRouteResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DeregisterVmsInLoadBalancerRequest(GeneratedModel): + backend_vm_ids: list[str] = Field(alias='BackendVmIds') + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class DeregisterVmsInLoadBalancerResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DhcpOptionsSet(GeneratedModel): + default: bool | None = Field(default=None, alias='Default') + dhcp_options_set_id: str | None = Field(default=None, alias='DhcpOptionsSetId') + domain_name: str | None = Field(default=None, alias='DomainName') + domain_name_servers: list[str] | None = Field(default=None, alias='DomainNameServers') + log_servers: list[str] | None = Field(default=None, alias='LogServers') + ntp_servers: list[str] | None = Field(default=None, alias='NtpServers') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class DirectLink(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + bandwidth: str | None = Field(default=None, alias='Bandwidth') + direct_link_id: str | None = Field(default=None, alias='DirectLinkId') + direct_link_name: str | None = Field(default=None, alias='DirectLinkName') + location: str | None = Field(default=None, alias='Location') + region_name: str | None = Field(default=None, alias='RegionName') + state: str | None = Field(default=None, alias='State') + +class DirectLinkInterface(GeneratedModel): + bgp_asn: int = Field(alias='BgpAsn') + bgp_key: str | None = Field(default=None, alias='BgpKey') + client_private_ip: str | None = Field(default=None, alias='ClientPrivateIp') + direct_link_interface_name: str = Field(alias='DirectLinkInterfaceName') + outscale_private_ip: str | None = Field(default=None, alias='OutscalePrivateIp') + virtual_gateway_id: str = Field(alias='VirtualGatewayId') + vlan: int = Field(alias='Vlan') + +class DirectLinkInterfaces(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + bgp_asn: int | None = Field(default=None, alias='BgpAsn') + bgp_key: str | None = Field(default=None, alias='BgpKey') + client_private_ip: str | None = Field(default=None, alias='ClientPrivateIp') + direct_link_id: str | None = Field(default=None, alias='DirectLinkId') + direct_link_interface_id: str | None = Field(default=None, alias='DirectLinkInterfaceId') + direct_link_interface_name: str | None = Field(default=None, alias='DirectLinkInterfaceName') + interface_type: str | None = Field(default=None, alias='InterfaceType') + location: str | None = Field(default=None, alias='Location') + mtu: int | None = Field(default=None, alias='Mtu') + outscale_private_ip: str | None = Field(default=None, alias='OutscalePrivateIp') + state: str | None = Field(default=None, alias='State') + virtual_gateway_id: str | None = Field(default=None, alias='VirtualGatewayId') + vlan: int | None = Field(default=None, alias='Vlan') + +class DisableOutscaleLoginForUsersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DisableOutscaleLoginForUsersResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DisableOutscaleLoginPerUsersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_names: list[str] = Field(alias='UserNames') + +class DisableOutscaleLoginPerUsersResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class DisableOutscaleLoginRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class DisableOutscaleLoginResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class EnableOutscaleLoginForUsersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class EnableOutscaleLoginForUsersResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class EnableOutscaleLoginPerUsersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_names: list[str] = Field(alias='UserNames') + +class EnableOutscaleLoginPerUsersResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class EnableOutscaleLoginRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class EnableOutscaleLoginResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ErrorResponse(GeneratedModel): + errors: list[Errors] | None = Field(default=None, alias='Errors') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class Errors(GeneratedModel): + code: str | None = Field(default=None, alias='Code') + details: str | None = Field(default=None, alias='Details') + type: str | None = Field(default=None, alias='Type') + +class FiltersAccessKeys(GeneratedModel): + access_key_ids: list[str] | None = Field(default=None, alias='AccessKeyIds') + states: list[str] | None = Field(default=None, alias='States') + +class FiltersApiAccessRule(GeneratedModel): + api_access_rule_ids: list[str] | None = Field(default=None, alias='ApiAccessRuleIds') + ca_ids: list[str] | None = Field(default=None, alias='CaIds') + cns: list[str] | None = Field(default=None, alias='Cns') + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + +class FiltersApiLog(GeneratedModel): + query_access_keys: list[str] | None = Field(default=None, alias='QueryAccessKeys') + query_api_names: list[str] | None = Field(default=None, alias='QueryApiNames') + query_call_names: list[str] | None = Field(default=None, alias='QueryCallNames') + query_date_after: datetime.datetime | str | None = Field(default=None, alias='QueryDateAfter') + query_date_before: datetime.datetime | str | None = Field(default=None, alias='QueryDateBefore') + query_ip_addresses: list[str] | None = Field(default=None, alias='QueryIpAddresses') + query_user_agents: list[str] | None = Field(default=None, alias='QueryUserAgents') + request_ids: list[str] | None = Field(default=None, alias='RequestIds') + response_status_codes: list[int] | None = Field(default=None, alias='ResponseStatusCodes') + +class FiltersCa(GeneratedModel): + ca_fingerprints: list[str] | None = Field(default=None, alias='CaFingerprints') + ca_ids: list[str] | None = Field(default=None, alias='CaIds') + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + +class FiltersCatalogs(GeneratedModel): + current_catalog_only: bool | None = Field(default=None, alias='CurrentCatalogOnly') + from_date: str | None = Field(default=None, alias='FromDate') + to_date: str | None = Field(default=None, alias='ToDate') + +class FiltersClientGateway(GeneratedModel): + bgp_asns: list[int] | None = Field(default=None, alias='BgpAsns') + client_gateway_ids: list[str] | None = Field(default=None, alias='ClientGatewayIds') + connection_types: list[str] | None = Field(default=None, alias='ConnectionTypes') + public_ips: list[str] | None = Field(default=None, alias='PublicIps') + states: list[str] | None = Field(default=None, alias='States') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersDedicatedGroup(GeneratedModel): + cpu_generations: list[int] | None = Field(default=None, alias='CpuGenerations') + dedicated_group_ids: list[str] | None = Field(default=None, alias='DedicatedGroupIds') + names: list[str] | None = Field(default=None, alias='Names') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + +class FiltersDhcpOptions(GeneratedModel): + default: bool | None = Field(default=None, alias='Default') + dhcp_options_set_ids: list[str] | None = Field(default=None, alias='DhcpOptionsSetIds') + domain_name_servers: list[str] | None = Field(default=None, alias='DomainNameServers') + domain_names: list[str] | None = Field(default=None, alias='DomainNames') + log_servers: list[str] | None = Field(default=None, alias='LogServers') + ntp_servers: list[str] | None = Field(default=None, alias='NtpServers') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersDirectLink(GeneratedModel): + direct_link_ids: list[str] | None = Field(default=None, alias='DirectLinkIds') + +class FiltersDirectLinkInterface(GeneratedModel): + direct_link_ids: list[str] | None = Field(default=None, alias='DirectLinkIds') + direct_link_interface_ids: list[str] | None = Field(default=None, alias='DirectLinkInterfaceIds') + +class FiltersFlexibleGpu(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + flexible_gpu_ids: list[str] | None = Field(default=None, alias='FlexibleGpuIds') + generations: list[str] | None = Field(default=None, alias='Generations') + model_names: list[str] | None = Field(default=None, alias='ModelNames') + states: list[str] | None = Field(default=None, alias='States') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tags: list[Tag] | None = Field(default=None, alias='Tags') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + +class FiltersImage(GeneratedModel): + account_aliases: list[str] | None = Field(default=None, alias='AccountAliases') + account_ids: list[str] | None = Field(default=None, alias='AccountIds') + architectures: list[str] | None = Field(default=None, alias='Architectures') + block_device_mapping_delete_on_vm_deletion: bool | None = Field(default=None, alias='BlockDeviceMappingDeleteOnVmDeletion') + block_device_mapping_device_names: list[str] | None = Field(default=None, alias='BlockDeviceMappingDeviceNames') + block_device_mapping_snapshot_ids: list[str] | None = Field(default=None, alias='BlockDeviceMappingSnapshotIds') + block_device_mapping_volume_sizes: list[int] | None = Field(default=None, alias='BlockDeviceMappingVolumeSizes') + block_device_mapping_volume_types: list[str] | None = Field(default=None, alias='BlockDeviceMappingVolumeTypes') + boot_modes: list[BootMode] | None = Field(default=None, alias='BootModes') + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + file_locations: list[str] | None = Field(default=None, alias='FileLocations') + hypervisors: list[str] | None = Field(default=None, alias='Hypervisors') + image_ids: list[str] | None = Field(default=None, alias='ImageIds') + image_names: list[str] | None = Field(default=None, alias='ImageNames') + permissions_to_launch_account_ids: list[str] | None = Field(default=None, alias='PermissionsToLaunchAccountIds') + permissions_to_launch_global_permission: bool | None = Field(default=None, alias='PermissionsToLaunchGlobalPermission') + product_code_names: list[str] | None = Field(default=None, alias='ProductCodeNames') + product_codes: list[str] | None = Field(default=None, alias='ProductCodes') + root_device_names: list[str] | None = Field(default=None, alias='RootDeviceNames') + root_device_types: list[str] | None = Field(default=None, alias='RootDeviceTypes') + secure_boot: bool | None = Field(default=None, alias='SecureBoot') + states: list[str] | None = Field(default=None, alias='States') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + tpm_mandatory: bool | None = Field(default=None, alias='TpmMandatory') + virtualization_types: list[str] | None = Field(default=None, alias='VirtualizationTypes') + +class FiltersInternetService(GeneratedModel): + internet_service_ids: list[str] | None = Field(default=None, alias='InternetServiceIds') + link_net_ids: list[str] | None = Field(default=None, alias='LinkNetIds') + link_states: list[str] | None = Field(default=None, alias='LinkStates') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersKeypair(GeneratedModel): + keypair_fingerprints: list[str] | None = Field(default=None, alias='KeypairFingerprints') + keypair_ids: list[str] | None = Field(default=None, alias='KeypairIds') + keypair_names: list[str] | None = Field(default=None, alias='KeypairNames') + keypair_types: list[str] | None = Field(default=None, alias='KeypairTypes') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersListenerRule(GeneratedModel): + listener_rule_names: list[str] | None = Field(default=None, alias='ListenerRuleNames') + +class FiltersLoadBalancer(GeneratedModel): + load_balancer_names: list[str] | None = Field(default=None, alias='LoadBalancerNames') + states: list[str] | None = Field(default=None, alias='States') + +class FiltersNatService(GeneratedModel): + client_tokens: list[str] | None = Field(default=None, alias='ClientTokens') + nat_service_ids: list[str] | None = Field(default=None, alias='NatServiceIds') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + states: list[str] | None = Field(default=None, alias='States') + subnet_ids: list[str] | None = Field(default=None, alias='SubnetIds') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersNet(GeneratedModel): + dhcp_options_set_ids: list[str] | None = Field(default=None, alias='DhcpOptionsSetIds') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + is_default: bool | None = Field(default=None, alias='IsDefault') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + states: list[str] | None = Field(default=None, alias='States') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersNetAccessPoint(GeneratedModel): + net_access_point_ids: list[str] | None = Field(default=None, alias='NetAccessPointIds') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + service_names: list[str] | None = Field(default=None, alias='ServiceNames') + states: list[str] | None = Field(default=None, alias='States') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersNetPeering(GeneratedModel): + accepter_net_account_ids: list[str] | None = Field(default=None, alias='AccepterNetAccountIds') + accepter_net_ip_ranges: list[str] | None = Field(default=None, alias='AccepterNetIpRanges') + accepter_net_net_ids: list[str] | None = Field(default=None, alias='AccepterNetNetIds') + expiration_dates: list[datetime.datetime] | None = Field(default=None, alias='ExpirationDates') + net_peering_ids: list[str] | None = Field(default=None, alias='NetPeeringIds') + source_net_account_ids: list[str] | None = Field(default=None, alias='SourceNetAccountIds') + source_net_ip_ranges: list[str] | None = Field(default=None, alias='SourceNetIpRanges') + source_net_net_ids: list[str] | None = Field(default=None, alias='SourceNetNetIds') + state_messages: list[str] | None = Field(default=None, alias='StateMessages') + state_names: list[str] | None = Field(default=None, alias='StateNames') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersNic(GeneratedModel): + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + is_source_dest_check: bool | None = Field(default=None, alias='IsSourceDestCheck') + link_nic_delete_on_vm_deletion: bool | None = Field(default=None, alias='LinkNicDeleteOnVmDeletion') + link_nic_device_numbers: list[int] | None = Field(default=None, alias='LinkNicDeviceNumbers') + link_nic_link_nic_ids: list[str] | None = Field(default=None, alias='LinkNicLinkNicIds') + link_nic_states: list[str] | None = Field(default=None, alias='LinkNicStates') + link_nic_vm_account_ids: list[str] | None = Field(default=None, alias='LinkNicVmAccountIds') + link_nic_vm_ids: list[str] | None = Field(default=None, alias='LinkNicVmIds') + link_public_ip_account_ids: list[str] | None = Field(default=None, alias='LinkPublicIpAccountIds') + link_public_ip_link_public_ip_ids: list[str] | None = Field(default=None, alias='LinkPublicIpLinkPublicIpIds') + link_public_ip_public_dns_names: list[str] | None = Field(default=None, alias='LinkPublicIpPublicDnsNames') + link_public_ip_public_ip_ids: list[str] | None = Field(default=None, alias='LinkPublicIpPublicIpIds') + link_public_ip_public_ips: list[str] | None = Field(default=None, alias='LinkPublicIpPublicIps') + mac_addresses: list[str] | None = Field(default=None, alias='MacAddresses') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + nic_ids: list[str] | None = Field(default=None, alias='NicIds') + private_dns_names: list[str] | None = Field(default=None, alias='PrivateDnsNames') + private_ips_link_public_ip_account_ids: list[str] | None = Field(default=None, alias='PrivateIpsLinkPublicIpAccountIds') + private_ips_link_public_ip_public_ips: list[str] | None = Field(default=None, alias='PrivateIpsLinkPublicIpPublicIps') + private_ips_primary_ip: bool | None = Field(default=None, alias='PrivateIpsPrimaryIp') + private_ips_private_ips: list[str] | None = Field(default=None, alias='PrivateIpsPrivateIps') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + security_group_names: list[str] | None = Field(default=None, alias='SecurityGroupNames') + states: list[str] | None = Field(default=None, alias='States') + subnet_ids: list[str] | None = Field(default=None, alias='SubnetIds') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersProductType(GeneratedModel): + product_type_ids: list[str] | None = Field(default=None, alias='ProductTypeIds') + +class FiltersPublicIp(GeneratedModel): + link_public_ip_ids: list[str] | None = Field(default=None, alias='LinkPublicIpIds') + nic_account_ids: list[str] | None = Field(default=None, alias='NicAccountIds') + nic_ids: list[str] | None = Field(default=None, alias='NicIds') + placements: list[str] | None = Field(default=None, alias='Placements') + private_ips: list[str] | None = Field(default=None, alias='PrivateIps') + public_ip_ids: list[str] | None = Field(default=None, alias='PublicIpIds') + public_ips: list[str] | None = Field(default=None, alias='PublicIps') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + +class FiltersQuota(GeneratedModel): + collections: list[str] | None = Field(default=None, alias='Collections') + quota_names: list[str] | None = Field(default=None, alias='QuotaNames') + quota_types: list[str] | None = Field(default=None, alias='QuotaTypes') + short_descriptions: list[str] | None = Field(default=None, alias='ShortDescriptions') + +class FiltersReadImageExportTask(GeneratedModel): + image_ids: list[str] | None = Field(default=None, alias='ImageIds') + task_ids: list[str] | None = Field(default=None, alias='TaskIds') + +class FiltersReadVolumeUpdateTask(GeneratedModel): + task_ids: list[str] | None = Field(default=None, alias='TaskIds') + volume_ids: list[str] | None = Field(default=None, alias='VolumeIds') + +class FiltersRouteTable(GeneratedModel): + link_route_table_ids: list[str] | None = Field(default=None, alias='LinkRouteTableIds') + link_route_table_link_route_table_ids: list[str] | None = Field(default=None, alias='LinkRouteTableLinkRouteTableIds') + link_route_table_main: bool | None = Field(default=None, alias='LinkRouteTableMain') + link_subnet_ids: list[str] | None = Field(default=None, alias='LinkSubnetIds') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + route_creation_methods: list[str] | None = Field(default=None, alias='RouteCreationMethods') + route_destination_ip_ranges: list[str] | None = Field(default=None, alias='RouteDestinationIpRanges') + route_destination_service_ids: list[str] | None = Field(default=None, alias='RouteDestinationServiceIds') + route_gateway_ids: list[str] | None = Field(default=None, alias='RouteGatewayIds') + route_nat_service_ids: list[str] | None = Field(default=None, alias='RouteNatServiceIds') + route_net_peering_ids: list[str] | None = Field(default=None, alias='RouteNetPeeringIds') + route_states: list[str] | None = Field(default=None, alias='RouteStates') + route_table_ids: list[str] | None = Field(default=None, alias='RouteTableIds') + route_vm_ids: list[str] | None = Field(default=None, alias='RouteVmIds') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersSecurityGroup(GeneratedModel): + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + inbound_rule_account_ids: list[str] | None = Field(default=None, alias='InboundRuleAccountIds') + inbound_rule_from_port_ranges: list[int] | None = Field(default=None, alias='InboundRuleFromPortRanges') + inbound_rule_ip_ranges: list[str] | None = Field(default=None, alias='InboundRuleIpRanges') + inbound_rule_protocols: list[str] | None = Field(default=None, alias='InboundRuleProtocols') + inbound_rule_security_group_ids: list[str] | None = Field(default=None, alias='InboundRuleSecurityGroupIds') + inbound_rule_security_group_names: list[str] | None = Field(default=None, alias='InboundRuleSecurityGroupNames') + inbound_rule_to_port_ranges: list[int] | None = Field(default=None, alias='InboundRuleToPortRanges') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + outbound_rule_account_ids: list[str] | None = Field(default=None, alias='OutboundRuleAccountIds') + outbound_rule_from_port_ranges: list[int] | None = Field(default=None, alias='OutboundRuleFromPortRanges') + outbound_rule_ip_ranges: list[str] | None = Field(default=None, alias='OutboundRuleIpRanges') + outbound_rule_protocols: list[str] | None = Field(default=None, alias='OutboundRuleProtocols') + outbound_rule_security_group_ids: list[str] | None = Field(default=None, alias='OutboundRuleSecurityGroupIds') + outbound_rule_security_group_names: list[str] | None = Field(default=None, alias='OutboundRuleSecurityGroupNames') + outbound_rule_to_port_ranges: list[int] | None = Field(default=None, alias='OutboundRuleToPortRanges') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + security_group_names: list[str] | None = Field(default=None, alias='SecurityGroupNames') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersServerCertificate(GeneratedModel): + paths: list[str] | None = Field(default=None, alias='Paths') + +class FiltersService(GeneratedModel): + service_ids: list[str] | None = Field(default=None, alias='ServiceIds') + service_names: list[str] | None = Field(default=None, alias='ServiceNames') + +class FiltersSnapshot(GeneratedModel): + account_aliases: list[str] | None = Field(default=None, alias='AccountAliases') + account_ids: list[str] | None = Field(default=None, alias='AccountIds') + client_tokens: list[str] | None = Field(default=None, alias='ClientTokens') + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + from_creation_date: datetime.datetime | None = Field(default=None, alias='FromCreationDate') + permissions_to_create_volume_account_ids: list[str] | None = Field(default=None, alias='PermissionsToCreateVolumeAccountIds') + permissions_to_create_volume_global_permission: bool | None = Field(default=None, alias='PermissionsToCreateVolumeGlobalPermission') + progresses: list[int] | None = Field(default=None, alias='Progresses') + snapshot_ids: list[str] | None = Field(default=None, alias='SnapshotIds') + states: list[str] | None = Field(default=None, alias='States') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + to_creation_date: datetime.datetime | None = Field(default=None, alias='ToCreationDate') + volume_ids: list[str] | None = Field(default=None, alias='VolumeIds') + volume_sizes: list[int] | None = Field(default=None, alias='VolumeSizes') + +class FiltersSnapshotExportTask(GeneratedModel): + snapshot_ids: list[str] | None = Field(default=None, alias='SnapshotIds') + task_ids: list[str] | None = Field(default=None, alias='TaskIds') + +class FiltersSubnet(GeneratedModel): + available_ips_counts: list[int] | None = Field(default=None, alias='AvailableIpsCounts') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + states: list[str] | None = Field(default=None, alias='States') + subnet_ids: list[str] | None = Field(default=None, alias='SubnetIds') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + +class FiltersSubregion(GeneratedModel): + region_names: list[str] | None = Field(default=None, alias='RegionNames') + states: list[str] | None = Field(default=None, alias='States') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + +class FiltersTag(GeneratedModel): + keys: list[str] | None = Field(default=None, alias='Keys') + resource_ids: list[str] | None = Field(default=None, alias='ResourceIds') + resource_types: list[str] | None = Field(default=None, alias='ResourceTypes') + values: list[str] | None = Field(default=None, alias='Values') + +class FiltersUserGroup(GeneratedModel): + path_prefix: str | None = Field(default=None, alias='PathPrefix') + user_group_ids: list[str] | None = Field(default=None, alias='UserGroupIds') + +class FiltersUsers(GeneratedModel): + user_ids: list[str] | None = Field(default=None, alias='UserIds') + +class FiltersVirtualGateway(GeneratedModel): + connection_types: list[str] | None = Field(default=None, alias='ConnectionTypes') + link_net_ids: list[str] | None = Field(default=None, alias='LinkNetIds') + link_states: list[str] | None = Field(default=None, alias='LinkStates') + states: list[str] | None = Field(default=None, alias='States') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + virtual_gateway_ids: list[str] | None = Field(default=None, alias='VirtualGatewayIds') + +class FiltersVm(GeneratedModel): + architectures: list[str] | None = Field(default=None, alias='Architectures') + block_device_mapping_delete_on_vm_deletion: bool | None = Field(default=None, alias='BlockDeviceMappingDeleteOnVmDeletion') + block_device_mapping_device_names: list[str] | None = Field(default=None, alias='BlockDeviceMappingDeviceNames') + block_device_mapping_link_dates: list[str | datetime.datetime] | None = Field(default=None, alias='BlockDeviceMappingLinkDates') + block_device_mapping_states: list[str] | None = Field(default=None, alias='BlockDeviceMappingStates') + block_device_mapping_volume_ids: list[str] | None = Field(default=None, alias='BlockDeviceMappingVolumeIds') + boot_modes: list[BootMode] | None = Field(default=None, alias='BootModes') + client_tokens: list[str] | None = Field(default=None, alias='ClientTokens') + creation_dates: list[str | datetime.datetime] | None = Field(default=None, alias='CreationDates') + image_ids: list[str] | None = Field(default=None, alias='ImageIds') + is_source_dest_checked: bool | None = Field(default=None, alias='IsSourceDestChecked') + keypair_names: list[str] | None = Field(default=None, alias='KeypairNames') + launch_numbers: list[int] | None = Field(default=None, alias='LaunchNumbers') + lifecycles: list[str] | None = Field(default=None, alias='Lifecycles') + net_ids: list[str] | None = Field(default=None, alias='NetIds') + nic_account_ids: list[str] | None = Field(default=None, alias='NicAccountIds') + nic_descriptions: list[str] | None = Field(default=None, alias='NicDescriptions') + nic_is_source_dest_checked: bool | None = Field(default=None, alias='NicIsSourceDestChecked') + nic_link_nic_delete_on_vm_deletion: bool | None = Field(default=None, alias='NicLinkNicDeleteOnVmDeletion') + nic_link_nic_device_numbers: list[int] | None = Field(default=None, alias='NicLinkNicDeviceNumbers') + nic_link_nic_link_nic_dates: list[str | datetime.datetime] | None = Field(default=None, alias='NicLinkNicLinkNicDates') + nic_link_nic_link_nic_ids: list[str] | None = Field(default=None, alias='NicLinkNicLinkNicIds') + nic_link_nic_states: list[str] | None = Field(default=None, alias='NicLinkNicStates') + nic_link_nic_vm_account_ids: list[str] | None = Field(default=None, alias='NicLinkNicVmAccountIds') + nic_link_nic_vm_ids: list[str] | None = Field(default=None, alias='NicLinkNicVmIds') + nic_link_public_ip_account_ids: list[str] | None = Field(default=None, alias='NicLinkPublicIpAccountIds') + nic_link_public_ip_link_public_ip_ids: list[str] | None = Field(default=None, alias='NicLinkPublicIpLinkPublicIpIds') + nic_link_public_ip_public_ip_ids: list[str] | None = Field(default=None, alias='NicLinkPublicIpPublicIpIds') + nic_link_public_ip_public_ips: list[str] | None = Field(default=None, alias='NicLinkPublicIpPublicIps') + nic_mac_addresses: list[str] | None = Field(default=None, alias='NicMacAddresses') + nic_net_ids: list[str] | None = Field(default=None, alias='NicNetIds') + nic_nic_ids: list[str] | None = Field(default=None, alias='NicNicIds') + nic_private_ips_link_public_ip_account_ids: list[str] | None = Field(default=None, alias='NicPrivateIpsLinkPublicIpAccountIds') + nic_private_ips_link_public_ip_ids: list[str] | None = Field(default=None, alias='NicPrivateIpsLinkPublicIpIds') + nic_private_ips_primary_ip: bool | None = Field(default=None, alias='NicPrivateIpsPrimaryIp') + nic_private_ips_private_ips: list[str] | None = Field(default=None, alias='NicPrivateIpsPrivateIps') + nic_security_group_ids: list[str] | None = Field(default=None, alias='NicSecurityGroupIds') + nic_security_group_names: list[str] | None = Field(default=None, alias='NicSecurityGroupNames') + nic_states: list[str] | None = Field(default=None, alias='NicStates') + nic_subnet_ids: list[str] | None = Field(default=None, alias='NicSubnetIds') + nic_subregion_names: list[str] | None = Field(default=None, alias='NicSubregionNames') + platforms: list[str] | None = Field(default=None, alias='Platforms') + private_ips: list[str] | None = Field(default=None, alias='PrivateIps') + product_codes: list[str] | None = Field(default=None, alias='ProductCodes') + public_ips: list[str] | None = Field(default=None, alias='PublicIps') + reservation_ids: list[str] | None = Field(default=None, alias='ReservationIds') + root_device_names: list[str] | None = Field(default=None, alias='RootDeviceNames') + root_device_types: list[str] | None = Field(default=None, alias='RootDeviceTypes') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + security_group_names: list[str] | None = Field(default=None, alias='SecurityGroupNames') + state_reason_codes: list[int] | None = Field(default=None, alias='StateReasonCodes') + state_reason_messages: list[str] | None = Field(default=None, alias='StateReasonMessages') + state_reasons: list[str] | None = Field(default=None, alias='StateReasons') + subnet_ids: list[str] | None = Field(default=None, alias='SubnetIds') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + tenancies: list[str] | None = Field(default=None, alias='Tenancies') + tpm_enabled: bool | None = Field(default=None, alias='TpmEnabled') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + vm_security_group_ids: list[str] | None = Field(default=None, alias='VmSecurityGroupIds') + vm_security_group_names: list[str] | None = Field(default=None, alias='VmSecurityGroupNames') + vm_state_codes: list[int] | None = Field(default=None, alias='VmStateCodes') + vm_state_names: list[str] | None = Field(default=None, alias='VmStateNames') + vm_types: list[str] | None = Field(default=None, alias='VmTypes') + +class FiltersVmGroup(GeneratedModel): + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + subnet_ids: list[str] | None = Field(default=None, alias='SubnetIds') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + vm_counts: list[int] | None = Field(default=None, alias='VmCounts') + vm_group_ids: list[str] | None = Field(default=None, alias='VmGroupIds') + vm_group_names: list[str] | None = Field(default=None, alias='VmGroupNames') + vm_template_ids: list[str] | None = Field(default=None, alias='VmTemplateIds') + +class FiltersVmTemplate(GeneratedModel): + cpu_cores: list[int] | None = Field(default=None, alias='CpuCores') + cpu_generations: list[str] | None = Field(default=None, alias='CpuGenerations') + cpu_performances: list[str] | None = Field(default=None, alias='CpuPerformances') + descriptions: list[str] | None = Field(default=None, alias='Descriptions') + image_ids: list[str] | None = Field(default=None, alias='ImageIds') + keypair_names: list[str] | None = Field(default=None, alias='KeypairNames') + rams: list[int] | None = Field(default=None, alias='Rams') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + vm_template_ids: list[str] | None = Field(default=None, alias='VmTemplateIds') + vm_template_names: list[str] | None = Field(default=None, alias='VmTemplateNames') + +class FiltersVmType(GeneratedModel): + bsu_optimized: bool | None = Field(default=None, alias='BsuOptimized') + ephemerals_types: list[str] | None = Field(default=None, alias='EphemeralsTypes') + eths: list[int] | None = Field(default=None, alias='Eths') + gpus: list[int] | None = Field(default=None, alias='Gpus') + memory_sizes: list[float] | None = Field(default=None, alias='MemorySizes') + vcore_counts: list[int] | None = Field(default=None, alias='VcoreCounts') + vm_type_names: list[str] | None = Field(default=None, alias='VmTypeNames') + volume_counts: list[int] | None = Field(default=None, alias='VolumeCounts') + volume_sizes: list[int] | None = Field(default=None, alias='VolumeSizes') + +class FiltersVmsState(GeneratedModel): + maintenance_event_codes: list[str] | None = Field(default=None, alias='MaintenanceEventCodes') + maintenance_event_descriptions: list[str] | None = Field(default=None, alias='MaintenanceEventDescriptions') + maintenance_events_not_after: list[str | datetime.datetime] | None = Field(default=None, alias='MaintenanceEventsNotAfter') + maintenance_events_not_before: list[str | datetime.datetime] | None = Field(default=None, alias='MaintenanceEventsNotBefore') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + vm_states: list[str] | None = Field(default=None, alias='VmStates') + +class FiltersVmsStopHistory(GeneratedModel): + state_reasons: list[str] | None = Field(default=None, alias='StateReasons') + stop_date_after: str | datetime.datetime | None = Field(default=None, alias='StopDateAfter') + stop_date_before: str | datetime.datetime | None = Field(default=None, alias='StopDateBefore') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + +class FiltersVolume(GeneratedModel): + client_tokens: list[str] | None = Field(default=None, alias='ClientTokens') + creation_dates: list[datetime.datetime] | None = Field(default=None, alias='CreationDates') + link_volume_delete_on_vm_deletion: bool | None = Field(default=None, alias='LinkVolumeDeleteOnVmDeletion') + link_volume_device_names: list[str] | None = Field(default=None, alias='LinkVolumeDeviceNames') + link_volume_link_dates: list[datetime.datetime] | None = Field(default=None, alias='LinkVolumeLinkDates') + link_volume_link_states: list[str] | None = Field(default=None, alias='LinkVolumeLinkStates') + link_volume_vm_ids: list[str] | None = Field(default=None, alias='LinkVolumeVmIds') + snapshot_ids: list[str] | None = Field(default=None, alias='SnapshotIds') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + volume_ids: list[str] | None = Field(default=None, alias='VolumeIds') + volume_sizes: list[int] | None = Field(default=None, alias='VolumeSizes') + volume_states: list[str] | None = Field(default=None, alias='VolumeStates') + volume_types: list[str] | None = Field(default=None, alias='VolumeTypes') + +class FiltersVpnConnection(GeneratedModel): + bgp_asns: list[int] | None = Field(default=None, alias='BgpAsns') + client_gateway_ids: list[str] | None = Field(default=None, alias='ClientGatewayIds') + connection_types: list[str] | None = Field(default=None, alias='ConnectionTypes') + route_destination_ip_ranges: list[str] | None = Field(default=None, alias='RouteDestinationIpRanges') + states: list[str] | None = Field(default=None, alias='States') + static_routes_only: bool | None = Field(default=None, alias='StaticRoutesOnly') + tag_keys: list[str] | None = Field(default=None, alias='TagKeys') + tag_values: list[str] | None = Field(default=None, alias='TagValues') + tags: list[str] | None = Field(default=None, alias='Tags') + virtual_gateway_ids: list[str] | None = Field(default=None, alias='VirtualGatewayIds') + vpn_connection_ids: list[str] | None = Field(default=None, alias='VpnConnectionIds') + +class FlexibleGpu(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + flexible_gpu_id: str | None = Field(default=None, alias='FlexibleGpuId') + generation: str | None = Field(default=None, alias='Generation') + model_name: str | None = Field(default=None, alias='ModelName') + state: str | None = Field(default=None, alias='State') + subregion_name: str | None = Field(default=None, alias='SubregionName') + tags: list[Tag] | None = Field(default=None, alias='Tags') + vm_id: str | None = Field(default=None, alias='VmId') + +class FlexibleGpuCatalog(GeneratedModel): + generations: list[str] | None = Field(default=None, alias='Generations') + max_cpu: int | None = Field(default=None, alias='MaxCpu') + max_ram: int | None = Field(default=None, alias='MaxRam') + model_name: str | None = Field(default=None, alias='ModelName') + v_ram: int | None = Field(default=None, alias='VRam') + +class HealthCheck(GeneratedModel): + check_interval: int = Field(alias='CheckInterval') + healthy_threshold: int = Field(alias='HealthyThreshold') + path: str | None = Field(default=None, alias='Path') + port: int = Field(alias='Port') + protocol: str = Field(alias='Protocol') + timeout: int = Field(alias='Timeout') + unhealthy_threshold: int = Field(alias='UnhealthyThreshold') + +class Image(GeneratedModel): + account_alias: str | None = Field(default=None, alias='AccountAlias') + account_id: str | None = Field(default=None, alias='AccountId') + architecture: str | None = Field(default=None, alias='Architecture') + block_device_mappings: list[BlockDeviceMappingImage] | None = Field(default=None, alias='BlockDeviceMappings') + boot_modes: list[BootMode] | None = Field(default=None, alias='BootModes') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + description: str | None = Field(default=None, alias='Description') + file_location: str | None = Field(default=None, alias='FileLocation') + image_id: str | None = Field(default=None, alias='ImageId') + image_name: str | None = Field(default=None, alias='ImageName') + image_type: str | None = Field(default=None, alias='ImageType') + permissions_to_launch: PermissionsOnResource | None = Field(default=None, alias='PermissionsToLaunch') + product_codes: list[str] | None = Field(default=None, alias='ProductCodes') + root_device_name: str | None = Field(default=None, alias='RootDeviceName') + root_device_type: str | None = Field(default=None, alias='RootDeviceType') + secure_boot: bool | None = Field(default=None, alias='SecureBoot') + state: str | None = Field(default=None, alias='State') + state_comment: StateComment | None = Field(default=None, alias='StateComment') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + tpm_mandatory: bool | None = Field(default=None, alias='TpmMandatory') + +class ImageExportTask(GeneratedModel): + comment: str | None = Field(default=None, alias='Comment') + image_id: str | None = Field(default=None, alias='ImageId') + osu_export: OsuExportImageExportTask | None = Field(default=None, alias='OsuExport') + progress: int | None = Field(default=None, alias='Progress') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + task_id: str | None = Field(default=None, alias='TaskId') + +class InlinePolicy(GeneratedModel): + body: str | None = Field(default=None, alias='Body') + name: str | None = Field(default=None, alias='Name') + +class InternetService(GeneratedModel): + internet_service_id: str | None = Field(default=None, alias='InternetServiceId') + net_id: str | None = Field(default=None, alias='NetId') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class Keypair(GeneratedModel): + keypair_fingerprint: str | None = Field(default=None, alias='KeypairFingerprint') + keypair_id: str | None = Field(default=None, alias='KeypairId') + keypair_name: str | None = Field(default=None, alias='KeypairName') + keypair_type: str | None = Field(default=None, alias='KeypairType') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class KeypairCreated(GeneratedModel): + keypair_fingerprint: str | None = Field(default=None, alias='KeypairFingerprint') + keypair_id: str | None = Field(default=None, alias='KeypairId') + keypair_name: str | None = Field(default=None, alias='KeypairName') + keypair_type: str | None = Field(default=None, alias='KeypairType') + private_key: str | None = Field(default=None, alias='PrivateKey') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class LinkFlexibleGpuRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + flexible_gpu_id: str = Field(alias='FlexibleGpuId') + vm_id: str = Field(alias='VmId') + +class LinkFlexibleGpuResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkInternetServiceRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + internet_service_id: str = Field(alias='InternetServiceId') + net_id: str = Field(alias='NetId') + +class LinkInternetServiceResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkLoadBalancerBackendMachinesRequest(GeneratedModel): + backend_ips: list[str] | None = Field(default=None, alias='BackendIps') + backend_vm_ids: list[str] | None = Field(default=None, alias='BackendVmIds') + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class LinkLoadBalancerBackendMachinesResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkManagedPolicyToUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_orn: str = Field(alias='PolicyOrn') + user_group_name: str = Field(alias='UserGroupName') + +class LinkManagedPolicyToUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkNic(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + device_number: int | None = Field(default=None, alias='DeviceNumber') + link_nic_id: str | None = Field(default=None, alias='LinkNicId') + state: str | None = Field(default=None, alias='State') + vm_account_id: str | None = Field(default=None, alias='VmAccountId') + vm_id: str | None = Field(default=None, alias='VmId') + +class LinkNicLight(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + device_number: int | None = Field(default=None, alias='DeviceNumber') + link_nic_id: str | None = Field(default=None, alias='LinkNicId') + state: str | None = Field(default=None, alias='State') + +class LinkNicRequest(GeneratedModel): + device_number: int = Field(alias='DeviceNumber') + dry_run: bool | None = Field(default=None, alias='DryRun') + nic_id: str = Field(alias='NicId') + vm_id: str = Field(alias='VmId') + +class LinkNicResponse(GeneratedModel): + link_nic_id: str | None = Field(default=None, alias='LinkNicId') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkNicToUpdate(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + link_nic_id: str | None = Field(default=None, alias='LinkNicId') + +class LinkPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_orn: str = Field(alias='PolicyOrn') + user_name: str = Field(alias='UserName') + +class LinkPolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkPrivateIpsRequest(GeneratedModel): + allow_relink: bool | None = Field(default=None, alias='AllowRelink') + dry_run: bool | None = Field(default=None, alias='DryRun') + nic_id: str = Field(alias='NicId') + private_ips: list[str] | None = Field(default=None, alias='PrivateIps') + secondary_private_ip_count: int | None = Field(default=None, alias='SecondaryPrivateIpCount') + +class LinkPrivateIpsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkPublicIp(GeneratedModel): + link_public_ip_id: str | None = Field(default=None, alias='LinkPublicIpId') + public_dns_name: str | None = Field(default=None, alias='PublicDnsName') + public_ip: str | None = Field(default=None, alias='PublicIp') + public_ip_account_id: str | None = Field(default=None, alias='PublicIpAccountId') + public_ip_id: str | None = Field(default=None, alias='PublicIpId') + +class LinkPublicIpLightForVm(GeneratedModel): + public_dns_name: str | None = Field(default=None, alias='PublicDnsName') + public_ip: str | None = Field(default=None, alias='PublicIp') + public_ip_account_id: str | None = Field(default=None, alias='PublicIpAccountId') + +class LinkPublicIpRequest(GeneratedModel): + allow_relink: bool | None = Field(default=None, alias='AllowRelink') + dry_run: bool | None = Field(default=None, alias='DryRun') + nic_id: str | None = Field(default=None, alias='NicId') + private_ip: str | None = Field(default=None, alias='PrivateIp') + public_ip: str | None = Field(default=None, alias='PublicIp') + public_ip_id: str | None = Field(default=None, alias='PublicIpId') + vm_id: str | None = Field(default=None, alias='VmId') + +class LinkPublicIpResponse(GeneratedModel): + link_public_ip_id: str | None = Field(default=None, alias='LinkPublicIpId') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkRouteTable(GeneratedModel): + link_route_table_id: str | None = Field(default=None, alias='LinkRouteTableId') + main: bool | None = Field(default=None, alias='Main') + net_id: str | None = Field(default=None, alias='NetId') + route_table_id: str | None = Field(default=None, alias='RouteTableId') + subnet_id: str | None = Field(default=None, alias='SubnetId') + +class LinkRouteTableRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + route_table_id: str = Field(alias='RouteTableId') + subnet_id: str = Field(alias='SubnetId') + +class LinkRouteTableResponse(GeneratedModel): + link_route_table_id: str | None = Field(default=None, alias='LinkRouteTableId') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkVirtualGatewayRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str = Field(alias='NetId') + virtual_gateway_id: str = Field(alias='VirtualGatewayId') + +class LinkVirtualGatewayResponse(GeneratedModel): + net_to_virtual_gateway_link: NetToVirtualGatewayLink | None = Field(default=None, alias='NetToVirtualGatewayLink') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkVolumeRequest(GeneratedModel): + device_name: str = Field(alias='DeviceName') + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_id: str = Field(alias='VmId') + volume_id: str = Field(alias='VolumeId') + +class LinkVolumeResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class LinkedPolicy(GeneratedModel): + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + last_modification_date: datetime.datetime | None = Field(default=None, alias='LastModificationDate') + orn: str | None = Field(default=None, alias='Orn') + policy_id: str | None = Field(default=None, alias='PolicyId') + policy_name: str | None = Field(default=None, alias='PolicyName') + +class LinkedVolume(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + device_name: str | None = Field(default=None, alias='DeviceName') + state: str | None = Field(default=None, alias='State') + vm_id: str | None = Field(default=None, alias='VmId') + volume_id: str | None = Field(default=None, alias='VolumeId') + +class Listener(GeneratedModel): + backend_port: int | None = Field(default=None, alias='BackendPort') + backend_protocol: str | None = Field(default=None, alias='BackendProtocol') + load_balancer_port: int | None = Field(default=None, alias='LoadBalancerPort') + load_balancer_protocol: str | None = Field(default=None, alias='LoadBalancerProtocol') + policy_names: list[str] | None = Field(default=None, alias='PolicyNames') + server_certificate_id: str | None = Field(default=None, alias='ServerCertificateId') + +class ListenerForCreation(GeneratedModel): + backend_port: int = Field(alias='BackendPort') + backend_protocol: str | None = Field(default=None, alias='BackendProtocol') + load_balancer_port: int = Field(alias='LoadBalancerPort') + load_balancer_protocol: str = Field(alias='LoadBalancerProtocol') + server_certificate_id: str | None = Field(default=None, alias='ServerCertificateId') + +class ListenerRule(GeneratedModel): + action: str | None = Field(default=None, alias='Action') + host_name_pattern: str | None = Field(default=None, alias='HostNamePattern') + listener_id: int | None = Field(default=None, alias='ListenerId') + listener_rule_id: int | None = Field(default=None, alias='ListenerRuleId') + listener_rule_name: str | None = Field(default=None, alias='ListenerRuleName') + path_pattern: str | None = Field(default=None, alias='PathPattern') + priority: int | None = Field(default=None, alias='Priority') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + +class ListenerRuleForCreation(GeneratedModel): + action: str | None = Field(default=None, alias='Action') + host_name_pattern: str | None = Field(default=None, alias='HostNamePattern') + listener_rule_name: str = Field(alias='ListenerRuleName') + path_pattern: str | None = Field(default=None, alias='PathPattern') + priority: int = Field(alias='Priority') + +class LoadBalancer(GeneratedModel): + access_log: AccessLog | None = Field(default=None, alias='AccessLog') + application_sticky_cookie_policies: list[ApplicationStickyCookiePolicy] | None = Field(default=None, alias='ApplicationStickyCookiePolicies') + backend_ips: list[str] | None = Field(default=None, alias='BackendIps') + backend_vm_ids: list[str] | None = Field(default=None, alias='BackendVmIds') + dns_name: str | None = Field(default=None, alias='DnsName') + health_check: HealthCheck | None = Field(default=None, alias='HealthCheck') + listeners: list[Listener] | None = Field(default=None, alias='Listeners') + load_balancer_name: str | None = Field(default=None, alias='LoadBalancerName') + load_balancer_sticky_cookie_policies: list[LoadBalancerStickyCookiePolicy] | None = Field(default=None, alias='LoadBalancerStickyCookiePolicies') + load_balancer_type: str | None = Field(default=None, alias='LoadBalancerType') + net_id: str | None = Field(default=None, alias='NetId') + private_ip: str | None = Field(default=None, alias='PrivateIp') + public_ip: str | None = Field(default=None, alias='PublicIp') + secured_cookies: bool | None = Field(default=None, alias='SecuredCookies') + security_groups: list[str] | None = Field(default=None, alias='SecurityGroups') + source_security_group: SourceSecurityGroup | None = Field(default=None, alias='SourceSecurityGroup') + state: str | None = Field(default=None, alias='State') + subnets: list[str] | None = Field(default=None, alias='Subnets') + subregion_names: list[str] | None = Field(default=None, alias='SubregionNames') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class LoadBalancerLight(GeneratedModel): + load_balancer_name: str = Field(alias='LoadBalancerName') + load_balancer_port: int = Field(alias='LoadBalancerPort') + +class LoadBalancerStickyCookiePolicy(GeneratedModel): + cookie_expiration_period: int | None = Field(default=None, alias='CookieExpirationPeriod') + policy_name: str | None = Field(default=None, alias='PolicyName') + +class LoadBalancerTag(GeneratedModel): + key: str | None = Field(default=None, alias='Key') + load_balancer_name: str | None = Field(default=None, alias='LoadBalancerName') + value: str | None = Field(default=None, alias='Value') + +class Location(GeneratedModel): + code: str | None = Field(default=None, alias='Code') + name: str | None = Field(default=None, alias='Name') + +class Log(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + call_duration: int | None = Field(default=None, alias='CallDuration') + query_access_key: str | None = Field(default=None, alias='QueryAccessKey') + query_api_name: str | None = Field(default=None, alias='QueryApiName') + query_api_version: str | None = Field(default=None, alias='QueryApiVersion') + query_call_name: str | None = Field(default=None, alias='QueryCallName') + query_date: datetime.datetime | None = Field(default=None, alias='QueryDate') + query_header_raw: str | None = Field(default=None, alias='QueryHeaderRaw') + query_header_size: int | None = Field(default=None, alias='QueryHeaderSize') + query_ip_address: str | None = Field(default=None, alias='QueryIpAddress') + query_payload_raw: str | None = Field(default=None, alias='QueryPayloadRaw') + query_payload_size: int | None = Field(default=None, alias='QueryPayloadSize') + query_user_agent: str | None = Field(default=None, alias='QueryUserAgent') + request_id: str | None = Field(default=None, alias='RequestId') + response_size: int | None = Field(default=None, alias='ResponseSize') + response_status_code: int | None = Field(default=None, alias='ResponseStatusCode') + +class MaintenanceEvent(GeneratedModel): + code: str | None = Field(default=None, alias='Code') + description: str | None = Field(default=None, alias='Description') + not_after: datetime.datetime | None = Field(default=None, alias='NotAfter') + not_before: datetime.datetime | None = Field(default=None, alias='NotBefore') + +class MinimalPolicy(GeneratedModel): + id: str | None = Field(default=None, alias='Id') + name: str | None = Field(default=None, alias='Name') + orn: str | None = Field(default=None, alias='Orn') + +class NatService(GeneratedModel): + client_token: str | None = Field(default=None, alias='ClientToken') + nat_service_id: str | None = Field(default=None, alias='NatServiceId') + net_id: str | None = Field(default=None, alias='NetId') + public_ips: list[PublicIpLight] | None = Field(default=None, alias='PublicIps') + state: str | None = Field(default=None, alias='State') + subnet_id: str | None = Field(default=None, alias='SubnetId') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class Net(GeneratedModel): + dhcp_options_set_id: str | None = Field(default=None, alias='DhcpOptionsSetId') + ip_range: str | None = Field(default=None, alias='IpRange') + net_id: str | None = Field(default=None, alias='NetId') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + tenancy: str | None = Field(default=None, alias='Tenancy') + +class NetAccessPoint(GeneratedModel): + net_access_point_id: str | None = Field(default=None, alias='NetAccessPointId') + net_id: str | None = Field(default=None, alias='NetId') + route_table_ids: list[str] | None = Field(default=None, alias='RouteTableIds') + service_name: str | None = Field(default=None, alias='ServiceName') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class NetPeering(GeneratedModel): + accepter_net: AccepterNet | None = Field(default=None, alias='AccepterNet') + expiration_date: datetime.datetime | None = Field(default=None, alias='ExpirationDate') + net_peering_id: str | None = Field(default=None, alias='NetPeeringId') + source_net: SourceNet | None = Field(default=None, alias='SourceNet') + state: NetPeeringState | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class NetPeeringState(GeneratedModel): + message: str | None = Field(default=None, alias='Message') + name: str | None = Field(default=None, alias='Name') + +class NetToVirtualGatewayLink(GeneratedModel): + net_id: str | None = Field(default=None, alias='NetId') + state: str | None = Field(default=None, alias='State') + +class Nic(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + description: str | None = Field(default=None, alias='Description') + is_source_dest_checked: bool | None = Field(default=None, alias='IsSourceDestChecked') + link_nic: LinkNic | None = Field(default=None, alias='LinkNic') + link_public_ip: LinkPublicIp | None = Field(default=None, alias='LinkPublicIp') + mac_address: str | None = Field(default=None, alias='MacAddress') + net_id: str | None = Field(default=None, alias='NetId') + nic_id: str | None = Field(default=None, alias='NicId') + private_dns_name: str | None = Field(default=None, alias='PrivateDnsName') + private_ips: list[PrivateIp] | None = Field(default=None, alias='PrivateIps') + security_groups: list[SecurityGroupLight] | None = Field(default=None, alias='SecurityGroups') + state: str | None = Field(default=None, alias='State') + subnet_id: str | None = Field(default=None, alias='SubnetId') + subregion_name: str | None = Field(default=None, alias='SubregionName') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class NicForVmCreation(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + description: str | None = Field(default=None, alias='Description') + device_number: int | None = Field(default=None, alias='DeviceNumber') + nic_id: str | None = Field(default=None, alias='NicId') + private_ips: list[PrivateIpLight] | None = Field(default=None, alias='PrivateIps') + secondary_private_ip_count: int | None = Field(default=None, alias='SecondaryPrivateIpCount') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + subnet_id: str | None = Field(default=None, alias='SubnetId') + +class NicLight(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + description: str | None = Field(default=None, alias='Description') + is_source_dest_checked: bool | None = Field(default=None, alias='IsSourceDestChecked') + link_nic: LinkNicLight | None = Field(default=None, alias='LinkNic') + link_public_ip: LinkPublicIpLightForVm | None = Field(default=None, alias='LinkPublicIp') + mac_address: str | None = Field(default=None, alias='MacAddress') + net_id: str | None = Field(default=None, alias='NetId') + nic_id: str | None = Field(default=None, alias='NicId') + private_dns_name: str | None = Field(default=None, alias='PrivateDnsName') + private_ips: list[PrivateIpLightForVm] | None = Field(default=None, alias='PrivateIps') + security_groups: list[SecurityGroupLight] | None = Field(default=None, alias='SecurityGroups') + state: str | None = Field(default=None, alias='State') + subnet_id: str | None = Field(default=None, alias='SubnetId') + +class OsuApiKey(GeneratedModel): + api_key_id: str | None = Field(default=None, alias='ApiKeyId') + secret_key: str | None = Field(default=None, alias='SecretKey') + +class OsuExportImageExportTask(GeneratedModel): + disk_image_format: str = Field(alias='DiskImageFormat') + osu_bucket: str = Field(alias='OsuBucket') + osu_manifest_url: str | None = Field(default=None, alias='OsuManifestUrl') + osu_prefix: str | None = Field(default=None, alias='OsuPrefix') + +class OsuExportSnapshotExportTask(GeneratedModel): + disk_image_format: str = Field(alias='DiskImageFormat') + osu_bucket: str = Field(alias='OsuBucket') + osu_prefix: str | None = Field(default=None, alias='OsuPrefix') + +class OsuExportToCreate(GeneratedModel): + disk_image_format: str = Field(alias='DiskImageFormat') + osu_api_key: OsuApiKey | None = Field(default=None, alias='OsuApiKey') + osu_bucket: str = Field(alias='OsuBucket') + osu_manifest_url: str | None = Field(default=None, alias='OsuManifestUrl') + osu_prefix: str | None = Field(default=None, alias='OsuPrefix') + +class PermissionsOnResource(GeneratedModel): + account_ids: list[str] | None = Field(default=None, alias='AccountIds') + global_permission: bool | None = Field(default=None, alias='GlobalPermission') + +class PermissionsOnResourceCreation(GeneratedModel): + additions: PermissionsOnResource | None = Field(default=None, alias='Additions') + removals: PermissionsOnResource | None = Field(default=None, alias='Removals') + +class Phase1Options(GeneratedModel): + dpd_timeout_action: str | None = Field(default=None, alias='DpdTimeoutAction') + dpd_timeout_seconds: int | None = Field(default=None, alias='DpdTimeoutSeconds') + ike_versions: list[str] | None = Field(default=None, alias='IkeVersions') + phase1_dh_group_numbers: list[int] | None = Field(default=None, alias='Phase1DhGroupNumbers') + phase1_encryption_algorithms: list[str] | None = Field(default=None, alias='Phase1EncryptionAlgorithms') + phase1_integrity_algorithms: list[str] | None = Field(default=None, alias='Phase1IntegrityAlgorithms') + phase1_lifetime_seconds: int | None = Field(default=None, alias='Phase1LifetimeSeconds') + replay_window_size: int | None = Field(default=None, alias='ReplayWindowSize') + startup_action: str | None = Field(default=None, alias='StartupAction') + +class Phase2Options(GeneratedModel): + phase2_dh_group_numbers: list[int] | None = Field(default=None, alias='Phase2DhGroupNumbers') + phase2_encryption_algorithms: list[str] | None = Field(default=None, alias='Phase2EncryptionAlgorithms') + phase2_integrity_algorithms: list[str] | None = Field(default=None, alias='Phase2IntegrityAlgorithms') + phase2_lifetime_seconds: int | None = Field(default=None, alias='Phase2LifetimeSeconds') + pre_shared_key: str | None = Field(default=None, alias='PreSharedKey') + +class Placement(GeneratedModel): + subregion_name: str | None = Field(default=None, alias='SubregionName') + tenancy: str | None = Field(default=None, alias='Tenancy') + +class Policy(GeneratedModel): + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + description: str | None = Field(default=None, alias='Description') + is_linkable: bool | None = Field(default=None, alias='IsLinkable') + last_modification_date: datetime.datetime | None = Field(default=None, alias='LastModificationDate') + orn: str | None = Field(default=None, alias='Orn') + path: str | None = Field(default=None, alias='Path') + policy_default_version_id: str | None = Field(default=None, alias='PolicyDefaultVersionId') + policy_id: str | None = Field(default=None, alias='PolicyId') + policy_name: str | None = Field(default=None, alias='PolicyName') + resources_count: int | None = Field(default=None, alias='ResourcesCount') + +class PolicyEntities(GeneratedModel): + accounts: list[MinimalPolicy] | None = Field(default=None, alias='Accounts') + groups: list[MinimalPolicy] | None = Field(default=None, alias='Groups') + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + items_count: int | None = Field(default=None, alias='ItemsCount') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + users: list[MinimalPolicy] | None = Field(default=None, alias='Users') + +class PolicyVersion(GeneratedModel): + body: str | None = Field(default=None, alias='Body') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + default_version: bool | None = Field(default=None, alias='DefaultVersion') + version_id: str | None = Field(default=None, alias='VersionId') + +class PrivateIp(GeneratedModel): + is_primary: bool | None = Field(default=None, alias='IsPrimary') + link_public_ip: LinkPublicIp | None = Field(default=None, alias='LinkPublicIp') + private_dns_name: str | None = Field(default=None, alias='PrivateDnsName') + private_ip: str | None = Field(default=None, alias='PrivateIp') + +class PrivateIpLight(GeneratedModel): + is_primary: bool | None = Field(default=None, alias='IsPrimary') + private_ip: str | None = Field(default=None, alias='PrivateIp') + +class PrivateIpLightForVm(GeneratedModel): + is_primary: bool | None = Field(default=None, alias='IsPrimary') + link_public_ip: LinkPublicIpLightForVm | None = Field(default=None, alias='LinkPublicIp') + private_dns_name: str | None = Field(default=None, alias='PrivateDnsName') + private_ip: str | None = Field(default=None, alias='PrivateIp') + +class ProductType(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + product_type_id: str | None = Field(default=None, alias='ProductTypeId') + vendor: str | None = Field(default=None, alias='Vendor') + +class PublicIp(GeneratedModel): + link_public_ip_id: str | None = Field(default=None, alias='LinkPublicIpId') + nat_service_id: str | None = Field(default=None, alias='NatServiceId') + net_access_point_ids: list[str] | None = Field(default=None, alias='NetAccessPointIds') + nic_account_id: str | None = Field(default=None, alias='NicAccountId') + nic_id: str | None = Field(default=None, alias='NicId') + private_ip: str | None = Field(default=None, alias='PrivateIp') + public_ip: str | None = Field(default=None, alias='PublicIp') + public_ip_id: str | None = Field(default=None, alias='PublicIpId') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_id: str | None = Field(default=None, alias='VmId') + +class PublicIpLight(GeneratedModel): + public_ip: str | None = Field(default=None, alias='PublicIp') + public_ip_id: str | None = Field(default=None, alias='PublicIpId') + +class PutUserGroupPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_document: str = Field(alias='PolicyDocument') + policy_name: str = Field(alias='PolicyName') + user_group_name: str = Field(alias='UserGroupName') + user_group_path: str | None = Field(default=None, alias='UserGroupPath') + +class PutUserGroupPolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class PutUserPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_document: str = Field(alias='PolicyDocument') + policy_name: str = Field(alias='PolicyName') + user_name: str = Field(alias='UserName') + +class PutUserPolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class Quota(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + description: str | None = Field(default=None, alias='Description') + max_value: int | None = Field(default=None, alias='MaxValue') + name: str | None = Field(default=None, alias='Name') + quota_collection: str | None = Field(default=None, alias='QuotaCollection') + short_description: str | None = Field(default=None, alias='ShortDescription') + used_value: int | None = Field(default=None, alias='UsedValue') + +class QuotaTypes(GeneratedModel): + quota_type: str | None = Field(default=None, alias='QuotaType') + quotas: list[Quota] | None = Field(default=None, alias='Quotas') + +class ReadAccessKeysRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersAccessKeys | None = Field(default=None, alias='Filters') + tag: str | None = Field(default=None, alias='Tag') + user_name: str | None = Field(default=None, alias='UserName') + +class ReadAccessKeysResponse(GeneratedModel): + access_keys: list[AccessKey] | None = Field(default=None, alias='AccessKeys') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadAccountsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class ReadAccountsResponse(GeneratedModel): + accounts: list[Account] | None = Field(default=None, alias='Accounts') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadAdminPasswordRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_id: str = Field(alias='VmId') + +class ReadAdminPasswordResponse(GeneratedModel): + admin_password: str | None = Field(default=None, alias='AdminPassword') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_id: str | None = Field(default=None, alias='VmId') + +class ReadApiAccessPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class ReadApiAccessPolicyResponse(GeneratedModel): + api_access_policy: ApiAccessPolicy | None = Field(default=None, alias='ApiAccessPolicy') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadApiAccessRulesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersApiAccessRule | None = Field(default=None, alias='Filters') + +class ReadApiAccessRulesResponse(GeneratedModel): + api_access_rules: list[ApiAccessRule] | None = Field(default=None, alias='ApiAccessRules') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadApiLogsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersApiLog | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + with_: With | None = Field(default=None, alias='With') + +class ReadApiLogsResponse(GeneratedModel): + logs: list[Log] | None = Field(default=None, alias='Logs') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadCO2EmissionAccountRequest(GeneratedModel): + from_month: str | datetime.datetime = Field(alias='FromMonth') + overall: bool | None = Field(default=None, alias='Overall') + to_month: str | datetime.datetime = Field(alias='ToMonth') + +class ReadCO2EmissionAccountResponse(GeneratedModel): + co2_emission_entries: list[CO2EmissionEntry] | None = Field(default=None, alias='CO2EmissionEntries') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + unit: str | None = Field(default=None, alias='Unit') + value: float | None = Field(default=None, alias='Value') + +class ReadCasRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersCa | None = Field(default=None, alias='Filters') + +class ReadCasResponse(GeneratedModel): + cas: list[Ca] | None = Field(default=None, alias='Cas') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadCatalogRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class ReadCatalogResponse(GeneratedModel): + catalog: Catalog | None = Field(default=None, alias='Catalog') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadCatalogsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersCatalogs | None = Field(default=None, alias='Filters') + +class ReadCatalogsResponse(GeneratedModel): + catalogs: list[Catalogs] | None = Field(default=None, alias='Catalogs') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadClientGatewaysRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersClientGateway | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadClientGatewaysResponse(GeneratedModel): + client_gateways: list[ClientGateway] | None = Field(default=None, alias='ClientGateways') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadConsoleOutputRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_id: str = Field(alias='VmId') + +class ReadConsoleOutputResponse(GeneratedModel): + console_output: str | None = Field(default=None, alias='ConsoleOutput') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_id: str | None = Field(default=None, alias='VmId') + +class ReadConsumptionAccountRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + from_date: str | datetime.datetime = Field(alias='FromDate') + overall: bool | None = Field(default=None, alias='Overall') + show_price: bool | None = Field(default=None, alias='ShowPrice') + show_resource_details: bool | None = Field(default=None, alias='ShowResourceDetails') + to_date: str | datetime.datetime = Field(alias='ToDate') + +class ReadConsumptionAccountResponse(GeneratedModel): + consumption_entries: list[ConsumptionEntry] | None = Field(default=None, alias='ConsumptionEntries') + currency: str | None = Field(default=None, alias='Currency') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadDedicatedGroupsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersDedicatedGroup | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadDedicatedGroupsResponse(GeneratedModel): + dedicated_groups: list[DedicatedGroup] | None = Field(default=None, alias='DedicatedGroups') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadDhcpOptionsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersDhcpOptions | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadDhcpOptionsResponse(GeneratedModel): + dhcp_options_sets: list[DhcpOptionsSet] | None = Field(default=None, alias='DhcpOptionsSets') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadDirectLinkInterfacesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersDirectLinkInterface | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadDirectLinkInterfacesResponse(GeneratedModel): + direct_link_interfaces: list[DirectLinkInterfaces] | None = Field(default=None, alias='DirectLinkInterfaces') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadDirectLinksRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersDirectLink | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadDirectLinksResponse(GeneratedModel): + direct_links: list[DirectLink] | None = Field(default=None, alias='DirectLinks') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadEntitiesLinkedToPolicyRequest(GeneratedModel): + entities_type: list[Literal['ACCOUNT', 'USER', 'GROUP']] | None = Field(default=None, alias='EntitiesType') + first_item: int | None = Field(default=None, alias='FirstItem') + policy_orn: str = Field(alias='PolicyOrn') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadEntitiesLinkedToPolicyResponse(GeneratedModel): + policy_entities: PolicyEntities | None = Field(default=None, alias='PolicyEntities') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadFlexibleGpuCatalogRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class ReadFlexibleGpuCatalogResponse(GeneratedModel): + flexible_gpu_catalog: list[FlexibleGpuCatalog] | None = Field(default=None, alias='FlexibleGpuCatalog') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadFlexibleGpusRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersFlexibleGpu | None = Field(default=None, alias='Filters') + +class ReadFlexibleGpusResponse(GeneratedModel): + flexible_gpus: list[FlexibleGpu] | None = Field(default=None, alias='FlexibleGpus') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadImageExportTasksRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersReadImageExportTask | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadImageExportTasksResponse(GeneratedModel): + image_export_tasks: list[ImageExportTask] | None = Field(default=None, alias='ImageExportTasks') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadImagesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersImage | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadImagesResponse(GeneratedModel): + images: list[Image] | None = Field(default=None, alias='Images') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadInternetServicesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersInternetService | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadInternetServicesResponse(GeneratedModel): + internet_services: list[InternetService] | None = Field(default=None, alias='InternetServices') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadKeypairsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersKeypair | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadKeypairsResponse(GeneratedModel): + keypairs: list[Keypair] | None = Field(default=None, alias='Keypairs') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadLinkedPoliciesFilters(GeneratedModel): + path_prefix: str | None = Field(default=None, alias='PathPrefix') + +class ReadLinkedPoliciesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: ReadLinkedPoliciesFilters | None = Field(default=None, alias='Filters') + first_item: int | None = Field(default=None, alias='FirstItem') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + user_name: str = Field(alias='UserName') + +class ReadLinkedPoliciesResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + policies: list[LinkedPolicy] | None = Field(default=None, alias='Policies') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadListenerRulesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersListenerRule | None = Field(default=None, alias='Filters') + +class ReadListenerRulesResponse(GeneratedModel): + listener_rules: list[ListenerRule] | None = Field(default=None, alias='ListenerRules') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadLoadBalancerTagsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_names: list[str] = Field(alias='LoadBalancerNames') + +class ReadLoadBalancerTagsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + tags: list[LoadBalancerTag] | None = Field(default=None, alias='Tags') + +class ReadLoadBalancersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersLoadBalancer | None = Field(default=None, alias='Filters') + +class ReadLoadBalancersResponse(GeneratedModel): + load_balancers: list[LoadBalancer] | None = Field(default=None, alias='LoadBalancers') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadLocationsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadLocationsResponse(GeneratedModel): + locations: list[Location] | None = Field(default=None, alias='Locations') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadManagedPoliciesLinkedToUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersUserGroup | None = Field(default=None, alias='Filters') + first_item: int | None = Field(default=None, alias='FirstItem') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + user_group_name: str = Field(alias='UserGroupName') + +class ReadManagedPoliciesLinkedToUserGroupResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + policies: list[LinkedPolicy] | None = Field(default=None, alias='Policies') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadNatServicesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersNatService | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadNatServicesResponse(GeneratedModel): + nat_services: list[NatService] | None = Field(default=None, alias='NatServices') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadNetAccessPointServicesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersService | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadNetAccessPointServicesResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + services: list[Service] | None = Field(default=None, alias='Services') + +class ReadNetAccessPointsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersNetAccessPoint | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadNetAccessPointsResponse(GeneratedModel): + net_access_points: list[NetAccessPoint] | None = Field(default=None, alias='NetAccessPoints') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadNetPeeringsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersNetPeering | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadNetPeeringsResponse(GeneratedModel): + net_peerings: list[NetPeering] | None = Field(default=None, alias='NetPeerings') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadNetsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersNet | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadNetsResponse(GeneratedModel): + nets: list[Net] | None = Field(default=None, alias='Nets') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadNicsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersNic | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadNicsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + nics: list[Nic] | None = Field(default=None, alias='Nics') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPoliciesFilters(GeneratedModel): + only_linked: bool | None = Field(default=None, alias='OnlyLinked') + path_prefix: str | None = Field(default=None, alias='PathPrefix') + scope: Literal['LOCAL', 'OWS'] | None = Field(default=None, alias='Scope') + +class ReadPoliciesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: ReadPoliciesFilters | None = Field(default=None, alias='Filters') + first_item: int | None = Field(default=None, alias='FirstItem') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadPoliciesResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + policies: list[Policy] | None = Field(default=None, alias='Policies') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPolicyRequest(GeneratedModel): + policy_orn: str = Field(alias='PolicyOrn') + +class ReadPolicyResponse(GeneratedModel): + policy: Policy | None = Field(default=None, alias='Policy') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPolicyVersionRequest(GeneratedModel): + policy_orn: str = Field(alias='PolicyOrn') + version_id: str = Field(alias='VersionId') + +class ReadPolicyVersionResponse(GeneratedModel): + policy_version: PolicyVersion | None = Field(default=None, alias='PolicyVersion') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPolicyVersionsRequest(GeneratedModel): + first_item: int | None = Field(default=None, alias='FirstItem') + policy_orn: str = Field(alias='PolicyOrn') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadPolicyVersionsResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + policy_versions: list[PolicyVersion] | None = Field(default=None, alias='PolicyVersions') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadProductTypesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersProductType | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadProductTypesResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + product_types: list[ProductType] | None = Field(default=None, alias='ProductTypes') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPublicCatalogRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class ReadPublicCatalogResponse(GeneratedModel): + catalog: Catalog | None = Field(default=None, alias='Catalog') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPublicIpRangesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadPublicIpRangesResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + public_ips: list[str] | None = Field(default=None, alias='PublicIps') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadPublicIpsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersPublicIp | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadPublicIpsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + public_ips: list[PublicIp] | None = Field(default=None, alias='PublicIps') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadQuotasRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersQuota | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadQuotasResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + quota_types: list[QuotaTypes] | None = Field(default=None, alias='QuotaTypes') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadRegionsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + +class ReadRegionsResponse(GeneratedModel): + regions: list[Region] | None = Field(default=None, alias='Regions') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadRouteTablesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersRouteTable | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadRouteTablesResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + route_tables: list[RouteTable] | None = Field(default=None, alias='RouteTables') + +class ReadSecurityGroupsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersSecurityGroup | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadSecurityGroupsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + security_groups: list[SecurityGroup] | None = Field(default=None, alias='SecurityGroups') + +class ReadServerCertificatesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersServerCertificate | None = Field(default=None, alias='Filters') + +class ReadServerCertificatesResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + server_certificates: list[ServerCertificate] | None = Field(default=None, alias='ServerCertificates') + +class ReadSnapshotExportTasksRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersSnapshotExportTask | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadSnapshotExportTasksResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + snapshot_export_tasks: list[SnapshotExportTask] | None = Field(default=None, alias='SnapshotExportTasks') + +class ReadSnapshotsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersSnapshot | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadSnapshotsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + snapshots: list[Snapshot] | None = Field(default=None, alias='Snapshots') + +class ReadSubnetsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersSubnet | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadSubnetsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + subnets: list[Subnet] | None = Field(default=None, alias='Subnets') + +class ReadSubregionsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersSubregion | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadSubregionsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + subregions: list[Subregion] | None = Field(default=None, alias='Subregions') + +class ReadTagsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersTag | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadTagsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + tags: list[Tag] | None = Field(default=None, alias='Tags') + +class ReadUnitPriceRequest(GeneratedModel): + operation: str = Field(alias='Operation') + service: str = Field(alias='Service') + type: str = Field(alias='Type') + +class ReadUnitPriceResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + unit_price_entry: UnitPriceEntry | None = Field(default=None, alias='UnitPriceEntry') + +class ReadUserGroupPoliciesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + first_item: int | None = Field(default=None, alias='FirstItem') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + user_group_name: str = Field(alias='UserGroupName') + user_group_path: str | None = Field(default=None, alias='UserGroupPath') + +class ReadUserGroupPoliciesResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + policies: list[InlinePolicy] | None = Field(default=None, alias='Policies') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadUserGroupPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_name: str = Field(alias='PolicyName') + user_group_name: str = Field(alias='UserGroupName') + user_group_path: str | None = Field(default=None, alias='UserGroupPath') + +class ReadUserGroupPolicyResponse(GeneratedModel): + policy: InlinePolicy | None = Field(default=None, alias='Policy') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + path: str | None = Field(default=None, alias='Path') + user_group_name: str = Field(alias='UserGroupName') + +class ReadUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user_group: UserGroup | None = Field(default=None, alias='UserGroup') + users: list[User] | None = Field(default=None, alias='Users') + +class ReadUserGroupsPerUserRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_name: str = Field(alias='UserName') + user_path: str | None = Field(default=None, alias='UserPath') + +class ReadUserGroupsPerUserResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user_groups: list[UserGroup] | None = Field(default=None, alias='UserGroups') + +class ReadUserGroupsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersUserGroup | None = Field(default=None, alias='Filters') + first_item: int | None = Field(default=None, alias='FirstItem') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadUserGroupsResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user_groups: list[UserGroup] | None = Field(default=None, alias='UserGroups') + +class ReadUserPoliciesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_name: str = Field(alias='UserName') + +class ReadUserPoliciesResponse(GeneratedModel): + policy_names: list[str] | None = Field(default=None, alias='PolicyNames') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadUserPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_name: str = Field(alias='PolicyName') + user_name: str = Field(alias='UserName') + +class ReadUserPolicyResponse(GeneratedModel): + policy_document: str | None = Field(default=None, alias='PolicyDocument') + policy_name: str | None = Field(default=None, alias='PolicyName') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user_name: str | None = Field(default=None, alias='UserName') + +class ReadUsersRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersUsers | None = Field(default=None, alias='Filters') + first_item: int | None = Field(default=None, alias='FirstItem') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadUsersResponse(GeneratedModel): + has_more_items: bool | None = Field(default=None, alias='HasMoreItems') + max_results_limit: int | None = Field(default=None, alias='MaxResultsLimit') + max_results_truncated: bool | None = Field(default=None, alias='MaxResultsTruncated') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + users: list[User] | None = Field(default=None, alias='Users') + +class ReadVirtualGatewaysRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVirtualGateway | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVirtualGatewaysResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + virtual_gateways: list[VirtualGateway] | None = Field(default=None, alias='VirtualGateways') + +class ReadVmGroupsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVmGroup | None = Field(default=None, alias='Filters') + +class ReadVmGroupsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_groups: list[VmGroup] | None = Field(default=None, alias='VmGroups') + +class ReadVmTemplatesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVmTemplate | None = Field(default=None, alias='Filters') + +class ReadVmTemplatesResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_templates: list[VmTemplate] | None = Field(default=None, alias='VmTemplates') + +class ReadVmTypesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVmType | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVmTypesResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_types: list[VmType] | None = Field(default=None, alias='VmTypes') + +class ReadVmsHealthRequest(GeneratedModel): + backend_vm_ids: list[str] | None = Field(default=None, alias='BackendVmIds') + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class ReadVmsHealthResponse(GeneratedModel): + backend_vm_health: list[BackendVmHealth] | None = Field(default=None, alias='BackendVmHealth') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ReadVmsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVm | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVmsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vms: list[Vm] | None = Field(default=None, alias='Vms') + +class ReadVmsStateRequest(GeneratedModel): + all_vms: bool | None = Field(default=None, alias='AllVms') + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVmsState | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVmsStateResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_states: list[VmStates] | None = Field(default=None, alias='VmStates') + +class ReadVmsStopHistoryRequest(GeneratedModel): + filters: FiltersVmsStopHistory | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVmsStopHistoryResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vms_stop_history: list[VmsStopHistory] | None = Field(default=None, alias='VmsStopHistory') + +class ReadVolumeUpdateTasksRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersReadVolumeUpdateTask | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVolumeUpdateTasksResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + volume_update_tasks: list[VolumeUpdateTask] | None = Field(default=None, alias='VolumeUpdateTasks') + +class ReadVolumesRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVolume | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVolumesResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + volumes: list[Volume] | None = Field(default=None, alias='Volumes') + +class ReadVpnConnectionsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + filters: FiltersVpnConnection | None = Field(default=None, alias='Filters') + next_page_token: str | None = Field(default=None, alias='NextPageToken') + results_per_page: int | None = Field(default=None, alias='ResultsPerPage') + +class ReadVpnConnectionsResponse(GeneratedModel): + next_page_token: str | None = Field(default=None, alias='NextPageToken') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vpn_connections: list[VpnConnection] | None = Field(default=None, alias='VpnConnections') + +class RebootVmsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_ids: list[str] = Field(alias='VmIds') + +class RebootVmsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class Region(GeneratedModel): + endpoint: str | None = Field(default=None, alias='Endpoint') + region_name: str | None = Field(default=None, alias='RegionName') + +class RegisterVmsInLoadBalancerRequest(GeneratedModel): + backend_vm_ids: list[str] = Field(alias='BackendVmIds') + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class RegisterVmsInLoadBalancerResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class RejectNetPeeringRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_peering_id: str = Field(alias='NetPeeringId') + +class RejectNetPeeringResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class RemoveUserFromUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + user_group_name: str = Field(alias='UserGroupName') + user_group_path: str | None = Field(default=None, alias='UserGroupPath') + user_name: str = Field(alias='UserName') + user_path: str | None = Field(default=None, alias='UserPath') + +class RemoveUserFromUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ResourceLoadBalancerTag(GeneratedModel): + key: str = Field(alias='Key') + +class ResourceTag(GeneratedModel): + key: str = Field(alias='Key') + value: str = Field(alias='Value') + +class ResponseContext(GeneratedModel): + request_id: str | None = Field(default=None, alias='RequestId') + +class Route(GeneratedModel): + creation_method: str | None = Field(default=None, alias='CreationMethod') + destination_ip_range: str | None = Field(default=None, alias='DestinationIpRange') + destination_service_id: str | None = Field(default=None, alias='DestinationServiceId') + gateway_id: str | None = Field(default=None, alias='GatewayId') + nat_service_id: str | None = Field(default=None, alias='NatServiceId') + net_access_point_id: str | None = Field(default=None, alias='NetAccessPointId') + net_peering_id: str | None = Field(default=None, alias='NetPeeringId') + nic_id: str | None = Field(default=None, alias='NicId') + state: str | None = Field(default=None, alias='State') + vm_account_id: str | None = Field(default=None, alias='VmAccountId') + vm_id: str | None = Field(default=None, alias='VmId') + +class RouteLight(GeneratedModel): + destination_ip_range: str | None = Field(default=None, alias='DestinationIpRange') + route_type: str | None = Field(default=None, alias='RouteType') + state: str | None = Field(default=None, alias='State') + +class RoutePropagatingVirtualGateway(GeneratedModel): + virtual_gateway_id: str | None = Field(default=None, alias='VirtualGatewayId') + +class RouteTable(GeneratedModel): + link_route_tables: list[LinkRouteTable] | None = Field(default=None, alias='LinkRouteTables') + net_id: str | None = Field(default=None, alias='NetId') + route_propagating_virtual_gateways: list[RoutePropagatingVirtualGateway] | None = Field(default=None, alias='RoutePropagatingVirtualGateways') + route_table_id: str | None = Field(default=None, alias='RouteTableId') + routes: list[Route] | None = Field(default=None, alias='Routes') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class ScaleDownVmGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_group_id: str = Field(alias='VmGroupId') + vm_subtraction: int = Field(alias='VmSubtraction') + +class ScaleDownVmGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ScaleUpVmGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_addition: int = Field(alias='VmAddition') + vm_group_id: str = Field(alias='VmGroupId') + +class ScaleUpVmGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +SecureBootAction = Literal['enable', 'disable', 'setup-mode', 'none', 'restore-factory-keys'] + +class SecurityGroup(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + description: str | None = Field(default=None, alias='Description') + inbound_rules: list[SecurityGroupRule] | None = Field(default=None, alias='InboundRules') + net_id: str | None = Field(default=None, alias='NetId') + outbound_rules: list[SecurityGroupRule] | None = Field(default=None, alias='OutboundRules') + security_group_id: str | None = Field(default=None, alias='SecurityGroupId') + security_group_name: str | None = Field(default=None, alias='SecurityGroupName') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class SecurityGroupLight(GeneratedModel): + security_group_id: str | None = Field(default=None, alias='SecurityGroupId') + security_group_name: str | None = Field(default=None, alias='SecurityGroupName') + +class SecurityGroupRule(GeneratedModel): + from_port_range: int | None = Field(default=None, alias='FromPortRange') + ip_protocol: str | None = Field(default=None, alias='IpProtocol') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + security_group_rule_id: str | None = Field(default=None, alias='SecurityGroupRuleId') + security_groups_members: list[SecurityGroupsMember] | None = Field(default=None, alias='SecurityGroupsMembers') + service_ids: list[str] | None = Field(default=None, alias='ServiceIds') + to_port_range: int | None = Field(default=None, alias='ToPortRange') + +class SecurityGroupsMember(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + security_group_id: str | None = Field(default=None, alias='SecurityGroupId') + security_group_name: str | None = Field(default=None, alias='SecurityGroupName') + +class ServerCertificate(GeneratedModel): + expiration_date: datetime.datetime | None = Field(default=None, alias='ExpirationDate') + id: str | None = Field(default=None, alias='Id') + name: str | None = Field(default=None, alias='Name') + orn: str | None = Field(default=None, alias='Orn') + path: str | None = Field(default=None, alias='Path') + upload_date: datetime.datetime | None = Field(default=None, alias='UploadDate') + +class Service(GeneratedModel): + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + service_id: str | None = Field(default=None, alias='ServiceId') + service_name: str | None = Field(default=None, alias='ServiceName') + +class SetDefaultPolicyVersionRequest(GeneratedModel): + policy_orn: str = Field(alias='PolicyOrn') + version_id: str = Field(alias='VersionId') + +class SetDefaultPolicyVersionResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class ShutdownBehaviorConfiguration(GeneratedModel): + guest_action: Literal['stop', 'terminate'] | None = Field(default=None, alias='GuestAction') + host_action: Literal['restart', 'stop'] | None = Field(default=None, alias='HostAction') + +class Snapshot(GeneratedModel): + account_alias: str | None = Field(default=None, alias='AccountAlias') + account_id: str | None = Field(default=None, alias='AccountId') + client_token: str | None = Field(default=None, alias='ClientToken') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + description: str | None = Field(default=None, alias='Description') + permissions_to_create_volume: PermissionsOnResource | None = Field(default=None, alias='PermissionsToCreateVolume') + progress: int | None = Field(default=None, alias='Progress') + snapshot_id: str | None = Field(default=None, alias='SnapshotId') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + volume_id: str | None = Field(default=None, alias='VolumeId') + volume_size: int | None = Field(default=None, alias='VolumeSize') + +class SnapshotExportTask(GeneratedModel): + comment: str | None = Field(default=None, alias='Comment') + osu_export: OsuExportSnapshotExportTask | None = Field(default=None, alias='OsuExport') + progress: int | None = Field(default=None, alias='Progress') + snapshot_id: str | None = Field(default=None, alias='SnapshotId') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + task_id: str | None = Field(default=None, alias='TaskId') + +class SourceNet(GeneratedModel): + account_id: str | None = Field(default=None, alias='AccountId') + ip_range: str | None = Field(default=None, alias='IpRange') + net_id: str | None = Field(default=None, alias='NetId') + +class SourceSecurityGroup(GeneratedModel): + security_group_account_id: str | None = Field(default=None, alias='SecurityGroupAccountId') + security_group_name: str | None = Field(default=None, alias='SecurityGroupName') + +class StartVmsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + vm_ids: list[str] = Field(alias='VmIds') + +class StartVmsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vms: list[VmState] | None = Field(default=None, alias='Vms') + +class StateComment(GeneratedModel): + state_code: str | None = Field(default=None, alias='StateCode') + state_message: str | None = Field(default=None, alias='StateMessage') + +class StopVmsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + force_stop: bool | None = Field(default=None, alias='ForceStop') + vm_ids: list[str] = Field(alias='VmIds') + +class StopVmsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vms: list[VmState] | None = Field(default=None, alias='Vms') + +class Subnet(GeneratedModel): + available_ips_count: int | None = Field(default=None, alias='AvailableIpsCount') + ip_range: str | None = Field(default=None, alias='IpRange') + map_public_ip_on_launch: bool | None = Field(default=None, alias='MapPublicIpOnLaunch') + net_id: str | None = Field(default=None, alias='NetId') + state: str | None = Field(default=None, alias='State') + subnet_id: str | None = Field(default=None, alias='SubnetId') + subregion_name: str | None = Field(default=None, alias='SubregionName') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + +class Subregion(GeneratedModel): + location_code: str | None = Field(default=None, alias='LocationCode') + region_name: str | None = Field(default=None, alias='RegionName') + state: str | None = Field(default=None, alias='State') + subregion_name: str | None = Field(default=None, alias='SubregionName') + +class Tag(GeneratedModel): + key: str | None = Field(default=None, alias='Key') + resource_id: str | None = Field(default=None, alias='ResourceId') + resource_type: str | None = Field(default=None, alias='ResourceType') + value: str | None = Field(default=None, alias='Value') + +class UnitPriceEntry(GeneratedModel): + currency: str | None = Field(default=None, alias='Currency') + operation: str | None = Field(default=None, alias='Operation') + service: str | None = Field(default=None, alias='Service') + type: str | None = Field(default=None, alias='Type') + unit: str | None = Field(default=None, alias='Unit') + unit_price: float | None = Field(default=None, alias='UnitPrice') + +class UnlinkFlexibleGpuRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + flexible_gpu_id: str = Field(alias='FlexibleGpuId') + +class UnlinkFlexibleGpuResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkInternetServiceRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + internet_service_id: str = Field(alias='InternetServiceId') + net_id: str = Field(alias='NetId') + +class UnlinkInternetServiceResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkLoadBalancerBackendMachinesRequest(GeneratedModel): + backend_ips: list[str] | None = Field(default=None, alias='BackendIps') + backend_vm_ids: list[str] | None = Field(default=None, alias='BackendVmIds') + dry_run: bool | None = Field(default=None, alias='DryRun') + load_balancer_name: str = Field(alias='LoadBalancerName') + +class UnlinkLoadBalancerBackendMachinesResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkManagedPolicyFromUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_orn: str = Field(alias='PolicyOrn') + user_group_name: str = Field(alias='UserGroupName') + +class UnlinkManagedPolicyFromUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkNicRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + link_nic_id: str = Field(alias='LinkNicId') + +class UnlinkNicResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + policy_orn: str = Field(alias='PolicyOrn') + user_name: str = Field(alias='UserName') + +class UnlinkPolicyResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkPrivateIpsRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + nic_id: str = Field(alias='NicId') + private_ips: list[str] = Field(alias='PrivateIps') + +class UnlinkPrivateIpsResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkPublicIpRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + link_public_ip_id: str | None = Field(default=None, alias='LinkPublicIpId') + public_ip: str | None = Field(default=None, alias='PublicIp') + +class UnlinkPublicIpResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkRouteTableRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + link_route_table_id: str = Field(alias='LinkRouteTableId') + +class UnlinkRouteTableResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkVirtualGatewayRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str = Field(alias='NetId') + virtual_gateway_id: str = Field(alias='VirtualGatewayId') + +class UnlinkVirtualGatewayResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UnlinkVolumeRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + force_unlink: bool | None = Field(default=None, alias='ForceUnlink') + volume_id: str = Field(alias='VolumeId') + +class UnlinkVolumeResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateAccessKeyRequest(GeneratedModel): + access_key_id: str = Field(alias='AccessKeyId') + clear_expiration_date: bool | None = Field(default=None, alias='ClearExpirationDate') + clear_tag: bool | None = Field(default=None, alias='ClearTag') + dry_run: bool | None = Field(default=None, alias='DryRun') + expiration_date: datetime.datetime | str | None = Field(default=None, alias='ExpirationDate') + state: str | None = Field(default=None, alias='State') + tag: str | None = Field(default=None, alias='Tag') + user_name: str | None = Field(default=None, alias='UserName') + +class UpdateAccessKeyResponse(GeneratedModel): + access_key: AccessKey | None = Field(default=None, alias='AccessKey') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateAccountRequest(GeneratedModel): + additional_emails: list[str] | None = Field(default=None, alias='AdditionalEmails') + city: str | None = Field(default=None, alias='City') + company_name: str | None = Field(default=None, alias='CompanyName') + country: str | None = Field(default=None, alias='Country') + dry_run: bool | None = Field(default=None, alias='DryRun') + email: str | None = Field(default=None, alias='Email') + first_name: str | None = Field(default=None, alias='FirstName') + job_title: str | None = Field(default=None, alias='JobTitle') + last_name: str | None = Field(default=None, alias='LastName') + mobile_number: str | None = Field(default=None, alias='MobileNumber') + phone_number: str | None = Field(default=None, alias='PhoneNumber') + state_province: str | None = Field(default=None, alias='StateProvince') + vat_number: str | None = Field(default=None, alias='VatNumber') + zip_code: str | None = Field(default=None, alias='ZipCode') + +class UpdateAccountResponse(GeneratedModel): + account: Account | None = Field(default=None, alias='Account') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateApiAccessPolicyRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + max_access_key_expiration_seconds: int = Field(alias='MaxAccessKeyExpirationSeconds') + require_trusted_env: bool = Field(alias='RequireTrustedEnv') + +class UpdateApiAccessPolicyResponse(GeneratedModel): + api_access_policy: ApiAccessPolicy | None = Field(default=None, alias='ApiAccessPolicy') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateApiAccessRuleRequest(GeneratedModel): + api_access_rule_id: str = Field(alias='ApiAccessRuleId') + ca_ids: list[str] | None = Field(default=None, alias='CaIds') + cns: list[str] | None = Field(default=None, alias='Cns') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + ip_ranges: list[str] | None = Field(default=None, alias='IpRanges') + +class UpdateApiAccessRuleResponse(GeneratedModel): + api_access_rule: ApiAccessRule | None = Field(default=None, alias='ApiAccessRule') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateCaRequest(GeneratedModel): + ca_id: str = Field(alias='CaId') + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + +class UpdateCaResponse(GeneratedModel): + ca: Ca | None = Field(default=None, alias='Ca') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateDedicatedGroupRequest(GeneratedModel): + dedicated_group_id: str = Field(alias='DedicatedGroupId') + dry_run: bool | None = Field(default=None, alias='DryRun') + name: str = Field(alias='Name') + +class UpdateDedicatedGroupResponse(GeneratedModel): + dedicated_group: DedicatedGroup | None = Field(default=None, alias='DedicatedGroup') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateDirectLinkInterfaceRequest(GeneratedModel): + direct_link_interface_id: str = Field(alias='DirectLinkInterfaceId') + dry_run: bool | None = Field(default=None, alias='DryRun') + mtu: Literal[1500] = Field(alias='Mtu') + +class UpdateDirectLinkInterfaceResponse(GeneratedModel): + direct_link_interface: DirectLinkInterfaces | None = Field(default=None, alias='DirectLinkInterface') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateFlexibleGpuRequest(GeneratedModel): + delete_on_vm_deletion: bool | None = Field(default=None, alias='DeleteOnVmDeletion') + dry_run: bool | None = Field(default=None, alias='DryRun') + flexible_gpu_id: str = Field(alias='FlexibleGpuId') + +class UpdateFlexibleGpuResponse(GeneratedModel): + flexible_gpu: FlexibleGpu | None = Field(default=None, alias='FlexibleGpu') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateImageRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + image_id: str = Field(alias='ImageId') + permissions_to_launch: PermissionsOnResourceCreation | None = Field(default=None, alias='PermissionsToLaunch') + product_codes: list[str] | None = Field(default=None, alias='ProductCodes') + +class UpdateImageResponse(GeneratedModel): + image: Image | None = Field(default=None, alias='Image') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateListenerRuleRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + host_pattern: str | None = Field(default=None, alias='HostPattern') + listener_rule_name: str = Field(alias='ListenerRuleName') + path_pattern: str | None = Field(default=None, alias='PathPattern') + +class UpdateListenerRuleResponse(GeneratedModel): + listener_rule: ListenerRule | None = Field(default=None, alias='ListenerRule') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateLoadBalancerRequest(GeneratedModel): + access_log: AccessLog | None = Field(default=None, alias='AccessLog') + dry_run: bool | None = Field(default=None, alias='DryRun') + health_check: HealthCheck | None = Field(default=None, alias='HealthCheck') + load_balancer_name: str = Field(alias='LoadBalancerName') + load_balancer_port: int | None = Field(default=None, alias='LoadBalancerPort') + policy_names: list[str] | None = Field(default=None, alias='PolicyNames') + public_ip: str | None = Field(default=None, alias='PublicIp') + secured_cookies: bool | None = Field(default=None, alias='SecuredCookies') + security_groups: list[str] | None = Field(default=None, alias='SecurityGroups') + server_certificate_id: str | None = Field(default=None, alias='ServerCertificateId') + +class UpdateLoadBalancerResponse(GeneratedModel): + load_balancer: LoadBalancer | None = Field(default=None, alias='LoadBalancer') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateNetAccessPointRequest(GeneratedModel): + add_route_table_ids: list[str] | None = Field(default=None, alias='AddRouteTableIds') + dry_run: bool | None = Field(default=None, alias='DryRun') + net_access_point_id: str = Field(alias='NetAccessPointId') + remove_route_table_ids: list[str] | None = Field(default=None, alias='RemoveRouteTableIds') + +class UpdateNetAccessPointResponse(GeneratedModel): + net_access_point: NetAccessPoint | None = Field(default=None, alias='NetAccessPoint') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateNetRequest(GeneratedModel): + dhcp_options_set_id: str = Field(alias='DhcpOptionsSetId') + dry_run: bool | None = Field(default=None, alias='DryRun') + net_id: str = Field(alias='NetId') + +class UpdateNetResponse(GeneratedModel): + net: Net | None = Field(default=None, alias='Net') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateNicRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + link_nic: LinkNicToUpdate | None = Field(default=None, alias='LinkNic') + nic_id: str = Field(alias='NicId') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + +class UpdateNicResponse(GeneratedModel): + nic: Nic | None = Field(default=None, alias='Nic') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateRoutePropagationRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + enable: bool = Field(alias='Enable') + route_table_id: str = Field(alias='RouteTableId') + virtual_gateway_id: str = Field(alias='VirtualGatewayId') + +class UpdateRoutePropagationResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + route_table: RouteTable | None = Field(default=None, alias='RouteTable') + +class UpdateRouteRequest(GeneratedModel): + destination_ip_range: str = Field(alias='DestinationIpRange') + dry_run: bool | None = Field(default=None, alias='DryRun') + gateway_id: str | None = Field(default=None, alias='GatewayId') + nat_service_id: str | None = Field(default=None, alias='NatServiceId') + net_peering_id: str | None = Field(default=None, alias='NetPeeringId') + nic_id: str | None = Field(default=None, alias='NicId') + route_table_id: str = Field(alias='RouteTableId') + vm_id: str | None = Field(default=None, alias='VmId') + +class UpdateRouteResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + route_table: RouteTable | None = Field(default=None, alias='RouteTable') + +class UpdateRouteTableLinkRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + link_route_table_id: str = Field(alias='LinkRouteTableId') + route_table_id: str = Field(alias='RouteTableId') + +class UpdateRouteTableLinkResponse(GeneratedModel): + link_route_table_id: str | None = Field(default=None, alias='LinkRouteTableId') + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + +class UpdateServerCertificateRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + name: str = Field(alias='Name') + new_name: str | None = Field(default=None, alias='NewName') + new_path: str | None = Field(default=None, alias='NewPath') + +class UpdateServerCertificateResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + server_certificate: ServerCertificate | None = Field(default=None, alias='ServerCertificate') + +class UpdateSnapshotRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + permissions_to_create_volume: PermissionsOnResourceCreation = Field(alias='PermissionsToCreateVolume') + snapshot_id: str = Field(alias='SnapshotId') + +class UpdateSnapshotResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + snapshot: Snapshot | None = Field(default=None, alias='Snapshot') + +class UpdateSubnetRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + map_public_ip_on_launch: bool = Field(alias='MapPublicIpOnLaunch') + subnet_id: str = Field(alias='SubnetId') + +class UpdateSubnetResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + subnet: Subnet | None = Field(default=None, alias='Subnet') + +class UpdateUserGroupRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + new_path: str | None = Field(default=None, alias='NewPath') + new_user_group_name: str | None = Field(default=None, alias='NewUserGroupName') + path: str | None = Field(default=None, alias='Path') + user_group_name: str = Field(alias='UserGroupName') + +class UpdateUserGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user_group: UserGroup | None = Field(default=None, alias='UserGroup') + users: list[User] | None = Field(default=None, alias='Users') + +class UpdateUserRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + new_path: str | None = Field(default=None, alias='NewPath') + new_user_email: str | None = Field(default=None, alias='NewUserEmail') + new_user_name: str | None = Field(default=None, alias='NewUserName') + user_name: str = Field(alias='UserName') + +class UpdateUserResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + user: User | None = Field(default=None, alias='User') + +class UpdateVmGroupRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_group_id: str = Field(alias='VmGroupId') + vm_group_name: str | None = Field(default=None, alias='VmGroupName') + vm_template_id: str | None = Field(default=None, alias='VmTemplateId') + +class UpdateVmGroupResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_group: VmGroup | None = Field(default=None, alias='VmGroup') + +class UpdateVmRequest(GeneratedModel): + actions_on_next_boot: ActionsOnNextBoot | None = Field(default=None, alias='ActionsOnNextBoot') + block_device_mappings: list[BlockDeviceMappingVmUpdate] | None = Field(default=None, alias='BlockDeviceMappings') + bsu_optimized: bool | None = Field(default=None, alias='BsuOptimized') + deletion_protection: bool | None = Field(default=None, alias='DeletionProtection') + dry_run: bool | None = Field(default=None, alias='DryRun') + is_source_dest_checked: bool | None = Field(default=None, alias='IsSourceDestChecked') + keypair_name: str | None = Field(default=None, alias='KeypairName') + nested_virtualization: bool | None = Field(default=None, alias='NestedVirtualization') + performance: Literal['medium', 'high', 'highest'] | None = Field(default=None, alias='Performance') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + shutdown_behavior_configuration: ShutdownBehaviorConfiguration | None = Field(default=None, alias='ShutdownBehaviorConfiguration') + user_data: str | None = Field(default=None, alias='UserData') + vm_id: str = Field(alias='VmId') + vm_initiated_shutdown_behavior: str | None = Field(default=None, alias='VmInitiatedShutdownBehavior') + vm_type: str | None = Field(default=None, alias='VmType') + +class UpdateVmResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm: Vm | None = Field(default=None, alias='Vm') + +class UpdateVmTemplateRequest(GeneratedModel): + description: str | None = Field(default=None, alias='Description') + dry_run: bool | None = Field(default=None, alias='DryRun') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_template_id: str = Field(alias='VmTemplateId') + vm_template_name: str | None = Field(default=None, alias='VmTemplateName') + +class UpdateVmTemplateResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vm_template: VmTemplate | None = Field(default=None, alias='VmTemplate') + +class UpdateVolumeRequest(GeneratedModel): + dry_run: bool | None = Field(default=None, alias='DryRun') + iops: int | None = Field(default=None, alias='Iops') + size: int | None = Field(default=None, alias='Size') + volume_id: str = Field(alias='VolumeId') + volume_type: str | None = Field(default=None, alias='VolumeType') + +class UpdateVolumeResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + volume: Volume | None = Field(default=None, alias='Volume') + +class UpdateVpnConnectionRequest(GeneratedModel): + client_gateway_id: str | None = Field(default=None, alias='ClientGatewayId') + dry_run: bool | None = Field(default=None, alias='DryRun') + virtual_gateway_id: str | None = Field(default=None, alias='VirtualGatewayId') + vpn_connection_id: str = Field(alias='VpnConnectionId') + vpn_options: VpnOptions | None = Field(default=None, alias='VpnOptions') + +class UpdateVpnConnectionResponse(GeneratedModel): + response_context: ResponseContext | None = Field(default=None, alias='ResponseContext') + vpn_connection: VpnConnection | None = Field(default=None, alias='VpnConnection') + +class User(GeneratedModel): + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + last_modification_date: datetime.datetime | None = Field(default=None, alias='LastModificationDate') + outscale_login_allowed: bool | None = Field(default=None, alias='OutscaleLoginAllowed') + path: str | None = Field(default=None, alias='Path') + user_email: str | None = Field(default=None, alias='UserEmail') + user_id: str | None = Field(default=None, alias='UserId') + user_name: str | None = Field(default=None, alias='UserName') + +class UserGroup(GeneratedModel): + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + last_modification_date: datetime.datetime | None = Field(default=None, alias='LastModificationDate') + name: str | None = Field(default=None, alias='Name') + orn: str | None = Field(default=None, alias='Orn') + path: str | None = Field(default=None, alias='Path') + user_group_id: str | None = Field(default=None, alias='UserGroupId') + +class VgwTelemetry(GeneratedModel): + accepted_route_count: int | None = Field(default=None, alias='AcceptedRouteCount') + last_state_change_date: datetime.datetime | None = Field(default=None, alias='LastStateChangeDate') + outside_ip_address: str | None = Field(default=None, alias='OutsideIpAddress') + state: str | None = Field(default=None, alias='State') + state_description: str | None = Field(default=None, alias='StateDescription') + +class VirtualGateway(GeneratedModel): + connection_type: str | None = Field(default=None, alias='ConnectionType') + net_to_virtual_gateway_links: list[NetToVirtualGatewayLink] | None = Field(default=None, alias='NetToVirtualGatewayLinks') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + virtual_gateway_id: str | None = Field(default=None, alias='VirtualGatewayId') + +class Vm(GeneratedModel): + actions_on_next_boot: ActionsOnNextBoot | None = Field(default=None, alias='ActionsOnNextBoot') + architecture: str | None = Field(default=None, alias='Architecture') + block_device_mappings: list[BlockDeviceMappingCreated] | None = Field(default=None, alias='BlockDeviceMappings') + boot_mode: BootMode | None = Field(default=None, alias='BootMode') + bsu_optimized: bool | None = Field(default=None, alias='BsuOptimized') + client_token: str | None = Field(default=None, alias='ClientToken') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + deletion_protection: bool | None = Field(default=None, alias='DeletionProtection') + hypervisor: str | None = Field(default=None, alias='Hypervisor') + image_id: str | None = Field(default=None, alias='ImageId') + is_source_dest_checked: bool | None = Field(default=None, alias='IsSourceDestChecked') + keypair_name: str | None = Field(default=None, alias='KeypairName') + launch_number: int | None = Field(default=None, alias='LaunchNumber') + nested_virtualization: bool | None = Field(default=None, alias='NestedVirtualization') + net_id: str | None = Field(default=None, alias='NetId') + nics: list[NicLight] | None = Field(default=None, alias='Nics') + os_family: str | None = Field(default=None, alias='OsFamily') + performance: str | None = Field(default=None, alias='Performance') + placement: Placement | None = Field(default=None, alias='Placement') + private_dns_name: str | None = Field(default=None, alias='PrivateDnsName') + private_ip: str | None = Field(default=None, alias='PrivateIp') + product_codes: list[str] | None = Field(default=None, alias='ProductCodes') + public_dns_name: str | None = Field(default=None, alias='PublicDnsName') + public_ip: str | None = Field(default=None, alias='PublicIp') + reservation_id: str | None = Field(default=None, alias='ReservationId') + root_device_name: str | None = Field(default=None, alias='RootDeviceName') + root_device_type: str | None = Field(default=None, alias='RootDeviceType') + security_groups: list[SecurityGroupLight] | None = Field(default=None, alias='SecurityGroups') + shutdown_behavior_configuration: ShutdownBehaviorConfiguration | None = Field(default=None, alias='ShutdownBehaviorConfiguration') + state: str | None = Field(default=None, alias='State') + state_reason: str | None = Field(default=None, alias='StateReason') + subnet_id: str | None = Field(default=None, alias='SubnetId') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + tpm_enabled: bool | None = Field(default=None, alias='TpmEnabled') + user_data: str | None = Field(default=None, alias='UserData') + vm_id: str | None = Field(default=None, alias='VmId') + vm_initiated_shutdown_behavior: str | None = Field(default=None, alias='VmInitiatedShutdownBehavior') + vm_type: str | None = Field(default=None, alias='VmType') + +class VmGroup(GeneratedModel): + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + description: str | None = Field(default=None, alias='Description') + positioning_strategy: Literal['attract', 'no-strategy', 'repulse'] | None = Field(default=None, alias='PositioningStrategy') + security_group_ids: list[str] | None = Field(default=None, alias='SecurityGroupIds') + state: Literal['available', 'deleted', 'deleting', 'pending', 'scaling down', 'scaling up'] | None = Field(default=None, alias='State') + subnet_id: str | None = Field(default=None, alias='SubnetId') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_count: int | None = Field(default=None, alias='VmCount') + vm_group_id: str | None = Field(default=None, alias='VmGroupId') + vm_group_name: str | None = Field(default=None, alias='VmGroupName') + vm_ids: list[str] | None = Field(default=None, alias='VmIds') + vm_template_id: str | None = Field(default=None, alias='VmTemplateId') + +class VmState(GeneratedModel): + current_state: str | None = Field(default=None, alias='CurrentState') + previous_state: str | None = Field(default=None, alias='PreviousState') + vm_id: str | None = Field(default=None, alias='VmId') + +class VmStates(GeneratedModel): + maintenance_events: list[MaintenanceEvent] | None = Field(default=None, alias='MaintenanceEvents') + subregion_name: str | None = Field(default=None, alias='SubregionName') + vm_id: str | None = Field(default=None, alias='VmId') + vm_state: str | None = Field(default=None, alias='VmState') + +class VmTemplate(GeneratedModel): + cpu_cores: int = Field(alias='CpuCores') + cpu_generation: str = Field(alias='CpuGeneration') + cpu_performance: Literal['medium', 'high', 'highest'] | None = Field(default=None, alias='CpuPerformance') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + description: str | None = Field(default=None, alias='Description') + image_id: str = Field(alias='ImageId') + keypair_name: str | None = Field(default=None, alias='KeypairName') + ram: int = Field(alias='Ram') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vm_template_id: str = Field(alias='VmTemplateId') + vm_template_name: str = Field(alias='VmTemplateName') + +class VmType(GeneratedModel): + bsu_optimized: bool | None = Field(default=None, alias='BsuOptimized') + ephemerals_type: str | None = Field(default=None, alias='EphemeralsType') + eth: int | None = Field(default=None, alias='Eth') + gpu: int | None = Field(default=None, alias='Gpu') + max_private_ips: int | None = Field(default=None, alias='MaxPrivateIps') + memory_size: float | None = Field(default=None, alias='MemorySize') + vcore_count: int | None = Field(default=None, alias='VcoreCount') + vm_type_name: str | None = Field(default=None, alias='VmTypeName') + volume_count: int | None = Field(default=None, alias='VolumeCount') + volume_size: int | None = Field(default=None, alias='VolumeSize') + +class VmsStopHistory(GeneratedModel): + state_reason: str | None = Field(default=None, alias='StateReason') + stop_date: datetime.datetime | None = Field(default=None, alias='StopDate') + vm_id: str | None = Field(default=None, alias='VmId') + +class Volume(GeneratedModel): + client_token: str | None = Field(default=None, alias='ClientToken') + creation_date: datetime.datetime | None = Field(default=None, alias='CreationDate') + iops: int | None = Field(default=None, alias='Iops') + linked_volumes: list[LinkedVolume] | None = Field(default=None, alias='LinkedVolumes') + size: int | None = Field(default=None, alias='Size') + snapshot_id: str | None = Field(default=None, alias='SnapshotId') + state: str | None = Field(default=None, alias='State') + subregion_name: str | None = Field(default=None, alias='SubregionName') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + task_id: str | None = Field(default=None, alias='TaskId') + volume_id: str | None = Field(default=None, alias='VolumeId') + volume_type: str | None = Field(default=None, alias='VolumeType') + +class VolumeUpdate(GeneratedModel): + origin: VolumeUpdateParameters | None = Field(default=None, alias='Origin') + target: VolumeUpdateParameters | None = Field(default=None, alias='Target') + +class VolumeUpdateParameters(GeneratedModel): + iops: int | None = Field(alias='Iops') + size: int = Field(alias='Size') + volume_type: str = Field(alias='VolumeType') + +class VolumeUpdateTask(GeneratedModel): + comment: str | None = Field(default=None, alias='Comment') + completion_date: datetime.datetime | None = Field(default=None, alias='CompletionDate') + progress: int | None = Field(default=None, alias='Progress') + start_date: datetime.datetime | None = Field(default=None, alias='StartDate') + state: str | None = Field(default=None, alias='State') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + task_id: str | None = Field(default=None, alias='TaskId') + volume_id: str | None = Field(default=None, alias='VolumeId') + volume_update: VolumeUpdate | None = Field(default=None, alias='VolumeUpdate') + +class VpnConnection(GeneratedModel): + client_gateway_configuration: str | None = Field(default=None, alias='ClientGatewayConfiguration') + client_gateway_id: str | None = Field(default=None, alias='ClientGatewayId') + connection_type: str | None = Field(default=None, alias='ConnectionType') + routes: list[RouteLight] | None = Field(default=None, alias='Routes') + state: str | None = Field(default=None, alias='State') + static_routes_only: bool | None = Field(default=None, alias='StaticRoutesOnly') + tags: list[ResourceTag] | None = Field(default=None, alias='Tags') + vgw_telemetries: list[VgwTelemetry] | None = Field(default=None, alias='VgwTelemetries') + virtual_gateway_id: str | None = Field(default=None, alias='VirtualGatewayId') + vpn_connection_id: str | None = Field(default=None, alias='VpnConnectionId') + vpn_options: VpnOptions | None = Field(default=None, alias='VpnOptions') + +class VpnOptions(GeneratedModel): + phase1_options: Phase1Options | None = Field(default=None, alias='Phase1Options') + phase2_options: Phase2Options | None = Field(default=None, alias='Phase2Options') + tunnel_inside_ip_range: str | None = Field(default=None, alias='TunnelInsideIpRange') + +class With(GeneratedModel): + account_id: bool | None = Field(default=None, alias='AccountId') + call_duration: bool | None = Field(default=None, alias='CallDuration') + query_access_key: bool | None = Field(default=None, alias='QueryAccessKey') + query_api_name: bool | None = Field(default=None, alias='QueryApiName') + query_api_version: bool | None = Field(default=None, alias='QueryApiVersion') + query_call_name: bool | None = Field(default=None, alias='QueryCallName') + query_date: bool | None = Field(default=None, alias='QueryDate') + query_header_raw: bool | None = Field(default=None, alias='QueryHeaderRaw') + query_header_size: bool | None = Field(default=None, alias='QueryHeaderSize') + query_ip_address: bool | None = Field(default=None, alias='QueryIpAddress') + query_payload_raw: bool | None = Field(default=None, alias='QueryPayloadRaw') + query_payload_size: bool | None = Field(default=None, alias='QueryPayloadSize') + query_user_agent: bool | None = Field(default=None, alias='QueryUserAgent') + request_id: bool | None = Field(default=None, alias='RequestId') + response_size: bool | None = Field(default=None, alias='ResponseSize') + response_status_code: bool | None = Field(default=None, alias='ResponseStatusCode') diff --git a/osc_sdk_python/limiter.py b/osc_sdk_python/limiter.py deleted file mode 100644 index 91529f4..0000000 --- a/osc_sdk_python/limiter.py +++ /dev/null @@ -1,29 +0,0 @@ -from datetime import datetime, timezone, timedelta -import time - - -class RateLimiter: - def __init__(self, window: timedelta, max_requests: int, datetime_cls=datetime): - self.datetime_cls = datetime_cls - self.window: timedelta = window - self.max_requests: int = max_requests - self.requests = [] - - def acquire(self): - now = self.datetime_cls.now(timezone.utc) - - self.clean_old_requests(now) - - if len(self.requests) >= self.max_requests: - oldest = self.requests[0] - wait_time = self.window - (now - oldest) - time.sleep(wait_time.total_seconds()) - - now = self.datetime_cls.now(timezone.utc) - self.clean_old_requests(now) - - self.requests.append(now) - - def clean_old_requests(self, now): - while len(self.requests) > 0 and self.requests[0] <= now - self.window: - self.requests.pop(0) diff --git a/osc_sdk_python/outscale_gateway.py b/osc_sdk_python/outscale_gateway.py index bed36f3..cb17588 100644 --- a/osc_sdk_python/outscale_gateway.py +++ b/osc_sdk_python/outscale_gateway.py @@ -1,7 +1,34 @@ import os -import sys -from .call import Call -from .limiter import RateLimiter +from .runtime.call import Call, AsyncCall +from .runtime.request import RequestSpec + +# Bootstrap logic for generated mixins. +# This allows the SDK to load even if specific service code isn't generated yet. +try: + from .generated.oks import AsyncOksTypedMixin +except (ImportError, ModuleNotFoundError): + + class AsyncOksTypedMixin: + pass + + +try: + from .generated.osc import AsyncOscTypedMixin +except (ImportError, ModuleNotFoundError): + + class AsyncOscTypedMixin: + pass + +# Replicate this pattern here for future services (e.g., EIM, FCU) +# if they are generated into separate mixins. + +from .runtime.transport import RateLimiter +from .exceptions import ( + SdkConfigurationError, + SdkOperationError, + SdkValidationError, + SdkUsageError, +) import ruamel.yaml from .version import get_version import warnings @@ -9,74 +36,38 @@ type_mapping = {"boolean": "bool", "string": "str", "integer": "int", "array": "list"} -# Logs Output Options -LOG_NONE = 0 -LOG_STDERR = 1 -LOG_STDIO = 2 -LOG_MEMORY = 4 - -# what to Log -LOG_ALL = 0 -LOG_KEEP_ONLY_LAST_REQ = 1 - # Default DEFAULT_LIMITER_WINDOW = timedelta(seconds=1) # 1 second DEFAULT_LIMITER_MAX_REQUESTS = 5 # 5 requests / sec +RESOURCE_DIR = os.path.join(os.path.dirname(__file__), "resources") +OSC_SPEC = os.path.join(RESOURCE_DIR, "osc/api.yaml") +OKS_SPEC = os.path.join(RESOURCE_DIR, "oks/api.yaml") +# Replicate this pattern here for future services (e.g., EIM, FCU) +# if they are generated into separate mixins. -class ActionNotExists(NotImplementedError): +class ActionNotExists(SdkOperationError): pass -class ParameterNotValid(NotImplementedError): +class ParameterNotValid(SdkValidationError): pass -class ParameterIsRequired(NotImplementedError): +class ParameterIsRequired(SdkValidationError): pass -class ParameterHasWrongType(NotImplementedError): +class ParameterHasWrongType(SdkValidationError): pass -class Logger: - string = "" - type = LOG_NONE - what = LOG_ALL - - def config(self, type=None, what=None): - if type is not None: - self.type = type - if what is not None: - self.what = what - - def str(self): - if self.type == LOG_MEMORY: - return self.string - return None - - def do_log(self, s): - if self.type & LOG_MEMORY: - if self.what == LOG_KEEP_ONLY_LAST_REQ: - self.string = s - else: - self.string = self.string + "\n" + s - - if self.type & LOG_STDIO: - print(s) - if self.type & LOG_STDERR: - print(s, file=sys.stderr) - - -class BaseAPI: - def __init__(self, spec, **kwargs): +class OpenAPIActionAPI: + def __init__(self, spec, service="api", *, _call_cls=Call, **kwargs): + self.service = service self._load_gateway_structure(spec) - self._load_errors() - self.log = Logger() self.limiter = RateLimiter(DEFAULT_LIMITER_WINDOW, DEFAULT_LIMITER_MAX_REQUESTS) - self.call = Call( - logger=self.log, + self.call = _call_cls( version=self.endpoint_api_version, limiter=self.limiter, **kwargs, @@ -84,7 +75,7 @@ def __init__(self, spec, **kwargs): def update_credentials(self, **kwargs): warnings.warn( - "update_credentials in deprecated. Use update_profile instead.", + "update_credentials is deprecated. Use update_profile instead.", DeprecationWarning, stacklevel=2, ) @@ -92,13 +83,18 @@ def update_credentials(self, **kwargs): def update_profile(self, **kwargs): """ - destroy and create a new credential map use for each call. - so you can change your ak/sk, region without having to recreate the whole Gateway - as the object is recreate, you can't expect to keep parameter from the old configuration - example: just updating the password, without renter the login will fail + Rebuild the service client profile so credentials, region, and endpoints + can be changed without recreating the parent SDK client. + + Profile updates replace the previous configuration. For example, updating + only the password without also providing the login will fail. """ self.call.update_profile(**kwargs) + @property + def profile(self): + return self.call.profile + def access_key(self): return self.call.profile.access_key @@ -110,7 +106,7 @@ def region(self): def email(self): warnings.warn( - "email in deprecated. Use login instead.", + "email is deprecated. Use login instead.", DeprecationWarning, stacklevel=2, ) @@ -129,7 +125,9 @@ def _convert(self, input_file): yaml = ruamel.yaml.YAML(typ="safe") content = yaml.load(fi.read()) except Exception as err: - print("Problem reading {}:{}".format(input_file, str(err))) + raise SdkConfigurationError( + "Problem reading OpenAPI spec {}: {}".format(input_file, err) + ) from err self.api_version = content["info"]["version"] self.endpoint_api_version = content["servers"][0]["url"].split("/")[-1] for action, params in content["components"]["schemas"].items(): @@ -156,13 +154,6 @@ def _convert(self, input_file): def _load_gateway_structure(self, spec): self.gateway_structure = self._convert(spec) - def _load_errors(self): - dir_path = os.path.join(os.path.dirname(__file__)) - yaml_file = os.path.abspath("{}/resources/gateway_errors.yaml".format(dir_path)) - with open(yaml_file, "r") as yam: - yaml = ruamel.yaml.YAML(typ="safe") - self.gateway_errors = yaml.load(yam.read()) - def _check_parameters_type(self, action_structure, input_structure): for i_param, i_value in input_structure.items(): if ( @@ -211,7 +202,7 @@ def _check_parameters_valid(self, action_name, params): def _check(self, action_name, **params): if action_name not in self.gateway_structure: raise ActionNotExists( - "Action {} does not exists for python sdk: {} with api: {}".format( + "Action {} does not exist for python sdk: {} with api: {}".format( action_name, get_version(), self.api_version ) ) @@ -231,19 +222,21 @@ def _get_action(self, action_name): def action(**kwargs): kwargs = self._remove_none_parameters(**kwargs) self._check(action_name, **kwargs) - result = self.call.api(action_name, **kwargs) + result = self.call.api(action_name, service=self.service, **kwargs) return result return action def __getattr__(self, attr): + if attr not in self.gateway_structure: + raise AttributeError(attr) return self._get_action(attr) def __dir__(self): return self.gateway_structure.keys() def raw(self, action_name, **kwargs): - return self.call.api(action_name, **kwargs) + return self.call.api(action_name, service=self.service, **kwargs) def __enter__(self): return self @@ -251,72 +244,236 @@ def __enter__(self): def __exit__(self, type, value, traceback): self.call.close() + def close(self): + self.call.close() + + +class AsyncOpenAPIActionAPI(OpenAPIActionAPI): + def __init__(self, spec, service="api", **kwargs): + super().__init__(spec, service=service, _call_cls=AsyncCall, **kwargs) + + def _get_action(self, action_name): + async def action(**kwargs): + kwargs = self._remove_none_parameters(**kwargs) + self._check(action_name, **kwargs) + result = await self.call.api(action_name, service=self.service, **kwargs) + return result + + return action + + async def raw(self, action_name, **kwargs): + return await self.call.api(action_name, service=self.service, **kwargs) + + async def __aenter__(self): + return self + + async def __aexit__(self, type, value, traceback): + await self.call.close() + + def __enter__(self): + raise SdkUsageError("AsyncGateway must be used with 'async with'") + + def __exit__(self, type, value, traceback): + return None + + async def close(self): + await self.call.close() + + +class OpenAPIPathAPI: + def __init__(self, spec, service, *, _call_cls=Call, **kwargs): + self.service = service + self.operations = self._load_operations(spec) + self.limiter = RateLimiter(DEFAULT_LIMITER_WINDOW, DEFAULT_LIMITER_MAX_REQUESTS) + self.call = _call_cls(limiter=self.limiter, **kwargs) + + @property + def profile(self): + return self.call.profile + + def _load_operations(self, spec): + with open(spec, "r") as fi: + yaml = ruamel.yaml.YAML(typ="safe") + content = yaml.load(fi.read()) + + self.api_version = content["info"]["version"] + operations = {} + for path, path_item in content.get("paths", {}).items(): + path_parameters = path_item.get("parameters", []) + for method in ["get", "post", "put", "patch", "delete"]: + operation = path_item.get(method) + if operation is None: + continue + + parameters = path_parameters + operation.get("parameters", []) + operation_id = operation.get("operationId") + if operation_id: + operations[operation_id] = { + "method": method.upper(), + "path": path, + "parameters": parameters, + "request_body": operation.get("requestBody"), + } + return operations + + def _build_request(self, operation_name, kwargs): + if operation_name not in self.operations: + raise ActionNotExists( + "Operation {} does not exist for python sdk: {} with api: {}".format( + operation_name, get_version(), self.api_version + ) + ) + + operation = self.operations[operation_name] + kwargs = OpenAPIActionAPI._remove_none_parameters(**kwargs) + path_params = {} + query_params = {} + + for parameter in operation["parameters"]: + name = parameter["name"] + location = parameter["in"] + if location == "path": + if name not in kwargs and parameter.get("required"): + raise ParameterIsRequired("Missing {}.".format(name)) + if name in kwargs: + path_params[name] = kwargs.pop(name) + elif location == "query": + if name in kwargs: + query_params[name] = kwargs.pop(name) + + body = kwargs.pop("body", None) + if operation["request_body"] is not None and body is None: + body = kwargs + kwargs = {} + + if kwargs: + raise ParameterNotValid( + "{}. Available parameters are path/query parameters or body.".format( + ", ".join(kwargs.keys()) + ) + ) + + return RequestSpec( + service=self.service, + method=operation["method"], + path=operation["path"], + json_body=body, + query_params=query_params, + ), path_params + + def _get_operation(self, operation_name): + def operation(**kwargs): + request, path_params = self._build_request(operation_name, kwargs) + return self.call.request(request, path_params=path_params) + + return operation + + def __getattr__(self, attr): + if attr not in self.operations: + raise AttributeError(attr) + return self._get_operation(attr) + + def __dir__(self): + return self.operations.keys() + + def close(self): + self.call.close() + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + +class AsyncOpenAPIPathAPI(OpenAPIPathAPI): + def __init__(self, spec, service, **kwargs): + super().__init__(spec, service, _call_cls=AsyncCall, **kwargs) + + def _get_operation(self, operation_name): + async def operation(**kwargs): + request, path_params = self._build_request(operation_name, kwargs) + return await self.call.request(request, path_params=path_params) + + return operation + + async def close(self): + await self.call.close() + + async def __aenter__(self): + return self + + async def __aexit__(self, type, value, traceback): + await self.close() + + def __enter__(self): + raise SdkUsageError("Async service client must be used with 'async with'") + + def __exit__(self, type, value, traceback): + return None + -class OutscaleGateway(BaseAPI): +class OutscaleGateway(OpenAPIActionAPI): def __init__(self, **kwargs): - super().__init__( - os.path.join(os.path.dirname(__file__), "resources/outscale.yaml"), **kwargs - ) + super().__init__(OSC_SPEC, service="api", **kwargs) -def test(): - a = OutscaleGateway() - a.CreateVms( - ImageId="ami-xx", - BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], - SecurityGroupIds=["sg-aaa", "sg-bbb"], - ) - try: - a.CreateVms( - ImageId="ami-xx", - BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], - SecurityGroupIds=["sg-aaa", "sg-bbb"], - Wrong="wrong", - ) - except ParameterNotValid: - pass - else: - raise AssertionError() - try: - a.CreateVms( - BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], - SecurityGroupIds=["sg-aaa", "sg-bbb"], - ) - except ParameterIsRequired: - pass - else: - raise AssertionError() - try: - a.CreateVms( - ImageId=["ami-xxx"], - BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], - SecurityGroupIds=["sg-aaa", "sg-bbb"], - ) - except ParameterHasWrongType: - pass - else: - raise AssertionError() - try: - a.CreateVms( - ImageId="ami-xxx", - BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], - SecurityGroupIds="wrong", - ) - except ParameterHasWrongType: - pass - else: - raise AssertionError() - try: - a.CreateVms( - ImageId=["ami-wrong"], - BlockDeviceMappings=[{"/dev/sda1": {"Size": 10}}], - SecurityGroupIds="wrong", - ) - except ParameterHasWrongType: - pass - else: - raise AssertionError() +class AsyncOutscaleGateway(AsyncOscTypedMixin, AsyncOpenAPIActionAPI): + def __init__(self, **kwargs): + super().__init__(OSC_SPEC, service="api", **kwargs) + + +class OksGateway(OpenAPIPathAPI): + def __init__(self, **kwargs): + super().__init__(OKS_SPEC, service="oks", **kwargs) + + +class AsyncOksGateway(AsyncOksTypedMixin, AsyncOpenAPIPathAPI): + def __init__(self, **kwargs): + super().__init__(OKS_SPEC, service="oks", **kwargs) + + +# Replicate this pattern here for future services (e.g., EIM, FCU) +# if they are generated into separate mixins. -if __name__ == "__main__": - test() +class Client: + def __init__(self, **kwargs): + self.osc = OutscaleGateway(**kwargs) + self.oks = OksGateway(**kwargs) + # Replicate this pattern here for future services (e.g., EIM, FCU) + # if they are generated into separate mixins. + + def close(self): + self.osc.close() + self.oks.close() + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + +class AsyncClient: + def __init__(self, **kwargs): + self.osc = AsyncOutscaleGateway(**kwargs) + self.oks = AsyncOksGateway(**kwargs) + # Replicate this pattern here for future services (e.g., EIM, FCU) + # if they are generated into separate mixins. + + async def close(self): + await self.osc.close() + await self.oks.close() + + async def __aenter__(self): + return self + + async def __aexit__(self, type, value, traceback): + await self.close() + + def __enter__(self): + raise SdkUsageError("AsyncClient must be used with 'async with'") + + def __exit__(self, type, value, traceback): + return None diff --git a/osc_sdk_python/problem.py b/osc_sdk_python/problem.py index 50ba89e..9389fbe 100644 --- a/osc_sdk_python/problem.py +++ b/osc_sdk_python/problem.py @@ -1,5 +1,7 @@ import json +from .exceptions import SdkHttpError, SdkValidationError + class ProblemDecoder(json.JSONDecoder): def decode(self, s): @@ -17,8 +19,11 @@ def _make_problem(self, data): return Problem(type_, status, title, detail, instance, **data) -class Problem(Exception): +class Problem(SdkHttpError): def __init__(self, type_, status, title, detail, instance, **kwargs): + super().__init__( + title or detail or "API problem", status_code=status, problem=self + ) self._type = type_ or "about:blank" self.status = status self.title = title @@ -28,7 +33,9 @@ def __init__(self, type_, status, title, detail, instance, **kwargs): for k in self.extras: if k in ["type", "status", "title", "detail", "instance"]: - raise ValueError(f"Reserved key '{k}' used in Problem extra arguments.") + raise SdkValidationError( + f"Reserved key '{k}' used in Problem extra arguments." + ) def __str__(self): return self.title @@ -80,8 +87,14 @@ def _make_legacy_problem(self, data): return LegacyProblem(None, error_code, code_type, request_id, None) -class LegacyProblem(Exception): +class LegacyProblem(SdkHttpError): def __init__(self, status, error_code, code_type, request_id, url): + super().__init__( + error_code or "API problem", + status_code=status, + problem=self, + url=url, + ) self.status = status self.error_code = error_code self.code_type = code_type diff --git a/osc_sdk_python/requester.py b/osc_sdk_python/requester.py deleted file mode 100644 index 3e89887..0000000 --- a/osc_sdk_python/requester.py +++ /dev/null @@ -1,29 +0,0 @@ -from .retry import Retry - - -class Requester: - def __init__(self, session, auth, endpoint, **kwargs): - self.session = session - self.auth = auth - self.endpoint = endpoint - self.request_kwargs = kwargs - - def send(self, uri, payload): - headers = None - if self.auth.is_basic_auth_configured(): - headers = self.auth.get_basic_auth_header() - else: - headers = self.auth.forge_headers_signed(uri, payload) - - if self.auth.x509_client_cert is not None: - cert_file = self.auth.x509_client_cert - else: - cert_file = None - - retry_kwargs = self.request_kwargs.copy() - retry_kwargs.update( - {"data": payload, "headers": headers, "verify": True, "cert": cert_file} - ) - - response = Retry(self.session, "post", self.endpoint, **retry_kwargs) - return response.execute().json() diff --git a/osc_sdk_python/resources/gateway_errors.yaml b/osc_sdk_python/resources/gateway_errors.yaml deleted file mode 100644 index 8072a44..0000000 --- a/osc_sdk_python/resources/gateway_errors.yaml +++ /dev/null @@ -1,1220 +0,0 @@ -4000: - Description: '' - Name: access-keys-not-found - Type: InvalidParameterValue -4001: - Description: '' - Name: architecture-mismatch - Type: InvalidParameterValue -4002: - Description: '' - Name: bucket-not-found - Type: InvalidParameterValue -4003: - Description: '' - Name: device-name-not-associated - Type: InvalidParameterValue -4004: - Description: '' - Name: empty-target - Type: InvalidParameterValue -4005: - Description: '' - Name: enable-access-log-not-found - Type: InvalidParameterValue -4006: - Description: '' - Name: gateway-does-not-exist - Type: InvalidParameterValue -4007: - Description: '' - Name: instance-invalid-tenancy - Type: InvalidParameterValue -4008: - Description: '' - Name: invalid-affinity-target - Type: InvalidParameterValue -4009: - Description: '' - Name: invalid-asn - Type: InvalidParameterValue -4010: - Description: '' - Name: invalid-asn-range - Type: InvalidParameterValue -4011: - Description: '' - Name: invalid-bdm - Type: InvalidParameterValue -4012: - Description: '' - Name: invalid-billing-value - Type: InvalidParameterValue -4013: - Description: '' - Name: invalid-block - Type: InvalidParameterValue -4014: - Description: '' - Name: invalid-block-size - Type: InvalidParameterValue -4015: - Description: '' - Name: invalid-cidr-size - Type: InvalidParameterValue -4016: - Description: '' - Name: invalid-class-name - Type: InvalidParameterValue -4017: - Description: '' - Name: invalid-configuration - Type: InvalidParameterValue -4018: - Description: '' - Name: invalid-count - Type: InvalidParameterValue -4019: - Description: '' - Name: invalid-device-name - Type: InvalidParameterValue -4020: - Description: '' - Name: invalid-email-address - Type: InvalidParameterValue -4021: - Description: '' - Name: invalid-end-port - Type: InvalidParameterValue -4022: - Description: '' - Name: invalid-field-value - Type: InvalidParameterValue -4023: - Description: '' - Name: invalid-filter - Type: InvalidParameterValue -4024: - Description: '' - Name: invalid-host-pattern - Type: InvalidParameterValue -4025: - Description: '' - Name: invalid-id - Type: InvalidParameterValue -4026: - Description: '' - Name: invalid-image-name - Type: InvalidParameterValue -4027: - Description: '' - Name: invalid-import-path - Type: InvalidParameterValue -4028: - Description: '' - Name: invalid-instance-port - Type: InvalidParameterValue -4029: - Description: '' - Name: invalid-iops - Type: InvalidParameterValue -4030: - Description: '' - Name: invalid-ip-address - Type: InvalidParameterValue -4031: - Description: '' - Name: invalid-isolation-mode - Type: InvalidParameterValue -4032: - Description: '' - Name: invalid-key - Type: InvalidParameterValue -4033: - Description: '' - Name: invalid-key-size - Type: InvalidParameterValue -4034: - Description: '' - Name: invalid-keypair-index - Type: InvalidParameterValue -4035: - Description: '' - Name: invalid-keypair-name - Type: InvalidParameterValue -4036: - Description: '' - Name: invalid-lb-name - Type: InvalidParameterValue -4037: - Description: '' - Name: invalid-lb-port - Type: InvalidParameterValue -4038: - Description: '' - Name: invalid-lifecycle - Type: InvalidParameterValue -4039: - Description: '' - Name: invalid-lifecycle-role - Type: InvalidParameterValue -4040: - Description: '' - Name: invalid-manifest - Type: InvalidParameterValue -4041: - Description: '' - Name: invalid-manifest-format - Type: InvalidParameterValue -4042: - Description: '' - Name: invalid-method-name - Type: InvalidParameterValue -4043: - Description: '' - Name: invalid-name - Type: InvalidParameterValue -4044: - Description: '' - Name: invalid-notification - Type: InvalidParameterValue -4045: - Description: '' - Name: invalid-parameter - Type: InvalidParameterValue -4046: - Description: '' - Name: invalid-parameter-set - Type: InvalidParameterValue -4047: - Description: '' - Name: invalid-parameter-value - Type: InvalidParameterValue -4048: - Description: '' - Name: invalid-password - Type: InvalidParameterValue -4049: - Description: '' - Name: invalid-path-pattern - Type: InvalidParameterValue -4050: - Description: '' - Name: invalid-permission-format - Type: InvalidParameterValue -4051: - Description: '' - Name: invalid-port-specification - Type: InvalidParameterValue -4052: - Description: '' - Name: invalid-profile - Type: InvalidParameterValue -4053: - Description: '' - Name: invalid-protocol - Type: InvalidParameterValue -4054: - Description: '' - Name: invalid-protocol-layer - Type: InvalidParameterValue -4055: - Description: '' - Name: invalid-quorum - Type: InvalidParameterValue -4056: - Description: '' - Name: invalid-range - Type: InvalidParameterValue -4057: - Description: '' - Name: invalid-root-mapping - Type: InvalidParameterValue -4058: - Description: '' - Name: invalid-rule-action - Type: InvalidParameterValue -4059: - Description: '' - Name: invalid-rule-name - Type: InvalidParameterValue -4060: - Description: '' - Name: invalid-scheme - Type: InvalidParameterValue -4061: - Description: '' - Name: invalid-snapshot-id - Type: InvalidParameterValue -4062: - Description: '' - Name: invalid-source - Type: InvalidParameterValue -4063: - Description: '' - Name: invalid-start-ip - Type: InvalidParameterValue -4064: - Description: '' - Name: invalid-start-port - Type: InvalidParameterValue -4065: - Description: '' - Name: invalid-sticky-policy-cookie_name - Type: InvalidParameterValue -4066: - Description: '' - Name: invalid-sticky-policy-expiration - Type: InvalidParameterValue -4067: - Description: '' - Name: invalid-sticky-policy-name - Type: InvalidParameterValue -4068: - Description: '' - Name: invalid-stop-ip - Type: InvalidParameterValue -4069: - Description: '' - Name: invalid-tag-key - Type: InvalidParameterValue -4070: - Description: '' - Name: invalid-tag-value - Type: InvalidParameterValue -4071: - Description: '' - Name: invalid-tag-value-length - Type: InvalidParameterValue -4072: - Description: '' - Name: invalid-target - Type: InvalidParameterValue -4073: - Description: '' - Name: invalid-type - Type: InvalidParameterValue -4074: - Description: '' - Name: invalid-url - Type: InvalidParameterValue -4075: - Description: '' - Name: invalid-url-format - Type: InvalidParameterValue -4076: - Description: '' - Name: invalid-virtual-name - Type: InvalidParameterValue -4077: - Description: '' - Name: invalid-volume-device-name-association - Type: InvalidParameterValue -4078: - Description: '' - Name: invalid-volume-size - Type: InvalidParameterValue -4079: - Description: '' - Name: invalid-vpn-propagation - Type: InvalidParameterValue -4080: - Description: '' - Name: invalid-vpn-type - Type: InvalidParameterValue -4081: - Description: '' - Name: invalid-zone - Type: InvalidParameterValue -4082: - Description: '' - Name: invalid-zone-owner - Type: InvalidParameterValue -4083: - Description: '' - Name: InvalidKeyPair - Type: InvalidParameterValue -4084: - Description: '' - Name: load-balancer-attribute-not-found - Type: InvalidParameterValue -4085: - Description: '' - Name: multiple-sticky-policies-forbidden - Type: InvalidParameterValue -4086: - Description: '' - Name: network-mismatch - Type: InvalidParameterValue -4087: - Description: '' - Name: not-windows-instance - Type: InvalidParameterValue -4088: - Description: '' - Name: osu-connection-error - Type: InvalidParameterValue -4089: - Description: '' - Name: overlapping-timespan - Type: InvalidParameterValue -4091: - Description: '' - Name: token-mismatch - Type: InvalidParameterValue -4093: - Description: '' - Name: undefined-forwarding - Type: InvalidParameterValue -4094: - Description: '' - Name: unknown-export-version - Type: InvalidParameterValue -4095: - Description: '' - Name: validation-error - Type: InvalidParameterValue -4097: - Description: '' - Name: wrong-certificate-orn - Type: InvalidParameterValue -4099: - Description: '' - Name: wrong-emit-interval - Type: InvalidParameterValue -4100: - Description: '' - Name: wrong-event-type - Type: InvalidParameterValue -4101: - Description: '' - Name: wrong-prefix - Type: InvalidParameterValue -4102: - Description: '' - Name: wrong-protocol-for-ssl - Type: InvalidParameterValue -4103: - Description: '' - Name: wrong-resource-type - Type: InvalidParameterValue -4104: - Description: '' - Name: invalid-id-prefix - Type: InvalidParameterValue -4105: - Description: '' - Name: malformed-id - Type: InvalidParameterValue -4106: - Description: '' - Name: invalid-parameter-value-length - Type: InvalidParameterValue -4108: - Description: '' - Name: invalid-parameter-type - Type: InvalidParameterValue -4109: - Description: '' - Name: invalid-interval-type - Type: InvalidParameterValue -4111: - Description: '' - Name: invalid-parameters-value - Type: InvalidParameterValue -5000: - Description: '' - Name: device-not-in-cluster - Type: InvalidResource -5001: - Description: '' - Name: gateway-does-not-exist - Type: InvalidResource -5002: - Description: '' - Name: ghostly-vm - Type: InvalidResource -5003: - Description: '' - Name: invalid-instance - Type: InvalidResource -5004: - Description: '' - Name: invalid-vpc - Type: InvalidResource -5005: - Description: '' - Name: no-az-for-user - Type: InvalidResource -5006: - Description: '' - Name: no-pz-available - Type: InvalidResource -5007: - Description: '' - Name: no-such-attachment - Type: InvalidResource -5008: - Description: '' - Name: no-such-authorization - Type: InvalidResource -5009: - Description: '' - Name: no-such-az - Type: InvalidResource -5010: - Description: '' - Name: no-such-azmapping - Type: InvalidResource -5011: - Description: '' - Name: no-such-call - Type: InvalidResource -5012: - Description: '' - Name: no-such-certificate - Type: InvalidResource -5013: - Description: '' - Name: no-such-cluster - Type: InvalidResource -5014: - Description: '' - Name: no-such-configuration - Type: InvalidResource -5015: - Description: '' - Name: no-such-customer-gateway - Type: InvalidResource -5016: - Description: '' - Name: no-such-data-file - Type: InvalidResource -5017: - Description: '' - Name: no-such-device - Type: InvalidResource -5018: - Description: '' - Name: no-such-dhcpoptions - Type: InvalidResource -5019: - Description: '' - Name: no-such-gateway - Type: InvalidResource -5020: - Description: '' - Name: no-such-group - Type: InvalidResource -5021: - Description: '' - Name: no-such-group-in-public-cloud - Type: InvalidResource -5022: - Description: '' - Name: no-such-group-in-vpc - Type: InvalidResource -5023: - Description: '' - Name: no-such-image - Type: InvalidResource -5024: - Description: '' - Name: no-such-instance-type - Type: InvalidResource -5025: - Description: '' - Name: no-such-ip - Type: InvalidResource -5026: - Description: '' - Name: no-such-ip-association - Type: InvalidResource -5027: - Description: '' - Name: no-such-key - Type: InvalidResource -5028: - Description: '' - Name: no-such-listener - Type: InvalidResource -5029: - Description: '' - Name: no-such-listener-rule - Type: InvalidResource -5030: - Description: '' - Name: no-such-load-balancer - Type: InvalidResource -5031: - Description: '' - Name: no-such-manifest - Type: InvalidResource -5032: - Description: '' - Name: no-such-nat-gateway - Type: InvalidResource -5033: - Description: '' - Name: no-such-network - Type: InvalidResource -5034: - Description: '' - Name: no-such-network-endpoint - Type: InvalidResource -5035: - Description: '' - Name: no-such-networklink - Type: InvalidResource -5036: - Description: '' - Name: no-such-nic - Type: InvalidResource -5037: - Description: '' - Name: no-such-object - Type: InvalidResource -5038: - Description: '' - Name: no-such-operation - Type: InvalidResource -5039: - Description: '' - Name: no-such-pending-call - Type: InvalidResource -5040: - Description: '' - Name: no-such-prefix-list - Type: InvalidResource -5041: - Description: '' - Name: no-such-pz - Type: InvalidResource -5042: - Description: '' - Name: no-such-quota - Type: InvalidResource -5043: - Description: '' - Name: no-such-region - Type: InvalidResource -5044: - Description: '' - Name: no-such-resource - Type: InvalidResource -5045: - Description: '' - Name: no-such-route - Type: InvalidResource -5046: - Description: '' - Name: no-such-route-table - Type: InvalidResource -5047: - Description: '' - Name: no-such-rtb-assoc - Type: InvalidResource -5048: - Description: '' - Name: no-such-server - Type: InvalidResource -5049: - Description: '' - Name: no-such-server-group - Type: InvalidResource -5050: - Description: '' - Name: no-such-servergroup - Type: InvalidResource -5051: - Description: '' - Name: no-such-shard - Type: InvalidResource -5052: - Description: '' - Name: no-such-site - Type: InvalidResource -5053: - Description: '' - Name: no-such-slot - Type: InvalidResource -5054: - Description: '' - Name: no-such-snapshot - Type: InvalidResource -5055: - Description: '' - Name: no-such-static-route - Type: InvalidResource -5056: - Description: '' - Name: no-such-sticky-policy - Type: InvalidResource -5057: - Description: '' - Name: no-such-subnet - Type: InvalidResource -5058: - Description: '' - Name: no-such-task - Type: InvalidResource -5059: - Description: '' - Name: no-such-type - Type: InvalidResource -5060: - Description: '' - Name: no-such-user-group - Type: InvalidResource -5061: - Description: '' - Name: no-such-vlan-pool - Type: InvalidResource -5062: - Description: '' - Name: no-such-vlan-range - Type: InvalidResource -5063: - Description: '' - Name: no-such-vm - Type: InvalidResource -5064: - Description: '' - Name: no-such-volume - Type: InvalidResource -5065: - Description: '' - Name: no-such-vpc - Type: InvalidResource -5066: - Description: '' - Name: no-such-vpn-attachment - Type: InvalidResource -5067: - Description: '' - Name: no-such-vpn-connection - Type: InvalidResource -5068: - Description: '' - Name: no-such-vpn-gateway - Type: InvalidResource -5069: - Description: '' - Name: zero-candidate - Type: InvalidResource -5070: - Description: '' - Name: wrong-ssl-certificate - Type: InvalidResource -5071: - Description: '' - Name: no-such-keypair - Type: InvalidResource -5072: - Description: '' - Name: no-such-connection - Type: InvalidResource -5073: - Description: '' - Name: no-such-interface - Type: InvalidResource -5074: - Description: '' - Name: no-such-gpu-reservation - Type: InvalidResource -5075: - Description: '' - Name: no-such-user - Type: InvalidResource -6000: - Description: '' - Name: invalid-cg-state - Type: InvalidState -6001: - Description: '' - Name: invalid-image-state - Type: InvalidState -6002: - Description: '' - Name: invalid-router-state - Type: InvalidState -6003: - Description: '' - Name: invalid-state - Type: InvalidState -6004: - Description: '' - Name: invalid-state-transition (can be match with invalid-state ?) - Type: InvalidState -6005: - Description: '' - Name: invalid-subnet-state - Type: InvalidState -6006: - Description: '' - Name: invalid-vm-state - Type: InvalidState -6007: - Description: '' - Name: invalid-volume-state - Type: InvalidState -6008: - Description: '' - Name: invalid-vpg-state - Type: InvalidState -6009: - Description: '' - Name: invalid-vpn-connection-state - Type: InvalidState -9000: - Description: '' - Name: 'already-exist ' - Type: ResourceConflict -9001: - Description: '' - Name: ambiguous-ip - Type: ResourceConflict -9002: - Description: '' - Name: busy - Type: ResourceConflict -9003: - Description: '' - Name: device-conflict - Type: ResourceConflict -9004: - Description: '' - Name: device-in-use - Type: ResourceConflict -9005: - Description: '' - Name: duplicate-cidr - Type: ResourceConflict -9006: - Description: '' - Name: duplicate-connection - Type: ResourceConflict -9007: - Description: '' - Name: duplicate-email-address - Type: ResourceConflict -9008: - Description: '' - Name: duplicate-group - Type: ResourceConflict -9009: - Description: '' - Name: duplicate-id - Type: ResourceConflict -9010: - Description: '' - Name: duplicate-interface - Type: ResourceConflict -9011: - Description: '' - Name: duplicate-key - Type: ResourceConflict -9012: - Description: '' - Name: duplicate-listener - Type: ResourceConflict -9013: - Description: '' - Name: duplicate-loadbalancer-name - Type: ResourceConflict -9014: - Description: '' - Name: duplicate-mount-point - Type: ResourceConflict -9015: - Description: '' - Name: duplicate-name - Type: ResourceConflict -9016: - Description: '' - Name: duplicate-port - Type: ResourceConflict -9017: - Description: '' - Name: duplicate-prefix-list - Type: ResourceConflict -9018: - Description: '' - Name: duplicate-product - Type: ResourceConflict -9019: - Description: '' - Name: duplicate-router - Type: ResourceConflict -9020: - Description: '' - Name: duplicate-shard - Type: ResourceConflict -9021: - Description: '' - Name: duplicate-sticky-policy-name - Type: ResourceConflict -9022: - Description: '' - Name: duplicate-username - Type: ResourceConflict -9023: - Description: '' - Name: duplicate-vlan - Type: ResourceConflict -9024: - Description: '' - Name: gateway-is-already-attached - Type: ResourceConflict -9025: - Description: '' - Name: gateway-is-already-attached - Type: ResourceConflict -9026: - Description: '' - Name: gateway-is-not-attached - Type: ResourceConflict -9027: - Description: '' - Name: gateway-not-attached - Type: ResourceConflict -9028: - Description: '' - Name: group-already-exists - Type: ResourceConflict -9030: - Description: '' - Name: invalid-vpn-connection - Type: ResourceConflict -9031: - Description: '' - Name: locked-address - Type: ResourceConflict -9033: - Description: '' - Name: not-migrating - Type: ResourceConflict -9034: - Description: '' - Name: multiple-app-sticky-policies-forbidden - Type: ResourceConflict -9035: - Description: '' - Name: multiple-lb-sticky-policies-forbidden - Type: ResourceConflict -9036: - Description: '' - Name: multiple-sticky-policies-forbidden - Type: ResourceConflict -9037: - Description: '' - Name: nat-gateway-already-exists - Type: ResourceConflict -9038: - Description: '' - Name: network-endpoint-already-exists - Type: ResourceConflict -9039: - Description: '' - Name: never-started - Type: ResourceConflict -9041: - Description: '' - Name: no-generated-password - Type: ResourceConflict -9042: - Description: '' - Name: no-keypair-associated - Type: ResourceConflict -9043: - Description: '' - Name: no-vpn-network - Type: ResourceConflict -9044: - Description: '' - Name: not-in-vpc-block - Type: ResourceConflict -9045: - Description: '' - Name: not-public-subnet - Type: ResourceConflict -9046: - Description: '' - Name: password-already-set - Type: ResourceConflict -9047: - Description: '' - Name: priority-already-in-use - Type: ResourceConflict -9048: - Description: '' - Name: pz-already-mapped - Type: ResourceConflict -9049: - Description: '' - Name: pz-not-enabled - Type: ResourceConflict -9050: - Description: '' - Name: reserved-block - Type: ResourceConflict -9051: - Description: '' - Name: reserved-group - Type: ResourceConflict -9052: - Description: '' - Name: route-already-exists - Type: ResourceConflict -9053: - Description: '' - Name: route-does-not-exist - Type: ResourceConflict -9054: - Description: '' - Name: rule-name-already-in-use - Type: ResourceConflict -9055: - Description: '' - Name: sticky-policy-enabled-with-listeners - Type: ResourceConflict -9056: - Description: '' - Name: sticky-policy-only-with-http-https - Type: ResourceConflict -9057: - Description: '' - Name: subnet-already-in-use - Type: ResourceConflict -9058: - Description: '' - Name: subnet-conflict - Type: ResourceConflict -9059: - Description: '' - Name: subnet-is-associated - Type: ResourceConflict -9060: - Description: '' - Name: 'tag-already-exist ' - Type: ResourceConflict -9061: - Description: '' - Name: vm-already-streaming - Type: ResourceConflict -9062: - Description: '' - Name: vm-not-in-vpc - Type: ResourceConflict -9063: - Description: '' - Name: volume-not-in-use - Type: ResourceConflict -9064: - Description: '' - Name: volume-not-streamable - Type: ResourceConflict -9065: - Description: '' - Name: vpc-already-attached - Type: ResourceConflict -9066: - Description: '' - Name: vpc-is-associated - Type: ResourceConflict -9067: - Description: '' - Name: vpc-peering-connection-already-exists - Type: ResourceConflict -9068: - Description: '' - Name: vpn-connection-is-not-static - Type: ResourceConflict -9069: - Description: '' - Name: vpnc-conflict - Type: ResourceConflict -9070: - Description: '' - Name: zone-already-exist - Type: ResourceConflict -9071: - Description: '' - Name: zone-mismatch - Type: ResourceConflict -9072: - Description: '' - Name: vm-specs-mismatch - Type: ResourceConflict -10003: - Description: '' - Name: migration-ports-exhausted - Type: TooManyResources (QuotaExceded) -10004: - Description: '' - Name: resolution-has-too-many-sgs - Type: TooManyResources (QuotaExceded) -10005: - Description: '' - Name: too-many-accesskeys - Type: TooManyResources (QuotaExceded) -10006: - Description: '' - Name: too-many-accounts-created - Type: TooManyResources (QuotaExceded) -10007: - Description: '' - Name: too-many-certificate - Type: TooManyResources (QuotaExceded) -10008: - Description: '' - Name: too-many-concurrent-snapshots - Type: TooManyResources (QuotaExceded) -10009: - Description: '' - Name: too-many-cookie-param-set - Type: TooManyResources (QuotaExceded) -10010: - Description: '' - Name: too-many-instances - Type: TooManyResources (QuotaExceded) -10011: - Description: '' - Name: too-many-ips - Type: TooManyResources (QuotaExceded) -10012: - Description: '' - Name: too-many-listener-rules - Type: TooManyResources (QuotaExceded) -10013: - Description: '' - Name: too-many-listeners - Type: TooManyResources (QuotaExceded) -10014: - Description: '' - Name: too-many-load-balancers - Type: TooManyResources (QuotaExceded) -10015: - Description: '' - Name: too-many-network-interfaces - Type: TooManyResources (QuotaExceded) -10016: - Description: '' - Name: too-many-pz - Type: TooManyResources (QuotaExceded) -10017: - Description: '' - Name: too-many-sg-rules - Type: TooManyResources (QuotaExceded) -10018: - Description: '' - Name: too-many-volumes - Type: TooManyResources (QuotaExceded) -10019: - Description: '' - Name: too-much-pz - Type: TooManyResources (QuotaExceded) -10020: - Description: '' - Name: vpn-gateway-attachment-limit-reached - Type: TooManyResources (QuotaExceded) -10021: - Description: '' - Name: too-many-private-ips - Type: TooManyResources (QuotaExceded) -10022: - Description: '' - Name: too-many-vpcs - Type: TooManyResources (QuotaExceded) -10023: - Description: '' - Name: too-many-igws - Type: TooManyResources (QuotaExceded) -10024: - Description: '' - Name: too-much-volume-size - Type: TooManyResources (QuotaExceded) -10025: - Description: '' - Name: too-many-iops - Type: TooManyResources (QuotaExceded) -10026: - Description: '' - Name: too-many-snapshots - Type: TooManyResources (QuotaExceded) -10027: - Description: '' - Name: too-many-cgws - Type: TooManyResources (QuotaExceded) -10028: - Description: '' - Name: too-many-connections - Type: TooManyResources (QuotaExceded) -10029: - Description: '' - Name: too-many-cores - Type: TooManyResources (QuotaExceded) -10030: - Description: '' - Name: too-many-gpu - Type: TooManyResources (QuotaExceded) -10031: - Description: '' - Name: too-many-interfaces - Type: TooManyResources (QuotaExceded) -10032: - Description: '' - Name: too-many-nat-gateways - Type: TooManyResources (QuotaExceded) -10033: - Description: '' - Name: too-many-network-endpoints - Type: TooManyResources (QuotaExceded) -10034: - Description: '' - Name: too-many-networklink-requests - Type: TooManyResources (QuotaExceded) -10035: - Description: '' - Name: too-many-networklinks - Type: TooManyResources (QuotaExceded) -10036: - Description: '' - Name: too-many-nic-sgs - Type: TooManyResources (QuotaExceded) -10037: - Description: '' - Name: too-many-rtbs - Type: TooManyResources (QuotaExceded) -10038: - Description: '' - Name: too-many-sgs - Type: TooManyResources (QuotaExceded) -10040: - Description: '' - Name: too-many-static-routes - Type: TooManyResources (QuotaExceded) -10041: - Description: '' - Name: too-many-tags - Type: TooManyResources (QuotaExceded) -10042: - Description: '' - Name: too-much-memory - Type: TooManyResources (QuotaExceded) -10043: - Description: '' - Name: too-many-bgp-routes - Type: TooManyResources (QuotaExceded) -10044: - Description: '' - Name: too-many-bypass-group - Type: TooManyResources (QuotaExceded) -10045: - Description: '' - Name: too-much-bypass-group-size - Type: TooManyResources (QuotaExceded) -10046: - Description: '' - Name: too-many-gpu-reservations - Type: TooManyResources (QuotaExceded) diff --git a/osc_sdk_python/resources/oks/api.yaml b/osc_sdk_python/resources/oks/api.yaml new file mode 100644 index 0000000..61d58a7 --- /dev/null +++ b/osc_sdk_python/resources/oks/api.yaml @@ -0,0 +1,5273 @@ +openapi: 3.1.0 +info: + title: OKS API + description: |- + The OKS API enables you to interact with OUTSCALE Kubernetes as a Service (OKS), a managed Kubernetes service on the OUTSCALE Cloud. + + In OKS, each cluster is linked to a project. In a project, you can have several clusters. Templates are also available for different resources.
For more information about the service, see [About OKS](https://docs.outscale.com/en/userguide/About-OKS.html). + + An OpenAPI description of this API is also available for download: + version: '1.0' +paths: + /projects: + post: + tags: + - Projects + summary: Create Project + description: Creates a new project. + operationId: CreateProject + parameters: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectInput' + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectResponse' + '403': + description: The HTTP 403 response (Forbidden). + content: + application/json: + example: + Errors: + - Type: ForbiddenError + Details: You cannot have more than 2 projects. + Code: '403' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: Project with this name already exists. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: 'Invalid CIDR: ''10.50.15.24/32'' must have a prefix length between /16 and /23.' + Code: '422' + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Failed to create project + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + get: + tags: + - Projects + summary: Get Projects + description: Lists one or more of your projects. The response can be filtered using the parameters. + operationId: ListProjects + parameters: + - name: name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Name + description: The name of the projects. + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + description: The status of the projects (`pending` | `ready` | `updating` | `failed` | `deleting`). + - name: cidr + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Cidr + description: The IP ranges for the projects, IN CIDR notation (for example, `192.0.2.0/16`). + - name: deleted + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Deleted + description: If true, returns deleted projects. + - name: cursor + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Cursor + description: The token to indicate where the next set of results should start. + - name: page + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Page + description: The page number of results to retrieve. + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + maximum: 100 + minimum: 1 + - type: 'null' + title: Limit + description: The maximum number of results to return in the response. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectResponseList' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Internal server error + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}: + get: + tags: + - Projects + summary: Get Project + description: Gets information about a specific project. + operationId: GetProject + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid project data format + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Internal server error + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + patch: + tags: + - Projects + summary: Update Project + description: Updates the details of an existing project. + operationId: UpdateProject + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectUpdate' + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Failed to update project + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: pending' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + delete: + tags: + - Projects + summary: Delete Project + description: Deletes a specific project. + operationId: DeleteProject + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/DetailResponse' + '403': + description: The HTTP 403 response (Forbidden). + content: + application/json: + example: + Errors: + - Type: ForbiddenError + Details: Project $uuid can't be deleted because disable_api_termination is enabled + Code: '403' + - Type: ForbiddenError + Details: 'The requested action cannot be performed because the project has 1 active cluster(s): test' + Code: '403' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: pending' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/quotas: + get: + tags: + - Projects + summary: Get Project Quotas + description: Gets the quota details for a specific project. + operationId: GetProjectQuotas + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/projects__project_schema__QuotasResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/snapshots: + get: + tags: + - Projects + summary: Get Project Snapshots + description: Retrieves the snapshot details for a specific project by its ID. Returns the snapshot information associated with the specified project. + operationId: GetProjectSnapshots + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/SnapshotsResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/public_ips: + get: + tags: + - Projects + summary: Get Project Public Ips + description: Retrieves the Public IP details for a specific project by its ID. Returns the public IP information associated with the specified project. + operationId: GetProjectPublicIps + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/PublicIpsResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/nets: + get: + tags: + - Projects + summary: Get Project Nets + description: Retrieves the Net details for a specific project by its ID. Returns the Net information associated with the specified project. + operationId: GetProjectNets + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/NetsResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/eim_users: + get: + tags: + - Projects + summary: Get Eim Users + description: Gets information about EIM users. + operationId: GetEimUsers + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/EimUsersResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Failed to retreive users. + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + post: + tags: + - Projects + summary: Create Eim User + description: Creates an EIM user. + operationId: CreateEimUser + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + - name: user + in: query + required: true + schema: + type: string + title: User + description: The name of the EIM user. + - name: ttl + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Ttl + description: The TTL (time-to-live) for the kubeconfig certificate. + - name: x-encrypt-nacl + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Encrypt-Nacl + description: The header to encrypt the kubeconfig file. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/EimUserResponse' + - $ref: '#/components/schemas/EnryptedResponse' + title: Response Createeimuser + description: The HTTP 200 response (OK). + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Unsupported user type. + Code: '400' + - Type: InvalidResource + Details: Failed to create requested user. + Code: '400' + - Type: InvalidResource + Details: Failed to link policy. + Code: '400' + - Type: InvalidResource + Details: Failed to generate AccessKey. + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The user 'user' already exist. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: pending' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/eim_users/types: + get: + tags: + - Projects + summary: Get Eim Users Types + description: |- + Gets information about EIM user types.

+ + **[IMPORTANT]**
+ In the API, the term `user type` corresponds to the `user role` described in the User Guide. For more information, see [Creating an EIM User Using OKS](Managing-EIM-Users-Using-OKS.html). + operationId: GetEimUserTypes + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/EimUserTypesResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Failed to retreive users. + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /projects/{project_id}/eim_users/{user}: + delete: + tags: + - Projects + summary: Delete Eim User + description: Deletes a specific EIM user. + operationId: DeleteEimUser + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + description: The ID of the project. + - name: user + in: path + required: true + schema: + type: string + title: User + description: The name of the EIM user. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/DetailsResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Unsupported user type. + Code: '400' + - Type: InvalidResource + Details: Failed to delete user. + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: pending' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters: + post: + tags: + - Clusters + summary: Create Cluster + description: Creates a cluster with the provided configuration. + operationId: CreateCluster + parameters: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterInput' + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: The 'version' field is required. + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: The HTTP 403 response (Forbidden). + content: + application/json: + example: + Errors: + - Type: ForbiddenError + Details: You cannot have more than 2 clusters. + Code: '403' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Project $uuid not found. + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The name 'default' cannot be used because it is a reserved keyword. + Code: '409' + - Type: ResourceConflict + Details: Cluster with this name already exists. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + - Type: ResourceConflict + Details: |- + Network overlap detected between source1 (net1) and source2 (net2). + Please use non-overlapping network ranges. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: 'Invalid CIDR: ''cidr'' is not an IPv4 range. Only IPv4 CIDR blocks are supported.' + Code: '422' + - Type: ValidationError + Details: 'Invalid CIDR: ''cidr'' is not a private IP range as defined by RFC1918.' + Code: '422' + - Type: ValidationError + Details: 'Invalid CIDR: ''cidr'' has a prefix length of /network.prefixlen. The minimal accepted prefix length is /23' + Code: '422' + - Type: ValidationError + Details: 'Invalid CIDR: ''cidr'' is not allowed as it belongs to the restricted subnet 172.31.0.0/16.' + Code: '422' + - Type: ValidationError + Details: 'Invalid CIDR: ''cidr'' is not a valid CIDR block.' + Code: '422' + - Type: ValidationError + Details: Failed to create cluster. cluster_dns is not part of cidr_service + Code: '422' + - Type: ValidationError + Details: 'Private RFC1918 IPs are not allowed: cidr' + Code: '422' + - Type: ValidationError + Details: 'Loopback IPs are not allowed: cidr' + Code: '422' + - Type: ValidationError + Details: 'IPv6 addresses are not allowed: cidr' + Code: '422' + - Type: ValidationError + Details: 'Invalid network address: cidr' + Code: '422' + - Type: ValidationError + Details: 'Invalid cidr in the admin_whitelist: cidr' + Code: '422' + - Type: ValidationError + Details: 'Invalid plugin(s): list of invalid_plugins. Allowed plugins to enable/disable are: list of allowed_plugins' + Code: '422' + - Type: ValidationError + Details: 'Failed to create cluster. Control plane plan ''control_planes'' does not exist. Valid plans are: list of valid_plans' + Code: '422' + - Type: ValidationError + Details: Multi AZ is not allowed for the requested control_plane size. + Code: '422' + - Type: ValidationError + Details: The requested subregions must be unique. + Code: '422' + - Type: ValidationError + Details: 'The requested subregions ''cp_subregions'' cannot be specified for multi AZ configuration (cp_multi_az: ''cp_multi_az''). Multi AZ cluster must have at least 3 subregions.' + Code: '422' + - Type: ValidationError + Details: 'The requested subregions ''cp_subregions'' cannot be specified for the mono AZ configuration (cp_multi_az: ''cp_multi_az'').' + Code: '422' + - Type: ValidationError + Details: The number of requested subregions exceeds the available subregions. + Code: '422' + - Type: ValidationError + Details: The subregion 'subregion' does not exist. + Code: '422' + - Type: ValidationError + Details: This version of Kubernetes is not implemented. + Code: '422' + - Type: ValidationError + Details: Invalid version format. Use 'X.Y' (e.g., '1.30'). + Code: '422' + - Type: ValidationError + Details: Invalid cluster version update. Only +1 minor version allowed. + Code: '422' + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Failed to create cluster. Unable to set default cp_subregion. + Code: '500' + - Type: InternalError + Details: Failed to create cluster. + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '501': + description: The HTTP 501 response (Not Implemented). + content: + application/json: + example: + Errors: + - Type: ResourceNotImplemented + Details: This cp_subregions is not implemented. + Code: '501' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: pending' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + get: + tags: + - Clusters + summary: Get Clusters + description: Lists one or more clusters associated with a project. The response can be filtered using the parameters. + operationId: ListClustersByProjectID + parameters: + - name: project_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Project Id + description: The ID of the project. + - name: name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Name + description: The name of the clusters. + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + description: The status of the clusters. + - name: version + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Version + description: The version of the clusters. + - name: deleted + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Deleted + description: If true, returns deleted clusters. + - name: cursor + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Cursor + description: The token to indicate where the next set of results should start. + - name: page + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Page + description: The page number of results to retrieve. + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + maximum: 100 + minimum: 1 + - type: 'null' + title: Limit + description: The maximum number of results to return in the response. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterResponseList' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid cluster data format + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Internal server error + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/all: + get: + tags: + - Clusters + summary: Get All Clusters + description: Lists one or more of your clusters. The response can be filtered using the parameters. + operationId: ListAllClusters + parameters: + - name: name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Name + description: The name of the clusters. + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + description: The status of the clusters. + - name: version + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Version + description: The version of the clusters. + - name: deleted + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Deleted + description: If true, returns deleted clusters. + - name: cursor + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Cursor + description: The token to indicate where the next set of results should start in cursor-based pagination. + - name: page + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Page + description: The page number of results to retrieve. + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + maximum: 100 + minimum: 1 + - type: 'null' + title: Limit + description: The maximum number of results to return in the response. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterResponseList' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid cluster data format + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Internal server error + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/{cluster_id}: + get: + tags: + - Clusters + summary: Get Cluster + description: Gets information about a specific cluster. + operationId: GetCluster + parameters: + - name: cluster_id + in: path + required: true + schema: + type: string + title: Cluster Id + description: The ID of the cluster. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid cluster data format + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing cluster data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + patch: + tags: + - Clusters + summary: Update Cluster + description: Updates the configuration of an existing cluster. + operationId: UpdateCluster + parameters: + - name: cluster_id + in: path + required: true + schema: + type: string + title: Cluster Id + description: The ID of the cluster. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterUpdate' + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid value for control_planes + Code: '400' + - Type: InvalidResource + Details: Downgrade from a multi-master setup (cp.3) to a single master (cp.mono.master) is not allowed. + Code: '400' + - Type: InvalidResource + Details: The 'version' field is required. + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the cluster has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: 'Invalid plugin(s): list of invalid_plugins. Allowed plugins to enable/disable are: list of allowed_plugins' + Code: '422' + - Type: ValidationError + Details: 'Private RFC1918 IPs are not allowed: cidr' + Code: '422' + - Type: ValidationError + Details: 'Loopback IPs are not allowed: cidr' + Code: '422' + - Type: ValidationError + Details: 'IPv6 addresses are not allowed: cidr' + Code: '422' + - Type: ValidationError + Details: 'Invalid network address: cidr' + Code: '422' + - Type: ValidationError + Details: 'Invalid cidr in the admin_whitelist: cidr' + Code: '422' + - Type: ValidationError + Details: This version of Kubernetes is not implemented. + Code: '422' + - Type: ValidationError + Details: Invalid cluster version update. Only +1 minor version allowed. + Code: '422' + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Failed to update cluster + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: status' + Code: '503' + - Type: ResourceIsNotReady + Details: 'Cluster is not ready: status' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + delete: + tags: + - Clusters + summary: Delete Cluster + description: Deletes a specific cluster. + operationId: DeleteCluster + parameters: + - name: cluster_id + in: path + required: true + schema: + type: string + title: Cluster Id + description: The ID of the cluster. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/DetailResponse' + '403': + description: The HTTP 403 response (Forbidden). + content: + application/json: + example: + Errors: + - Type: ForbiddenError + Details: Cluster $uuid can't be deleted because disable_api_termination is enable + Code: '403' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the cluster has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Internal server error + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: status' + Code: '503' + - Type: ResourceIsNotReady + Details: 'Cluster is not ready: status' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/{cluster_id}/kubeconfig: + get: + tags: + - Clusters + summary: Get Cluster Kubeconfig + description: Gets the kubeconfig file for a specific cluster. You can specify query parameters for the kubeconfig file. + operationId: GetKubeconfig + parameters: + - name: cluster_id + in: path + required: true + schema: + type: string + title: Cluster Id + description: The ID of the cluster. + - name: user + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: User + description: The user of the kubeconfig file. + - name: group + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Group + description: The group of the kubeconfig file. + - name: ttl + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Ttl + description: The time to live (TTL) of the kubeconfig file. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/KubeconfigResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid cluster data format + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the cluster has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: 'Invalid user ''user'': Usernames cannot start with ''system:'' or ''oks:'' as these are reserved.' + Code: '422' + - Type: ValidationError + Details: 'Invalid group ''group'': Group names cannot start with ''system:'' or ''oks:'' as these are reserved.' + Code: '422' + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The cluster is currently being created and is not yet available. Please try in few minutes. + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: status' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + post: + tags: + - Clusters + summary: Post Cluster Kubeconfig + description: Gets the kubeconfig file for a specific cluster, optionally encrypted with a NaCl public key. For more information, see the [NaCl website](https://nacl.cr.yp.to/). You can specify query parameters for the kubeconfig file. + operationId: GetKubeconfigWithPubkeyNACL + parameters: + - name: cluster_id + in: path + required: true + schema: + type: string + title: Cluster Id + description: The ID of the cluster. + - name: user + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: User + description: The user of the kubeconfig file. + - name: group + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Group + description: The group of the kubeconfig file. + - name: ttl + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Ttl + description: The time to live (TTL) of the kubeconfig file. + - name: x-encrypt-nacl + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Encrypt-Nacl + description: The header to encrypt the kubeconfig file. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/KubeconfigResponse' + '400': + description: The HTTP 400 response (Bad Request). + content: + application/json: + example: + Errors: + - Type: InvalidResource + Details: Invalid cluster data format + Code: '400' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: The HTTP 408 response (Timeout) + content: + application/json: + example: + Errors: + - Type: TimeoutError + Details: Request Timeout. The server timed out waiting for the request. + Code: '408' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the cluster has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: 'Invalid user ''user'': Usernames cannot start with ''system:'' or ''oks:'' as these are reserved.' + Code: '422' + - Type: ValidationError + Details: 'Invalid group ''group'': Group names cannot start with ''system:'' or ''oks:'' as these are reserved.' + Code: '422' + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Error processing project data + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The cluster is currently being created and is not yet available. Please try in few minutes. + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: status' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/{cluster_id}/upgrade: + patch: + tags: + - Clusters + summary: Upgrade Cluster + description: Upgrades a specific cluster to the latest available version of Kubernetes. For more information, see [GetKubernetesVersions](#getkubernetesversions). + operationId: UpgradeCluster + parameters: + - name: cluster_id + in: path + required: true + schema: + type: string + title: Cluster Id + description: The ID of the cluster. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterResponse' + '404': + description: The HTTP 404 response (Not Found). + content: + application/json: + example: + Errors: + - Type: NotFoundError + Details: Cluster $uuid not found. + Code: '404' + - Type: NotFoundError + Details: Project $uuid not found + Code: '404' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: The HTTP 409 response (Conflict). + content: + application/json: + example: + Errors: + - Type: ResourceConflict + Details: The requested action cannot be performed because the project has been deleted. + Code: '409' + - Type: ResourceConflict + Details: The requested action cannot be performed because the cluster has been deleted. + Code: '409' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '423': + description: The HTTP 423 response (Locked Resource). + content: + application/json: + example: + Errors: + - Type: LockedResource + Details: The project has been under maintenance for 15 mins. Please, try again later + Code: '423' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Failed to upgrade cluster + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '503': + description: The HTTP 503 response (Service Unavailable). + content: + application/json: + example: + Errors: + - Type: ResourceIsNotReady + Details: The service has been under global maintenance for 15 mins. Please, try again later + Code: '503' + - Type: ResourceIsNotReady + Details: 'Project not ready: status' + Code: '503' + - Type: ResourceIsNotReady + Details: 'Cluster is not ready: status' + Code: '503' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/limits/kubernetes_versions: + get: + tags: + - Clusters + summary: Get Kubernetes Versions + description: Gets the available Kubernetes versions for cluster creation or upgrades. + operationId: GetKubernetesVersions + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/KubernetesVersionsResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/limits/cp_subregions: + get: + tags: + - Clusters + summary: Get Cp Subregions + description: Gets the Subregions where you can deploy control planes for your clusters. + operationId: GetCPSubregions + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/CPSubregionsResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/limits/control_plane_plans: + get: + tags: + - Clusters + summary: Get Control Plane Plans + description: Gets the control plane types that you can use to create your clusters. + operationId: GetControlPlanePlans + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/ControlPlanesResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /clusters/limits/admission_plugins: + get: + tags: + - Clusters + summary: Get Admission Plugins + description: Gets the list of admission plugins available for cluster configuration. + operationId: GetAdmissionPlugins + parameters: + - name: version + in: query + required: true + schema: + type: string + title: Version + description: The Kubernetes version to filter admission plugins (e.g. 1.30). + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/AdmissionPluginsResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /templates/project: + get: + tags: + - Templates + summary: Get Project Template + description: Gets the default project template, including the predefined network configurations, Region, and metadata. + operationId: GetProjectTemplate + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateResponse_ProjectInput_' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /templates/cluster: + get: + tags: + - Templates + summary: Get Cluster Template + description: Gets the default cluster template, including the predefined control plane configurations, networking settings, and maintenance schedules. + operationId: GetClusterTemplate + parameters: + - name: X-Real-IP + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Real-Ip + description: A header with the IP of the client making the request. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateResponse_ClusterInputTemplate_' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /templates/nodepool: + get: + tags: + - Templates + summary: Get Nodepool Template + description: Gets the default node pool template, including the predefined configurations for node scaling, storage, and upgrade strategies. + operationId: GetNodepoolTemplate + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateResponse_Nodepool_' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /templates/netpeeringrequest: + get: + tags: + - Templates + summary: Get Netpeering Request Template + description: Gets the default request template used to configure a Net peering, including the predefined fields and values required to create the request. + operationId: GetNetPeeringRequestTemplate + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateResponse_NetPeeringRequest_' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /templates/netpeeringacceptance: + get: + tags: + - Templates + summary: Get Netpeering Acceptance Template + description: Gets the default request template used to accept a Net peering, including the predefined fields and values required to process the acceptance. + operationId: GetNetPeeringAcceptanceTemplate + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateResponse_NetPeeringAcceptance_' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /quotas: + get: + tags: + - Quotas + summary: Get Quotas + description: Get OKS Quotas. + operationId: GetQuotas + parameters: [] + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/quotas__quota_schema__QuotasResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: The HTTP 500 response (Internal Server Error). + content: + application/json: + example: + Errors: + - Type: InternalError + Details: Validation error limits not set + Code: '500' + - Type: InternalError + Details: Validation error limits not set for projects. + Code: '500' + - Type: InternalError + Details: Validation error limits not set for clusters. + Code: '500' + - Type: InternalError + Details: Failed to get quotas + Code: '500' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' + /myip: + get: + tags: + - MyIp + summary: Get Client Ip + description: Gets the IP of the client making the request. + operationId: GetClientIP + parameters: + - name: X-Real-IP + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Real-Ip + description: The IP of the client. + responses: + '200': + description: The HTTP 200 response (OK). + content: + application/json: + schema: + $ref: '#/components/schemas/IPResponse' + '422': + description: The HTTP 422 response (Unprocessable Content). + content: + application/json: + example: + Errors: + - Type: ValidationError + Details: + - loc: + - string + - 0 + msg: string + type: string + Code: '422' + ResponseContext: + RequestId: 45a64090-2e5b-428f-bfea-83a5f783f9e6 + schema: + $ref: '#/components/schemas/ErrorResponse' +components: + schemas: + AccessKey: + properties: + State: + type: string + enum: + - ACTIVE + - INACTIVE + title: State + description: The state of the access key (`ACTIVE` if the key is valid for API calls, or `INACTIVE` if not). + AccessKeyId: + type: string + title: Access Key Id + description: The ID of the access key. + CreationDate: + type: string + title: Creation Date + description: The date and time (UTC) at which the access key was created. + ExpirationDate: + anyOf: + - type: string + - type: 'null' + title: Expiration Date + description: The date and time (UTC) at which the access key expires. + SecretKey: + anyOf: + - type: string + - type: 'null' + title: Secret Key + description: The secret key that enables you to send requests. + type: object + required: + - State + - AccessKeyId + - CreationDate + title: Access Key + description: Information about the access key. + AdmissionFlags: + properties: + disable_admission_plugins: + items: + type: string + type: array + title: Disable Admission Plugins + description: The list of Kubernetes admission plugins that are disabled. + default: [] + enable_admission_plugins: + items: + type: string + type: array + title: Enable Admission Plugins + description: The list of Kubernetes admission plugins that are enabled. + default: [] + applied_admission_plugins: + items: + type: string + type: array + title: Applied Admission Plugins + description: The list of admission plugins that are currently applied to the cluster. + default: + - CertificateApproval + - CertificateSigning + - CertificateSubjectRestriction + - ClusterTrustBundleAttest + - DefaultIngressClass + - DefaultStorageClass + - DefaultTolerationSeconds + - LimitRanger + - MutatingAdmissionWebhook + - NamespaceLifecycle + - PersistentVolumeClaimResize + - PodSecurity + - Priority + - ResourceQuota + - RuntimeClass + - ServiceAccount + - StorageObjectInUseProtection + - TaintNodesByCondition + - ValidatingAdmissionPolicy + - ValidatingAdmissionWebhook + type: object + title: AdmissionFlags + description: Information about the Kubernetes admission plugins configuration. + AdmissionFlagsInput: + properties: + disable_admission_plugins: + items: + type: string + type: array + title: Disable Admission Plugins + description: The list of Kubernetes admission plugins to disable. + enable_admission_plugins: + items: + type: string + type: array + title: Enable Admission Plugins + description: The list of Kubernetes admission plugins to enable. + type: object + title: AdmissionFlagsInput + description: Information about the Kubernetes admission plugins. + AdmissionPlugins: + properties: + EnableAdmissionPlugins: + items: + type: string + type: array + title: Enable Admission Plugins + description: The list of admission plugins that can be enabled. + DisableAdmissionPlugins: + items: + type: string + type: array + title: Disable Admission Plugins + description: The list of admission plugins that can be disabled. + DefaultAdmissionPlugins: + items: + type: string + type: array + title: Default Admission Plugins + description: The list of admission plugins enabled by default. + type: object + required: + - EnableAdmissionPlugins + - DisableAdmissionPlugins + - DefaultAdmissionPlugins + title: AdmissionPlugins + description: Information about the admission plugins configuration. + AdmissionPluginsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + AdmissionPlugins: + $ref: '#/components/schemas/AdmissionPlugins' + description: Information about the admission plugins configuration. + type: object + required: + - ResponseContext + - AdmissionPlugins + title: Admission Plugins Response + AuthStrategy: + properties: + oidc: + anyOf: + - $ref: '#/components/schemas/OpenIdConnectConfig' + - type: 'null' + title: OpenID Connect Authentication + description: The configuration for authenticating to the cluster using OpenID Connect (OIDC). + type: object + title: AuthStrategy + description: Information about the method used by Kubernetes to authenticate API requests. + AutoMaintenances: + properties: + minor_upgrade_maintenance: + allOf: + - $ref: '#/components/schemas/MaintenanceWindow' + title: Minor Upgrade Maintenance + description: The maintenance window configuration for minor Kubernetes upgrades. + patch_upgrade_maintenance: + allOf: + - $ref: '#/components/schemas/MaintenanceWindow' + title: Patch Upgrade Maintenance + description: The maintenance window configuration for patch Kubernetes upgrades. + type: object + required: + - minor_upgrade_maintenance + - patch_upgrade_maintenance + title: AutoMaintenances + description: Information about the automated maintenance windows. + AutoUpgradeMaintenance: + properties: + durationHours: + type: integer + title: Duration Hours + description: The duration of the maintenance window, in hours. + startHour: + type: integer + maximum: 23 + minimum: 0 + title: Start Hour + description: The starting time of the maintenance window, in hours. + weekDay: + type: string + enum: + - Mon + - Tue + - Wed + - Thu + - Fri + - Sat + - Sun + title: Week Day + description: The weekday on which the maintenance window begins. + type: object + required: + - durationHours + - startHour + - weekDay + title: AutoUpgradeMaintenance + description: Information about the window of the automated upgrade maintenance. + CPSubregionsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + CPSubregions: + items: + type: string + type: array + title: CP Subregions + description: The list of Subregions where you can deploy control planes for your clusters. + type: object + required: + - ResponseContext + - CPSubregions + title: CP Subregions Response + Cluster: + properties: + project_id: + type: string + title: Project ID + description: The ID of the project this cluster belongs to. + id: + type: string + title: Cluster ID + description: The Universally Unique Identifier (UUID) of the cluster. + name: + type: string + title: Cluster Name + description: A unique name for the cluster within the project. + description: + type: string + title: Description + description: An optional description of the cluster. + default: '' + cp_multi_az: + type: boolean + title: Control Plane Multi-AZ + description: If true, multi-Subregion deployment is enabled for the control plane. If false, it is disabled. + cp_subregions: + items: + type: string + type: array + title: Control Plane Subregions + description: The Subregions on which the control plane components are deployed. + version: + type: string + title: Kubernetes Version + description: The Kubernetes version deployed for the cluster. For more information, see [GetKubernetesVersions](#getkubernetesversions). + expected_version: + anyOf: + - type: string + - type: 'null' + title: Expected Kubernetes Version + description: The version of Kubernetes that is expected to be deployed during maintenance. + cni: + type: string + title: CNI + description: The Container Network Interface (CNI) used in the cluster. + admin_lbu: + type: boolean + title: Admin LBU + description: If true, load balancer administration is enabled for cluster management. If false, it is disabled. + admission_flags: + allOf: + - $ref: '#/components/schemas/AdmissionFlags' + title: Admission Flags + description: The configuration for Kubernetes admission controllers. + cidr_pods: + type: string + title: Pods CIDR + description: The CIDR block of the Kubernetes pods' network. + cidr_service: + type: string + title: Service CIDR + description: The CIDR block of the Kubernetes services' network. + cluster_dns: + type: string + title: Cluster DNS + description: The IP of the cluster's DNS service. + tags: + additionalProperties: + type: string + type: object + title: Tags + description: The key/value combinations of the tags associated with the cluster, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + example: + key: value + auto_maintenances: + anyOf: + - $ref: '#/components/schemas/AutoMaintenances' + - type: 'null' + title: Auto Maintenances + description: The configuration for automated maintenance windows. + deprecated: true + maintenance_window: + anyOf: + - $ref: '#/components/schemas/Maintenance' + - type: 'null' + title: Auto Maintenances + description: The configuration for automated maintenance windows. + control_planes: + type: string + title: Control Planes + description: The control plane sizing of the cluster. + expected_control_planes: + anyOf: + - type: string + - type: 'null' + title: Expected Control Planes + description: The type of control plane size that is expected. + admin_whitelist: + items: + type: string + type: array + title: Admin Whitelist + description: The list of CIDR blocks or IPs allowed to access the cluster via the Kubernetes API. + statuses: + description: The status information of the cluster. + allOf: + - $ref: '#/components/schemas/Statuses' + disable_api_termination: + type: boolean + title: Disable API Termination + description: If true, cluster deletion through the API is disabled. If false, it is enabled. + default: false + example: false + auth: + anyOf: + - $ref: '#/components/schemas/AuthStrategy' + - type: 'null' + title: Cluster Authentication Configuration + description: The authentication strategy used to access the cluster. + type: object + required: + - project_id + - id + - name + - cp_multi_az + - cp_subregions + - version + - cni + - admin_lbu + - admission_flags + - cidr_pods + - cidr_service + - cluster_dns + - tags + - control_planes + - admin_whitelist + - statuses + title: Cluster + description: Information about the cluster. + ClusterInput: + properties: + name: + type: string + maxLength: 40 + minLength: 1 + pattern: ^[a-z][a-z0-9-]*[a-z0-9]$ + title: Cluster Name + description: A unique name for the cluster within the project. + example: awesome-cluster + project_id: + type: string + title: Project ID + description: The ID of the project in which you want to create a cluster. + description: + anyOf: + - type: string + - type: 'null' + title: Description + description: A description of the cluster. + default: '' + cp_multi_az: + type: boolean + title: Control Plane Multi-AZ + description: If true, multi-Subregion deployment is enabled for the control plane. If false, it is disabled. + cp_subregions: + items: + type: string + type: array + title: Control Plane Subregions + description: The list of Subregions where control plane components are deployed. + default: [] + version: + type: string + title: Kubernetes Version + description: The Kubernetes version to be deployed for the cluster. For more information, see [GetKubernetesVersions](#getkubernetesversions). + admin_lbu: + type: boolean + title: Admin LBU + description: If true, load balancer administration is enabled for cluster management. If false, it is disabled. + default: false + admission_flags: + allOf: + - $ref: '#/components/schemas/AdmissionFlagsInput' + title: Admission Flags + description: The configuration for Kubernetes admission controllers. + default: {} + cni: + anyOf: + - type: string + - type: 'null' + title: CNI + description: The Container Network Interface to use in the cluster + default: cilium + cidr_pods: + type: string + title: Pods CIDR + description: The CIDR block for Kubernetes pods' network. + example: 10.91.0.0/16 + cidr_service: + type: string + title: Service CIDR + description: The CIDR block for the Kubernetes services' network. + example: 10.92.0.0/16 + cluster_dns: + type: string + title: Cluster DNS + description: The IP for the cluster's DNS service. + example: 10.92.0.10 + tags: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Tags + description: The key/value combinations of the tags associated with the cluster's metadata, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + example: + key: value + auto_maintenances: + allOf: + - $ref: '#/components/schemas/AutoMaintenances' + title: Auto Maintenances + description: The configuration for automated maintenance windows. + deprecated: true + maintenance_window: + allOf: + - $ref: '#/components/schemas/Maintenance' + title: Maintenance Window + description: The configuration for automated maintenance windows. + control_planes: + type: string + title: Control Planes + description: The size of control plane deployment for the cluster. For more information, see [About OKS > Control Planes](https://docs.outscale.com/en/userguide/About-OKS.html#_control_planes). + default: cp.3.masters.small + admin_whitelist: + items: + type: string + type: array + title: Admin Whitelist + description: The list of CIDR blocks or IPs allowed to access the cluster via the Kubernetes API. + quirks: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Quirks + description: The list of special configurations or behaviors for the cluster. + disable_api_termination: + type: boolean + title: Disable API Termination + description: If true, cluster deletion through the API is disabled. If false, it is enabled. + default: false + example: false + auth: + anyOf: + - $ref: '#/components/schemas/AuthStrategy' + - type: 'null' + title: Cluster Authentication Configuration + description: The authentication strategy used to access the cluster. + type: object + required: + - name + - project_id + - version + - cidr_pods + - cidr_service + - admin_whitelist + title: ClusterInput + description: Information about the cluster configuration. + ClusterInputTemplate: + properties: + project_id: + type: string + title: Project ID + description: The ID of the project to which this cluster belongs to. + description: + anyOf: + - type: string + - type: 'null' + title: Description + description: The description of the cluster. + default: '' + version: + type: string + title: Kubernetes Version + description: The Kubernetes version deployed for the cluster. For more information, see [GetKubernetesVersions](#getkubernetesversions). + admin_lbu: + type: boolean + title: Admin LBU + description: If true, the admin load balancer for cluster management is enabled. If false, it is disabled. + default: false + admission_flags: + allOf: + - $ref: '#/components/schemas/AdmissionFlagsInput' + title: Admission Flags + description: The configuration for Kubernetes admission controllers. + default: {} + cidr_pods: + type: string + title: Pods CIDR + description: The CIDR block of the Kubernetes pods' network. + example: 10.91.0.0/16 + cidr_service: + type: string + title: Service CIDR + description: The CIDR block of the Kubernetes pods' network. + example: 10.92.0.0/16 + cluster_dns: + type: string + title: Cluster DNS + description: The IP for the cluster DNS service. + example: 10.92.0.10 + tags: + additionalProperties: + type: string + type: object + title: Tags + description: The key/value combinations of the tags associated with the cluster's metadata, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + example: + key: value + auto_maintenances: + anyOf: + - $ref: '#/components/schemas/AutoMaintenances' + - type: 'null' + title: Auto Maintenances + description: The configuration for automated maintenance windows. + deprecated: true + maintenance_window: + anyOf: + - $ref: '#/components/schemas/Maintenance' + - type: 'null' + title: Auto Maintenances + description: The configuration for automated maintenance windows. + control_planes: + type: string + title: Control Planes + description: The control plane type of the cluster. + default: cp.3.masters.small + admin_whitelist: + items: + type: string + type: array + title: Admin Whitelist + description: The list of CIDR blocks or IPs allowed to access the cluster via the Kubernetes API. + quirks: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Quirks + description: A list of special configurations or behaviors for the cluster. + disable_api_termination: + type: boolean + title: Disable API Termination + description: If true, cluster deletion through the API is disabled. If false, it is enabled. + default: false + example: false + type: object + required: + - project_id + - version + - admin_whitelist + title: ClusterInputTemplate + description: Information about the default cluster template. + ClusterResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + Cluster: + $ref: '#/components/schemas/Cluster' + description: Information about the cluster. + type: object + required: + - ResponseContext + - Cluster + title: Cluster Response + ClusterResponseList: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + Pagination: + $ref: '#/components/schemas/Pagination' + description: Information used to split large lists of results into multiple responses (either cursor or offset-based). + Clusters: + items: + $ref: '#/components/schemas/Cluster' + type: array + title: Clusters + description: Information about the clusters. + type: object + required: + - ResponseContext + - Pagination + - Clusters + title: Cluster Response List + description: Information about the clusters associated with a project. + ClusterUpdate: + properties: + description: + anyOf: + - type: string + - type: 'null' + title: Description + description: The updated description of the cluster. + admission_flags: + anyOf: + - $ref: '#/components/schemas/AdmissionFlagsInput' + - type: 'null' + title: Admission Flags + description: The updated configuration for Kubernetes admission controllers. + tags: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Tags + description: The updated key/value combinations of the tags associated with the cluster's metadata, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + auto_maintenances: + anyOf: + - $ref: '#/components/schemas/AutoMaintenances' + - type: 'null' + title: Auto Maintenances + description: The updated configuration for automated maintenance windows. + deprecated: true + maintenance_window: + anyOf: + - $ref: '#/components/schemas/Maintenance' + - type: 'null' + title: Auto Maintenances + description: The updated configuration for automated maintenance windows. + admin_whitelist: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Admin Whitelist + description: The updated list of CIDR blocks or IPs allowed to access the cluster via the Kubernetes API. + quirks: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Quirks + description: The updated list of special configurations or behaviors for the cluster. + disable_api_termination: + anyOf: + - type: boolean + - type: 'null' + title: Disable API Termination + description: If true, cluster deletion through the API is disabled. If false, it is enabled. + version: + anyOf: + - type: string + - type: 'null' + title: Kubernetes Version + description: The updated version of Kubernetes for the cluster. For more information, see [GetKubernetesVersions](#getkubernetesversions). + control_planes: + type: string + title: Control Planes + description: The size of the control plane deployment for the cluster. + auth: + anyOf: + - $ref: '#/components/schemas/AuthStrategy' + - type: 'null' + title: Cluster Authentication Configuration + description: The authentication strategy used to access the cluster. + type: object + title: ClusterUpdate + description: Information about the updated cluster configuration. + ControlPlanesResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + ControlPlanes: + items: + type: string + type: array + title: Control Planes + description: The list of available control plane types. + type: object + required: + - ResponseContext + - ControlPlanes + title: Control Planes Response + Cursor: + properties: + next_cursor: + anyOf: + - type: string + - type: 'null' + title: Next Cursor + description: The pagination token indicating where the next set of results should start. + type: object + title: Cursor + description: Information about the position in the result list for pagination. + DetailResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + detail: + type: string + title: Detail + description: A detailed message related to the API response. + type: object + required: + - ResponseContext + - detail + title: DetailResponse + DetailsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Details: + type: string + title: Details + description: Details about the response. + type: object + required: + - ResponseContext + - Details + title: Details Response + EimUser: + properties: + UserName: + type: string + title: User Name + description: The name of the EIM user. + AccessKeys: + items: + $ref: '#/components/schemas/AccessKey' + type: array + title: Access Keys + description: A list of access keys. + type: object + required: + - UserName + title: EIM User + description: The name of the EIM user. + EimUserResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + EimUser: + $ref: '#/components/schemas/EimUser' + description: The name of the EIM user. + type: object + required: + - ResponseContext + - EimUser + title: EIM User Response + EimUserType: + properties: + UserType: + type: string + title: User Type + description: The type of the EIM user. + Description: + anyOf: + - type: string + - type: 'null' + title: Description + description: The description of the EIM user. + type: object + required: + - UserType + - Description + title: EimUserType + description: The type of EIM user. + EimUserTypesResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + EimUserTypes: + items: + $ref: '#/components/schemas/EimUserType' + type: array + title: Eimusertypes + description: Information about the EIM user types. + type: object + required: + - ResponseContext + - EimUserTypes + title: EIM User Types Response + EimUsersResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + EimUsers: + items: + $ref: '#/components/schemas/EimUser' + type: array + title: EIM User + description: A list of EIM users. + type: object + required: + - ResponseContext + title: EIM Users Response + EnryptedResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Data: + type: string + title: Enrypted data + description: Encrypted data in Base64 format. + type: object + required: + - ResponseContext + - Data + title: Enrypted Response + ErrorItem: + properties: + Type: + type: string + title: Type + description: The type of error. + Details: + anyOf: + - type: string + - items: + $ref: '#/components/schemas/ValidationDetail' + type: array + title: Details + description: Details about the error. + Code: + type: string + title: Code + description: The error code. + type: object + required: + - Type + - Details + - Code + title: ErrorItem + description: Information about the error. + ErrorResponse: + properties: + Errors: + items: + $ref: '#/components/schemas/ErrorItem' + type: array + title: Errors + description: A list of errors. + ResponseContext: + $ref: '#/components/schemas/ResponseContext-Input' + description: Information about the context of the response. + type: object + required: + - Errors + - ResponseContext + title: ErrorResponse + IPDetails: + properties: + x_real_ip: + anyOf: + - type: string + - type: 'null' + title: X-Real-IP + description: The IP associated with the incoming request. + type: object + title: IPDetails + description: Details related to the IP. + IPResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/myip__myip_schema__ResponseContext' + description: Information about the context of the response. + IP: + $ref: '#/components/schemas/IPDetails' + description: Details related to the IP. + type: object + required: + - ResponseContext + - IP + title: IPResponse + KubeconfigData: + properties: + kubeconfig: + type: string + title: Kubeconfig + description: The content of the kubeconfig file used to configure access to the cluster. + type: object + required: + - kubeconfig + title: KubeconfigData + description: Information about the kubeconfig for the cluster. + KubeconfigResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + Cluster: + $ref: '#/components/schemas/clusters__cluster_schema__RPCResponse' + description: The kubeconfig details associated with the cluster. + type: object + required: + - ResponseContext + - Cluster + title: Kubeconfig Response + KubernetesVersionsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/clusters__cluster_schema__ResponseContext' + description: Information about the context of the response. + Versions: + items: + type: string + type: array + title: Versions + description: A list of available Kubernetes versions. + type: object + required: + - ResponseContext + - Versions + title: Kubernetes Versions Response + Maintenance: + properties: + duration_hours: + type: integer + maximum: 23 + minimum: 0 + title: Duration Hours + description: The duration of the maintenance window, in hours. + start_hour: + type: integer + maximum: 23 + minimum: 0 + title: Start Hour + description: The starting time of the maintenance window, in hours. + week_day: + type: string + enum: + - Mon + - Tue + - Wed + - Thu + - Fri + - Sat + - Sun + - string + title: Week Day + description: The weekday on which the maintenance window begins. + tz: + type: string + title: Timezone + description: The timezone for the maintenance window. + default: UTC + type: object + required: + - duration_hours + - start_hour + - week_day + title: Maintenance + description: Information about the maintenance window configuration. + MaintenanceWindow: + properties: + enabled: + type: boolean + title: Enabled + description: If true, a maintenance window is enabled. + default: true + duration_hours: + type: integer + maximum: 23 + minimum: 0 + title: Duration Hours + description: The duration of the maintenance window, in hours. + default: 0 + start_hour: + type: integer + maximum: 23 + minimum: 0 + title: Start Hour + description: The starting time of the maintenance window, in hours. + default: 12 + week_day: + type: string + enum: + - Mon + - Tue + - Wed + - Thu + - Fri + - Sat + - Sun + - string + title: Week Day + description: The weekday on which the maintenance window begins. + default: Tue + tz: + type: string + title: Timezone + description: The timezone for the maintenance window. + default: UTC + type: object + title: MaintenanceWindow + description: Information about the maintenance window configuration. + Net: + properties: + DhcpOptionsSetId: + type: string + title: DHCP Options ID + description: The ID of the DHCP options set. + IpRange: + type: string + title: IP Range + description: The IP range for the Net, in CIDR notation (for example, `10.0.0.0/16`) + NetId: + type: string + title: Net ID + description: The ID of the Net. + State: + type: string + title: State + description: The state of the Net (`pending` | `available` | `deleting`). + Tenancy: + type: string + title: Tenancy + description: The VM tenancy in the Net. + type: object + required: + - DhcpOptionsSetId + - IpRange + - NetId + - State + - Tenancy + title: Net + description: Information about the Net. + NetPeeringAcceptance: + properties: + apiVersion: + type: string + title: API Version + description: The Net peering API version in use. + kind: + type: string + title: Kind + description: The resource type, always `NetPeeringAcceptance`. + metadata: + description: The metadata information for the Net peering. + allOf: + - $ref: '#/components/schemas/netpeerings__netpeering_schema__Metadata' + spec: + allOf: + - $ref: '#/components/schemas/SpecNetPeeringAcceptance' + title: Spec + description: The configuration for accepting the Net peering. + type: object + required: + - apiVersion + - kind + - metadata + - spec + title: NetPeeringAcceptance + description: The custom resource used to accept the Net peering. + NetPeeringRequest: + properties: + apiVersion: + type: string + title: API Version + description: The Net peering API version in use. + kind: + type: string + title: Kind + description: The resource type, always `NetPeeringRequest`. + metadata: + description: The metadata information for the Net peering. + allOf: + - $ref: '#/components/schemas/netpeerings__netpeering_schema__Metadata' + spec: + allOf: + - $ref: '#/components/schemas/SpecNetPeeringRequest' + title: Spec + description: The configuration for the custom resource used to request a Net peering. + type: object + required: + - apiVersion + - kind + - metadata + - spec + title: NetPeeringRequest + NetSpecific: + properties: + disable_lan_security_groups: + type: boolean + title: Disable LAN security groups + description: The value used to modify the firewall behavior within the VPC. + type: object + required: + - disable_lan_security_groups + title: NetSpecific + description: Information about the network-specific configuration. + NetsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Nets: + items: + $ref: '#/components/schemas/Net' + type: array + title: Nets + description: Net details associated with the project. + type: object + required: + - ResponseContext + - Nets + title: NetsResponse + Nodepool: + properties: + apiVersion: + type: string + title: API Version + description: The node pool API version in use. + kind: + type: string + title: Kind + description: The resource type, always `Nodepool` for node pool resources. + metadata: + description: The metadata information for the node pool. + allOf: + - $ref: '#/components/schemas/nodepools__nodepool_schema__Metadata' + spec: + description: The specification for the node pool configuration. + allOf: + - $ref: '#/components/schemas/Spec' + type: object + required: + - apiVersion + - kind + - metadata + - spec + title: Nodepool + description: Information about the node pool. + OKSQuotas: + properties: + Projects: + type: integer + title: Projects + description: The maximum allowed number of projects. + ClustersPerProject: + type: integer + title: Clustersperproject + description: The maximum allowed number of clusters per project. + KubeVersions: + items: + type: string + type: array + title: Max Value + description: The list of available Kubernetes versions. + CPSubregions: + items: + type: string + type: array + title: Cpsubregions + description: The list of available Subregions. + type: object + required: + - Projects + - ClustersPerProject + - KubeVersions + - CPSubregions + title: OKSQuotas + description: Information about your quotas. + Offset: + properties: + page: + anyOf: + - type: integer + - type: 'null' + title: Page + description: The page number of the current results. + limit: + anyOf: + - type: integer + - type: 'null' + title: Limit + description: The maximum number of results returned per page. + total: + anyOf: + - type: integer + - type: 'null' + title: Total + description: The total number of available results. + type: object + title: Offset + description: Information about the current pagination state when using offset-based pagination. + OpenIdConnectConfig: + properties: + issuer-url: + type: string + title: OIDC Issuer URL + description: The server URL of the OIDC provider, using the HTTPS protocol (`https://`). + client-id: + type: string + title: OIDC Client ID + description: The client ID provided by the OIDC provider. This will be used by the cluster to connect to the server. + username-claim: + anyOf: + - type: string + - type: 'null' + title: Username Claim + description: A JSON Web Token (JWT) claim provided by the OIDC provider. This will be used to retrieve the username on the Kubernetes side. + username-prefix: + anyOf: + - type: string + - type: 'null' + title: Username Prefix + description: A prefix added to the Kubernetes username, to prevent clashing with existing names (for example, `oidc:`). + groups-claim: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Groups Claim + description: A JSON Web Token (JWT) claim provided by the OIDC provider. This will be used to retrieve the user's group on the Kubernetes side. + groups-prefix: + anyOf: + - type: string + - type: 'null' + title: Groups Prefix + description: A prefix added to the Kubernetes group, to prevent clashing with existing names (for example, `oidc:`). + required-claim: + anyOf: + - additionalProperties: + anyOf: + - type: string + - type: boolean + - type: integer + - type: number + type: object + - type: 'null' + title: Required OIDC Claims + description: The key/value combination of OIDC claims that must be included in the ID token for the user to authenticate. If not specified, no claim-based restrictions are enforced. + example: + claim: value + type: object + required: + - issuer-url + - client-id + title: OpenIdConnectConfig + description: The configuration parameters to activate the OpenID Connect (OIDC) authentication protocol. + Pagination: + properties: + cursor: + description: Information used for cursor-based pagination. + anyOf: + - $ref: '#/components/schemas/Cursor' + - type: 'null' + offset: + description: Information used for offset-based pagination. + anyOf: + - $ref: '#/components/schemas/Offset' + - type: 'null' + type: object + title: Pagination + description: Information used to split large lists of results into multiple responses (either cursor or offset-based). + PermissionsOnResource: + properties: + GlobalPermission: + type: integer + title: Global Permission + description: A global permission for all accounts. + AccountIds: + items: + type: string + type: array + title: Account IDs + description: One or more account IDs that the permission is associated with. + type: object + required: + - GlobalPermission + - AccountIds + title: PermissionsOnResource + description: Information about the permissions for a resource. + Project: + properties: + id: + type: string + title: Project ID + description: The ID of the project. + name: + type: string + title: Project Name + description: The name of the project. + description: + type: string + title: Description + description: A description for the project. + default: '' + cidr: + type: string + title: VPC CIDR + description: The CIDR block associated with the Net of the project. + region: + type: string + title: Region + description: The Region on which the project is deployed. + status: + type: string + title: Status + description: The status of the project (`pending` | `ready` | `updating` | `failed` | `deleting`). + tags: + additionalProperties: + type: string + type: object + title: Tags + description: The key/value combinations of the tags associated with the resource, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + net_specific: + anyOf: + - $ref: '#/components/schemas/NetSpecific' + - type: 'null' + title: Net Specific + description: The field to configure network-specific behavior for the VPC of the project. + disable_api_termination: + type: boolean + title: Disable API Termination + description: If true, project deletion through the API is disabled. If false, it is enabled. + default: false + example: false + created_at: + type: string + format: date-time + title: Created At + description: The timestamp when the project was created. + updated_at: + type: string + format: date-time + title: Updated At + description: The timestamp when the project was last updated. + deleted_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Deleted At + description: The timestamp when the project was deleted (if applicable). + type: object + required: + - id + - name + - cidr + - region + - status + - tags + - created_at + - updated_at + title: Project + description: Information about the project. + ProjectInput: + properties: + name: + type: string + maxLength: 40 + minLength: 1 + pattern: ^[a-z][a-z0-9-]*[a-z0-9]$ + title: Project Name + description: A unique name for the project. Must start with a letter and contain only lowercase letters, numbers, or hyphens. + example: awesome-project + description: + type: string + title: Description + description: A description for the project. + default: '' + cidr: + type: string + title: VPC CIDR + description: The CIDR block to associate with the Net of the project. + region: + type: string + title: Region + description: The Region on which the project is deployed. + tags: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Tags + description: The key/value combinations of the tags associated with the resource, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + example: + key: value + net_specific: + anyOf: + - $ref: '#/components/schemas/NetSpecific' + - type: 'null' + title: Net Specific + description: Network-specific configuration for the project. + quirks: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Quirks + description: A list of special configurations or behaviors for the project. + disable_api_termination: + type: boolean + title: Disable API Termination + description: If true, project deletion through the API is disabled. If false, it is enabled. + default: false + example: false + type: object + required: + - name + - cidr + - region + title: ProjectInput + description: Information about the project configuration. + ProjectResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Project: + $ref: '#/components/schemas/Project' + description: Information about the project. + type: object + required: + - ResponseContext + - Project + title: Project Response + ProjectResponseList: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Pagination: + $ref: '#/components/schemas/Pagination' + description: Information used to split large lists of results into multiple responses (either cursor or offset-based). + Projects: + items: + $ref: '#/components/schemas/Project' + type: array + title: Projects + description: The list of retrieved projects. + type: object + required: + - ResponseContext + - Pagination + - Projects + title: ProjectResponseList + description: Information about the retrieved projects. + ProjectUpdate: + properties: + description: + anyOf: + - type: string + - type: 'null' + title: Description + description: The updated description for the project. + tags: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Tags + description: The updated key/value combinations of the tags associated with the project's metadata, in the following format: `"tags":{"TAGKEY1":"TAGVALUE1","TAGKEY2":"TAGVALUE2"}`. + example: + key: value + quirks: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Quirks + description: The updated list of special configurations or behaviors for the project. + disable_api_termination: + anyOf: + - type: boolean + - type: 'null' + title: Disable API Termination + description: If true, project deletion through the API is disabled. If false, it is enabled. + type: object + title: ProjectUpdate + description: Information about the updated project configuration. + PublicIp: + properties: + Tags: + items: + $ref: '#/components/schemas/ResourceTag' + type: array + title: Tags + description: One or more tags associated with the public IP. + PublicIp: + type: string + title: Public IP + description: The address of the public IP. + PublicIpId: + type: string + title: Public IP ID + description: The allocation ID of the public IP. + type: object + required: + - Tags + - PublicIp + - PublicIpId + title: PublicIp + description: Information about the public IP. + PublicIpsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + PublicIps: + items: + $ref: '#/components/schemas/PublicIp' + type: array + title: Project Public IPs + description: The public IP details associated with the project. + type: object + required: + - ResponseContext + - PublicIps + title: PublicIpsResponse + Quotas: + properties: + ShortDescription: + type: string + title: Short Description + description: A brief summary of the quota. + QuotaCollection: + type: string + title: Quota Collection + description: A category or group to which the quota belongs to. + AccountId: + type: string + title: Account ID + description: The ID of the account. + Description: + type: string + title: Description + description: A detailed description of the quota. + MaxValue: + type: integer + title: Max Value + description: The maximum allowed value for the quota. + UsedValue: + type: integer + title: Used Value + description: The current usage value for the quota. + Name: + type: string + title: Name + description: The name of the quota. + type: object + required: + - ShortDescription + - QuotaCollection + - AccountId + - Description + - MaxValue + - UsedValue + - Name + title: Quotas + description: Information about the quotas. + QuotasData: + properties: + quotas: + items: + $ref: '#/components/schemas/Quotas' + type: array + title: Quotas + description: A list of quota details. + subregions: + items: + $ref: '#/components/schemas/Subregion' + type: array + title: Subregions + description: A list of Subregion details. + type: object + required: + - quotas + - subregions + title: QuotasData + description: Information about the quotas for a project. + ResourceTag: + properties: + Key: + type: string + title: Key + description: The key for the tag. + Value: + type: string + title: Value + description: The value for the tag. + type: object + required: + - Key + - Value + title: ResourceTag + description: Information about the tags associated with a resource. + ResponseContext-Input: + properties: + RequestId: + type: string + title: Requestid + description: The ID of the request. + type: object + required: + - RequestId + title: ResponseContext + description: Information about the context of the response. + Snapshot: + properties: + VolumeSize: + type: integer + title: Volume Size + description: The size of the volume used to create the snapshot, in gibibytes (GiB). + AccountId: + type: string + title: Account ID + description: The account ID of the owner of the snapshot. + VolumeId: + type: string + title: Volume ID + description: The ID of the volume used to create the snapshot. + CreationDate: + type: string + title: Creation Date + description: The date and time (UTC) at which the snapshot was created. + Progress: + type: integer + title: Progress + description: The progress of the snapshot, as a percentage. + SnapshotId: + type: string + title: Snapshot ID + description: The ID of the snapshot. + State: + type: string + title: State + description: The state of the snapshot (`in-queue` | `pending` | `completed` | `error` | `deleting`). + Description: + type: string + title: Description + description: The description of the snapshot. + Tags: + items: + $ref: '#/components/schemas/ResourceTag' + type: array + title: Tags + description: One or more tags associated with the snapshot. + PermissionsToCreateVolume: + allOf: + - $ref: '#/components/schemas/PermissionsOnResource' + title: Permissions To Create Volume + description: Permissions for the resource. + type: object + required: + - VolumeSize + - AccountId + - VolumeId + - CreationDate + - Progress + - SnapshotId + - State + - Description + - Tags + - PermissionsToCreateVolume + title: Snapshot + description: Information about the Snapshot. + SnapshotsResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Snapshots: + items: + $ref: '#/components/schemas/Snapshot' + type: array + title: Project Snapshots + description: Snapshot details associated with the project. + type: object + required: + - ResponseContext + - Snapshots + title: SnapshotsResponse + Spec: + properties: + desiredNodes: + type: string + title: Desired Nodes + description: The number of desired nodes in the node pool. + nodeType: + type: string + title: Node Type + description: The type of VM for the nodes. + zones: + items: + type: string + type: array + title: Zones + description: A list of Subregions where nodes should be deployed. + volumes: + items: + $ref: '#/components/schemas/Volume' + type: array + title: Volumes + description: A list of volume configurations for the nodes. + upgradeStrategy: + allOf: + - $ref: '#/components/schemas/UpgradeStrategy' + title: Upgrade Strategy + description: The configuration for managing node pool upgrades. + autoHealing: + type: boolean + title: Auto Healing + description: If true, the automatic healing of failed nodes is enabled. + type: object + required: + - desiredNodes + - nodeType + - zones + - volumes + - upgradeStrategy + - autoHealing + title: Spec + description: Information about the specification for the node pool configuration. + SpecNetPeeringAcceptance: + properties: + netPeeringId: + type: string + title: NetID + description: The ID of the Net peering to accept. + type: object + required: + - netPeeringId + title: SpecNetPeeringAcceptance + description: The configuration of the custom resource used to accept a Net peering. + SpecNetPeeringRequest: + properties: + accepterNetId: + type: string + title: Accepter NetID + description: The ID of the Net that will accept the Net peering request. + accepterOwnerId: + type: string + title: Accepter Owner ID + description: The ID of the account that owns the accepted Net. + type: object + required: + - accepterNetId + - accepterOwnerId + title: SpecNetPeeringRequest + Statuses: + properties: + created_at: + type: string + format: date-time + title: Created At + description: The timestamp when the cluster was created. + deleted_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Deleted At + description: The timestamp when the cluster was deleted (if applicable). + updated_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Updated At + description: The timestamp when the cluster was last updated. + status: + anyOf: + - type: string + - type: 'null' + title: Status + description: The status of the cluster. + available_upgrade: + type: string + title: Available Upgrade + description: Any available version of Kubernetes for upgrade (if applicable). For more information, see [GetKubernetesVersions](#getkubernetesversions). + default: '' + type: object + required: + - created_at + title: Statuses + description: Information about the status of the cluster. + Subregion: + properties: + State: + type: string + title: State + description: The state of the Subregion. + RegionName: + type: string + title: Region Name + description: The name of the Region containing the Subregion. + SubregionName: + type: string + title: Subregion Name + description: The name of the Subregion. + LocationCode: + type: string + title: Location Code + description: The location code (physical zone) of the Subregion. + type: object + required: + - State + - RegionName + - SubregionName + - LocationCode + title: Subregion + description: Information about the Subregion. + TemplateResponse_ClusterInputTemplate_: + properties: + ResponseContext: + $ref: '#/components/schemas/templates__template_schema__ResponseContext' + description: Information about the context of the response. + Template: + allOf: + - $ref: '#/components/schemas/ClusterInputTemplate' + title: Template + description: The returned template resource. + type: object + required: + - ResponseContext + - Template + title: TemplateResponse[ClusterInputTemplate] + description: Information about the default cluster template. + TemplateResponse_NetPeeringAcceptance_: + properties: + ResponseContext: + $ref: '#/components/schemas/templates__template_schema__ResponseContext' + description: Information about the context of the response. + Template: + allOf: + - $ref: '#/components/schemas/NetPeeringAcceptance' + title: Template + description: The returned template resource. + type: object + required: + - ResponseContext + - Template + title: TemplateResponse[NetPeeringAcceptance] + description: The default Net peering acceptance template. + TemplateResponse_NetPeeringRequest_: + properties: + ResponseContext: + $ref: '#/components/schemas/templates__template_schema__ResponseContext' + description: Information about the context of the response. + Template: + allOf: + - $ref: '#/components/schemas/NetPeeringRequest' + title: Template + description: The returned template resource. + type: object + required: + - ResponseContext + - Template + title: TemplateResponse[NetPeeringRequest] + description: The default Net peering request template. + TemplateResponse_Nodepool_: + properties: + ResponseContext: + $ref: '#/components/schemas/templates__template_schema__ResponseContext' + description: Information about the context of the response. + Template: + allOf: + - $ref: '#/components/schemas/Nodepool' + title: Template + description: The returned template resource. + type: object + required: + - ResponseContext + - Template + title: TemplateResponse[Nodepool] + description: Information about the default node pool template. + TemplateResponse_ProjectInput_: + properties: + ResponseContext: + $ref: '#/components/schemas/templates__template_schema__ResponseContext' + description: Information about the context of the response. + Template: + allOf: + - $ref: '#/components/schemas/ProjectInput' + title: Template + description: The returned template resource. + type: object + required: + - ResponseContext + - Template + title: TemplateResponse[ProjectInput] + description: Information about the default project template. + UpgradeStrategy: + properties: + maxUnavailable: + type: integer + title: Max Unavailable + description: The maximum number of nodes that can be unavailable during an upgrade. + maxSurge: + type: integer + title: Max Surge + description: The maximum number of extra nodes that can be created during an upgrade. + autoUpgradeEnabled: + type: boolean + title: Auto Upgrade Enabled + description: If true, automatic upgrades for the node pool are enabled. + autoUpgradeMaintenance: + allOf: + - $ref: '#/components/schemas/AutoUpgradeMaintenance' + title: Auto Upgrade Maintenance + description: The configuration for the automated upgrade maintenance window. + type: object + required: + - maxUnavailable + - maxSurge + - autoUpgradeEnabled + - autoUpgradeMaintenance + title: UpgradeStrategy + description: Information for the management of node pool upgrades. + ValidationDetail: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Loc + description: The location of the validation error in the request. + msg: + type: string + title: Msg + description: A descriptive message about the error. + type: + type: string + title: Type + description: The identifier for the type of validation error. + type: object + required: + - loc + - msg + - type + title: ValidationDetail + description: Information about the validation error. + Volume: + properties: + device: + type: string + title: Device + description: The device name for the volume. + type: + type: string + title: Type + description: The type of the volume (`gp2`, `io1`, `standard`). + size: + type: integer + title: Size + description: The size of the volume. + dir: + type: string + title: Directory + description: The mount point directory path for the volume. + type: object + required: + - device + - type + - size + - dir + title: Volume + description: Information about the volume configuration. + clusters__cluster_schema__RPCResponse: + properties: + request_id: + type: string + title: Request ID + description: The ID of the API request. + data: + allOf: + - $ref: '#/components/schemas/KubeconfigData' + title: Data + description: The kubeconfig data for the cluster. + type: object + required: + - request_id + - data + title: RPCResponse + clusters__cluster_schema__ResponseContext: + properties: + RequestId: + type: string + title: Request ID + description: The ID of the API request. + type: object + required: + - RequestId + title: ResponseContext + description: Information about the context of the response. + myip__myip_schema__ResponseContext: + properties: + RequestId: + type: string + title: Request ID + description: The ID of the API request. + type: object + required: + - RequestId + title: ResponseContext + description: Information about the context of the response. + netpeerings__netpeering_schema__Metadata: + properties: + name: + type: string + title: Name + description: An ID for the Net peering. + type: object + required: + - name + title: Metadata + description: Information about the Net peering's metadata. + nodepools__nodepool_schema__Metadata: + properties: + name: + type: string + title: Name + description: An ID for the node pool. + type: object + required: + - name + title: Metadata + description: Information about the node pool's metadata. + projects__project_schema__QuotasResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/projects__project_schema__ResponseContext' + description: Information about the context of the response. + Project: + allOf: + - $ref: '#/components/schemas/projects__project_schema__RPCResponse' + title: Project Quotas + description: The quota details associated with the project. + type: object + required: + - ResponseContext + - Project + title: QuotasResponse + projects__project_schema__RPCResponse: + properties: + request_id: + type: string + title: Request ID + description: The ID of the API request. + data: + allOf: + - $ref: '#/components/schemas/QuotasData' + title: Data + description: The quota information related to the request. + type: object + required: + - request_id + - data + title: RPCResponse + projects__project_schema__ResponseContext: + properties: + RequestId: + type: string + title: Request ID + description: The ID of the API request. + type: object + required: + - RequestId + title: Response Context + description: Information about the context of the response. + quotas__quota_schema__QuotasResponse: + properties: + ResponseContext: + $ref: '#/components/schemas/quotas__quota_schema__ResponseContext' + description: Information about the context of the response. + Quotas: + $ref: '#/components/schemas/OKSQuotas' + description: Information about your quotas. + type: object + required: + - ResponseContext + - Quotas + title: QuotasResponse + quotas__quota_schema__ResponseContext: + properties: + RequestId: + type: string + title: Request ID + description: The ID of the API request. + type: object + required: + - RequestId + title: Response Context + description: Information about the context of the response. + templates__template_schema__ResponseContext: + properties: + RequestId: + type: string + title: Request ID + description: The ID of the API request. + type: object + required: + - RequestId + title: ResponseContext + description: Information about the context of the response. + securitySchemes: + BasicAuth: + type: http + scheme: basic + description: A header in the form `Basic XXXX`, where `XXXX` is the Base64 encoding of the root user's login and password joined by a colon (`:`). Note that with Curl, you can use the `--user LOGIN:PASSWORD` option as a shortcut for this and Curl will format the header for you. + AccessKeyAuth: + type: apiKey + in: header + name: AccessKey + description: A header with an access key of the root user. You cannot use EIM users' access keys with OKS. + SecretKeyAuth: + type: apiKey + in: header + name: SecretKey + description: A header with the corresponding secret key of the access key. + AccessTokenAuth: + type: apiKey + in: header + name: AccessToken + description: In the case of JSON Web Token (JWT) authentication, a header with an access token. + RefreshTokenAuth: + type: apiKey + in: header + name: RefreshToken + description: In the case of JWT authentication, a header with the corresponding refresh token. + OTPCodeAuth: + type: apiKey + in: header + name: X-OTP-Code + description: (optional) If multi-factor authentication (MFA) is set up on your account, a header with your OTP token. +security: +- BasicAuth: [] + OTPCodeAuth: [] +- AccessKeyAuth: [] + SecretKeyAuth: [] + OTPCodeAuth: [] +- AccessTokenAuth: [] + RefreshTokenAuth: [] +servers: +- url: https://api.eu-west-2.oks.outscale.com/api/v2 +- url: https://api.cloudgouv-eu-west-1.oks.outscale.com/api/v2 diff --git a/osc_sdk_python/resources/oks/cfg.yaml b/osc_sdk_python/resources/oks/cfg.yaml new file mode 100644 index 0000000..e34f446 --- /dev/null +++ b/osc_sdk_python/resources/oks/cfg.yaml @@ -0,0 +1,2 @@ +spec: ./api.yaml +overlay: ./patch.yaml diff --git a/osc_sdk_python/resources/oks/patch.yaml b/osc_sdk_python/resources/oks/patch.yaml new file mode 100644 index 0000000..5fdc01f --- /dev/null +++ b/osc_sdk_python/resources/oks/patch.yaml @@ -0,0 +1,19 @@ +overlay: 1.1.0 +info: + title: aaa + version: 1.0.0 + description: bbb +actions: + - target: $.components.schemas + update: + ResponseContext: + additionalProperties: false + description: Information about the context of the response. + properties: + RequestId: + description: The ID of the request. + type: string + type: object + - target: $.components.schemas.*.properties.ResponseContext + update: + "$ref": "#/components/schemas/ResponseContext" diff --git a/osc_sdk_python/resources/outscale.yaml b/osc_sdk_python/resources/osc/api.yaml similarity index 95% rename from osc_sdk_python/resources/outscale.yaml rename to osc_sdk_python/resources/osc/api.yaml index 9882430..c4695c4 100644 --- a/osc_sdk_python/resources/outscale.yaml +++ b/osc_sdk_python/resources/osc/api.yaml @@ -259,11 +259,11 @@ components: description: The description of the state of the backend VM. type: string State: - description: The state of the backend VM (`InService` \| `OutOfService` \| `Unknown`). + description: The state of the backend VM (`UP` \| `DOWN` \| `UNKNOWN`). type: string StateReason: description: |- - Information about the cause of `OutOfService` VMs.
+ Information about the cause of `DOWN` VMs.
Specifically, whether the cause is Elastic Load Balancing or the VM (`ELB` \| `Instance` \| `N/A`). type: string VmId: @@ -670,7 +670,7 @@ components: description: The country of the account owner. type: string CustomerId: - description: The ID of the customer. It must be 8 digits. + description: 'The ID of the customer. It must be 8 digits.
With OSC CLI, you must wrap this value in two pairs of quotes to make sure it is parsed as a string: `--CustomerId ''"12345678"''`.' type: string DryRun: description: If true, checks whether you have the required permissions to perform the action. @@ -700,7 +700,7 @@ components: description: The value added tax (VAT) number for the account. type: string ZipCode: - description: The ZIP code of the city. + description: 'The ZIP code of the city.
With OSC CLI, you must wrap this value in two pairs of quotes to make sure it is parsed as a string: `--ZipCode ''"12345678"''`.' type: string required: - City @@ -1283,7 +1283,7 @@ components: additionalProperties: false properties: ClientToken: - description: A unique identifier which enables you to manage the idempotency. + description: 'A unique identifier which enables you to manage the idempotency.
With OSC CLI, if you want to specify a number for this value, you must wrap it in two pairs of quotes to make sure the value is parsed as a string: `--ClientToken ''"12345678"''`.' type: string DryRun: description: If true, checks whether you have the required permissions to perform the action. @@ -1346,13 +1346,13 @@ components: properties: AccepterNetId: description: |- - The ID of the Net you want to connect with.

+ The ID of the Net you want to connect with.
If the Net does not belong to you, you must also specify the `AccepterOwnerId` parameter with the OUTSCALE account ID owning the Net you want to connect with. type: string AccepterOwnerId: description: |- - The OUTSCALE account ID of the owner of the Net you want to connect with. By default, the account ID of the owner of the Net from which the peering request is sent.

- This parameter is required if the Net you want to connect with does not belong to you. + The OUTSCALE account ID of the owner of the Net you want to connect with. By default, the account ID of the owner of the Net from which the peering request is sent.
+ This parameter is required if the Net you want to connect with does not belong to you.
With OSC CLI, you must wrap the value in two pairs of quotes to make sure it is parsed as a string: `--AccepterOwnerId '"12345678"'`. type: string DryRun: description: If true, checks whether you have the required permissions to perform the action. @@ -1660,7 +1660,7 @@ components: $ref: '#/components/schemas/SecurityGroupRule' type: array SecurityGroupAccountIdToLink: - description: The OUTSCALE account ID that owns the source or destination security group specified in the `SecurityGroupNameToLink` parameter. + description: 'The OUTSCALE account ID that owns the source or destination security group specified in the `SecurityGroupNameToLink` parameter.
With OSC CLI, you must wrap the value in two pairs of quotes to make sure it is parsed as a string: `--SecurityGroupAccountIdToLink ''"12345678"''`.' type: string SecurityGroupId: description: The ID of the security group for which you want to create a rule. @@ -1751,7 +1751,7 @@ components: additionalProperties: false properties: ClientToken: - description: A unique identifier which enables you to manage the idempotency. + description: 'A unique identifier which enables you to manage the idempotency.
With OSC CLI, if you want to specify a number for this value, you must wrap it in two pairs of quotes to make sure the value is parsed as a string: `--ClientToken ''"12345678"''`.' type: string Description: description: A description for the snapshot. @@ -2054,7 +2054,7 @@ components: description: This parameter is not available. It is present in our API for the sake of historical compatibility with AWS. type: boolean ClientToken: - description: A unique identifier which enables you to manage the idempotency. + description: 'A unique identifier which enables you to manage the idempotency.
With OSC CLI, if you want to specify a number for this value, you must wrap it in two pairs of quotes to make sure the value is parsed as a string: `--ClientToken ''"12345678"''`.' type: string DeletionProtection: description: If true, you cannot delete the VM unless you change this parameter back to false. @@ -2109,6 +2109,9 @@ components: items: type: string type: array + ShutdownBehaviorConfiguration: + $ref: '#/components/schemas/ShutdownBehaviorConfiguration' + description: Information about the actions performed by the orchestrator when the VM shuts down. SubnetId: description: The ID of the Subnet in which you want to create the VM. type: string @@ -2119,7 +2122,7 @@ components: description: Data or script used to add a specific configuration to the VM. It must be Base64-encoded and is limited to 500 kibibytes (KiB). For more information about user data, see [Configuring a VM with User Data and OUTSCALE Tags](https://docs.outscale.com/en/userguide/Configuring-a-VM-with-User-Data-and-OUTSCALE-Tags.html). type: string VmInitiatedShutdownBehavior: - default: stop + deprecated: true description: The VM behavior when you stop it. If set to `stop`, the VM stops. If set to `restart`, the VM stops then automatically restarts. If set to `terminate`, the VM stops and is terminated. type: string VmType: @@ -2146,7 +2149,7 @@ components: additionalProperties: false properties: ClientToken: - description: A unique identifier which enables you to manage the idempotency. + description: 'A unique identifier which enables you to manage the idempotency.
With OSC CLI, if you want to specify a number for this value, you must wrap it in two pairs of quotes to make sure the value is parsed as a string: `--ClientToken ''"12345678"''`.' type: string DryRun: description: If true, checks whether you have the required permissions to perform the action. @@ -5607,6 +5610,35 @@ components: type: string type: array type: object + FiltersVmsStopHistory: + additionalProperties: false + description: One or more filters. + properties: + StateReasons: + description: The reason explaining why the VM stopped. You can filter by reason code or reason prefix (for example, `Client.ApiGracefulShutdown` or `Client.*`). For the list of reason codes, see [Creating VMs > VM State Reference](https://docs.outscale.com/en/userguide/Creating-VMs). + items: + type: string + type: array + StopDateAfter: + description: The date and time (UTC), or the date, after which you want to retrieve VM stops, in ISO 8601 format (for example, `2026-06-14T00:00:00.000Z` or `2026-06-14`). + oneOf: + - format: date + type: string + - format: date-time + type: string + StopDateBefore: + description: The date and time (UTC), or the date, before which you want to retrieve VM stops, in ISO 8601 format (for example, `2026-06-14T00:00:00.000Z` or `2026-06-14`). + oneOf: + - format: date + type: string + - format: date-time + type: string + VmIds: + description: The IDs of the stopped VM(s). + items: + type: string + type: array + type: object FiltersVolume: additionalProperties: false description: One or more filters. @@ -6121,7 +6153,7 @@ components: description: If true, the NIC is deleted when the VM is terminated. type: boolean DeviceNumber: - description: The device index for the NIC attachment (between `1` and `7`, both included). + description: The device index for the NIC attachment (between `0` and `7`, both included). type: integer LinkNicId: description: The ID of the NIC to attach. @@ -6144,7 +6176,7 @@ components: description: If true, the NIC is deleted when the VM is terminated. type: boolean DeviceNumber: - description: The device index for the NIC attachment (between `1` and `7`, both included). + description: The device index for the NIC attachment (between `0` and `7`, both included). type: integer LinkNicId: description: The ID of the NIC to attach. @@ -9668,6 +9700,36 @@ components: $ref: '#/components/schemas/VmStates' type: array type: object + ReadVmsStopHistoryRequest: + additionalProperties: false + properties: + Filters: + $ref: '#/components/schemas/FiltersVmsStopHistory' + description: One or more filters. + NextPageToken: + description: The token to request the next page of results. Each token refers to a specific page. + format: byte + type: string + ResultsPerPage: + description: The maximum number of logs returned in a single response (between `1` and `1000`, both included). + type: integer + type: object + ReadVmsStopHistoryResponse: + additionalProperties: false + properties: + NextPageToken: + description: The token to request the next page of results. Each token refers to a specific page. + format: byte + type: string + ResponseContext: + $ref: '#/components/schemas/ResponseContext' + description: Information about the context of the response. + VmsStopHistory: + description: Information about the VM(s) stop history. + items: + $ref: '#/components/schemas/VmsStopHistory' + type: array + type: object ReadVolumeUpdateTasksRequest: additionalProperties: false properties: @@ -10206,6 +10268,23 @@ components: $ref: '#/components/schemas/ResponseContext' description: Information about the context of the response. type: object + ShutdownBehaviorConfiguration: + additionalProperties: false + description: Information about the actions performed by the orchestrator when the VM shuts down. + properties: + GuestAction: + description: The action performed by the orchestrator when the VM is shut down from the guest operating system. By default, `stop`. + enum: + - stop + - terminate + type: string + HostAction: + description: The action performed by the orchestrator when the VM is shut down due to a host infrastructure failure. By default, `restart`. + enum: + - restart + - stop + type: string + type: object Snapshot: additionalProperties: false description: Information about the snapshot. @@ -10267,7 +10346,7 @@ components: description: The ID of the snapshot to be exported. type: string State: - description: The state of the snapshot export task (`pending` \| `active` \| `completed` \| `cancelled` \| `failed`). + description: The state of the snapshot export task (`pending` \| `initializing` \| `preparing` \| `uploading` \| `completed` \| `cancelled` \| `failed`). type: string Tags: description: One or more tags associated with the snapshot export task. @@ -10382,7 +10461,7 @@ components: description: The IP range in the Subnet, in CIDR notation (for example, `10.0.0.0/16`). type: string MapPublicIpOnLaunch: - description: If true, a public IP is assigned to the network interface cards (NICs) created in the specified Subnet. + description: If true, a public IP is assigned to the network interface cards (NICs) created in the specified Subnet. By default, false. type: boolean NetId: description: The ID of the Net in which the Subnet is. @@ -10796,7 +10875,7 @@ components: description: The new value added tax (VAT) number for the account. type: string ZipCode: - description: The new ZIP code of the city. + description: 'The new ZIP code of the city.
With OSC CLI, you must wrap this value in two pairs of quotes to make sure it is parsed as a string: `--ZipCode ''"12345678"''`.' type: string type: object UpdateAccountResponse: @@ -11515,6 +11594,9 @@ components: items: type: string type: array + ShutdownBehaviorConfiguration: + $ref: '#/components/schemas/ShutdownBehaviorConfiguration' + description: Information about the actions performed by the orchestrator when the VM shuts down. UserData: description: The Base64-encoded MIME user data, limited to 500 kibibytes (KiB). type: string @@ -11522,6 +11604,7 @@ components: description: The ID of the VM. type: string VmInitiatedShutdownBehavior: + deprecated: true description: The VM behavior when you stop it. If set to `stop`, the VM stops. If set to `restart`, the VM stops then automatically restarts. If set to `terminate`, the VM stops and is terminated. type: string VmType: @@ -11830,11 +11913,14 @@ components: items: $ref: '#/components/schemas/SecurityGroupLight' type: array + ShutdownBehaviorConfiguration: + $ref: '#/components/schemas/ShutdownBehaviorConfiguration' + description: Information about the actions performed by the orchestrator when the VM shuts down. State: description: The state of the VM (`pending` \| `running` \| `stopping` \| `stopped` \| `shutting-down` \| `terminated` \| `quarantine`). type: string StateReason: - description: The reason explaining the current state of the VM. + description: The reason explaining the current state of the VM. For more information, see [Creating VMs > VM State Reference](https://docs.outscale.com/en/userguide/Creating-VMs.html#_vm_state_reference_statereason_2). type: string SubnetId: description: The ID of the Subnet for the VM. @@ -11854,7 +11940,7 @@ components: description: The ID of the VM. type: string VmInitiatedShutdownBehavior: - description: The VM behavior when you stop it. If set to `stop`, the VM stops. If set to `restart`, the VM stops then automatically restarts. If set to `terminate`, the VM stops and is deleted. + description: 'The VM behavior when you stop it. If set to `stop`, the VM stops. If set to `restart`, the VM stops then automatically restarts. If set to `terminate`, the VM stops and is deleted. Important: This parameter is deprecated in favor of `ShutDownBeheviorConfiguration` and will be removed.' type: string VmType: description: The type of VM. For more information, see [VM Types](https://docs.outscale.com/en/userguide/VM-Types.html). @@ -12040,6 +12126,21 @@ components: description: The size of one ephemeral storage disk, in gibibytes (GiB). type: integer type: object + VmsStopHistory: + additionalProperties: false + description: Information about the stop history of one or more VMs. + properties: + StateReason: + description: The reason explaining why the VM stopped. For more information, see [Creating VMs > VM State Reference](https://docs.outscale.com/en/userguide/Creating-VMs.html#_vm_state_reference_statereason_2). + type: string + StopDate: + description: The date and time (UTC) of the stop event. + format: date-time + type: string + VmId: + description: The ID of the VM. + type: string + type: object Volume: additionalProperties: false description: Information about the volume. @@ -12335,11 +12436,11 @@ info: The mechanism behind this is based on AWS Signature Version 4, whose technical implementation details are described in [Signature of API Requests](https://docs.outscale.com/en/userguide/Signature-of-API-Requests.html).

In practice, the way to specify your access key and secret key depends on the tool or SDK you want to use to interact with the API.
- > For example, if you use OSC CLI: - > 1. You need to create an **~/.osc/config.json** file to specify your access key, secret key, and the Region of your account. - > 2. You then specify the `--profile` option when executing OSC CLI commands. + > For example, to authenticate with access key/secret key when using octl: + > * You can specify the following environment variables: `OSC_ACCESS_KEY`, `OSC_SECRET_KEY`, and `OSC_REGION`. + > * Or you can store a profile in a **~/.osc/config.json** file, with the following fields specified: `access_key`, `secret_key`, and `region`. Then you select the profile by specifying the `--profile` option when executing octl commands. > - > For more information, see [Installing and Configuring OSC CLI](https://docs.outscale.com/en/userguide/Installing-and-Configuring-OSC-CLI.html). + > For more information, see [Installing and Configuring octl](https://docs.outscale.com/en/userguide/Installing-and-Configuring-octl.html). See the code samples in each section of this documentation for specific examples in different programming languages.
For more information about access keys, see [About Access Keys](https://docs.outscale.com/en/userguide/About-Access-Keys.html). @@ -12351,9 +12452,11 @@ info: This is useful only in special circumstances, for example if you do not know your access key/secret key and want to retrieve them programmatically.
In most cases, however, you can use the Cockpit web interface to retrieve them.
- > For example, if you use OSC CLI: - > 1. You need to create an **~/.osc/config.json** file to specify the Region of your account, but you leave the access key value and secret key value empty (`""`). - > 2. You then specify the `--profile`, `--authentication-method`, `--login`, and `--password` options when executing OSC CLI commands. + > For example, to authenticate with login/password when using octl: + > * You can specify the following environment variables: `OSC_LOGIN`, `OSC_PASSWORD`, and `OSC_REGION`. + > * Or you can store a profile in a **~/.osc/config.json** file, with the following fields specified: `login`, `password`, and `region`. Then you select the profile by specifying the `--profile` option when executing octl commands. + > + > For more information, see [Installing and Configuring octl](https://docs.outscale.com/en/userguide/Installing-and-Configuring-octl.html). See the code samples in each section of this documentation for specific examples in different programming languages. @@ -12373,11 +12476,11 @@ info: url: https://opensource.org/licenses/BSD-3-Clause termsOfService: https://en.outscale.com/terms-of-service/ title: 3DS OUTSCALE API - version: 1.41.0 + version: 1.42.0 x-osc-api-osc-billing: 1.39.0 x-osc-api-osc-cloud-region: 1.37.0 x-osc-api-osc-cloud-vision: 1.37.0 - x-osc-api-osc-core-iaas: 1.39.0 + x-osc-api-osc-core-iaas: 1.40.0 x-osc-api-osc-iam: 1.37.6 x-osc-api-type: external openapi: 3.0.0 @@ -12426,31 +12529,31 @@ paths: IpRange: 10.0.0.0/16 AccountId: '123456789012' NetPeeringId: pcx-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '409': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 409 response (Conflict). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NetPeering /AddUserToUserGroup: @@ -12480,7 +12583,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /CheckAuthentication: @@ -12508,7 +12611,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Account /CreateAccessKey: @@ -12547,7 +12650,7 @@ paths: SecretKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX LastModificationDate: 2010-10-01T12:34:56.789+0000 Tag: Group1 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -12560,7 +12663,6 @@ paths: **[IMPORTANT]**
* You need OUTSCALE credentials and the appropriate quotas to create an account via API. To get quotas, you can send an email to sales@outscale.com.
- * If you want to pass a numeral value as a string instead of an integer, you must wrap your string in additional quotes (for example, `'"92000"'`). For more information, see [About Your Account](https://docs.outscale.com/en/userguide/About-Your-OUTSCALE-Account.html). operationId: CreateAccount @@ -12600,7 +12702,7 @@ paths: LastName: DUPONT AccountId: '123456789012' Email: example@example.com - description: '' + description: The HTTP 200 response (OK). tags: - Account /CreateApiAccessRule: @@ -12669,7 +12771,7 @@ paths: - ca-fedcba0987654321fedcba0987654321 Cns: [] Description: API Access Rule with IPs and CA - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -12706,7 +12808,7 @@ paths: Description: CA example CaId: ca-fedcba0987654321fedcba0987654321 CaFingerprint: 1234567890abcdef1234567890abcdef12345678 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -12749,7 +12851,7 @@ paths: ClientGatewayId: cgw-12345678 ConnectionType: ipsec.1 PublicIp: 192.0.2.0 - description: '' + description: The HTTP 200 response (OK). tags: - ClientGateway /CreateDedicatedGroup: @@ -12788,25 +12890,25 @@ paths: Name: dedicated-group-example SubregionName: eu-west-2a DedicatedGroupId: ded-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - DedicatedGroup /CreateDhcpOptions: @@ -12852,7 +12954,7 @@ paths: DomainNameServers: - 192.0.2.0 - 198.51.100.0 - description: '' + description: The HTTP 200 response (OK). tags: - DhcpOption /CreateDirectLink: @@ -12891,7 +12993,7 @@ paths: Location: PAR1 RegionName: eu-west-2 State: requested - description: '' + description: The HTTP 200 response (OK). tags: - DirectLink /CreateDirectLinkInterface: @@ -12943,7 +13045,7 @@ paths: State: pending InterfaceType: private Location: PAR1 - description: '' + description: The HTTP 200 response (OK). tags: - DirectLinkInterface /CreateFlexibleGpu: @@ -12984,7 +13086,7 @@ paths: State: allocated FlexibleGpuId: fgpu-12345678 Tags: [] - description: '' + description: The HTTP 200 response (OK). tags: - FlexibleGpu /CreateImage: @@ -13186,25 +13288,25 @@ paths: FileLocation: https://oos.eu-west-2.outscale.com/BUCKET/KEY?AWSAccessKeyId=ABCDEFGHIJ0123456789&Expires=1493372309&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Architecture: x86_64 ImageName: register-image-from-bucket-example - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Image /CreateImageExportTask: @@ -13256,7 +13358,7 @@ paths: DiskImageFormat: qcow2 State: pending/queued Progress: 0 - description: '' + description: The HTTP 200 response (OK). tags: - Image /CreateInternetService: @@ -13288,25 +13390,25 @@ paths: InternetService: Tags: [] InternetServiceId: igw-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - InternetService /CreateKeypair: @@ -13366,31 +13468,31 @@ paths: KeypairName: create-keypair-example KeypairId: key-abcdef1234567890abcdef1234567890 KeypairFingerprint: 11:22:33:44:55:66:77:88:99:00:aa:bb:cc:dd:ee:ff - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '409': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 409 response (Conflict). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Keypair /CreateListenerRule: @@ -13467,7 +13569,7 @@ paths: ListenerId: 123456 PathPattern: /docs/* ListenerRuleId: 1234 - description: '' + description: The HTTP 200 response (OK). tags: - Listener /CreateLoadBalancer: @@ -13648,7 +13750,7 @@ paths: LoadBalancerPort: 8080 LoadBalancerProtocol: HTTP LoadBalancerName: public-lb-example - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /CreateLoadBalancerListeners: @@ -13720,7 +13822,7 @@ paths: LoadBalancerPort: 80 LoadBalancerProtocol: TCP LoadBalancerName: example-lbu - description: '' + description: The HTTP 200 response (OK). tags: - Listener /CreateLoadBalancerPolicy: @@ -13841,7 +13943,7 @@ paths: LoadBalancerPort: 80 LoadBalancerProtocol: HTTP LoadBalancerName: example-lbu - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancerPolicy /CreateLoadBalancerTags: @@ -13875,7 +13977,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /CreateNatService: @@ -13920,25 +14022,25 @@ paths: PublicIp: 192.0.2.0 NetId: vpc-12345678 State: available - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NatService /CreateNet: @@ -13975,31 +14077,31 @@ paths: Tenancy: default NetId: vpc-12345678 State: available - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '409': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 409 response (Conflict). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Net /CreateNetAccessPoint: @@ -14042,7 +14144,7 @@ paths: State: pending NetId: vpc-12345678 ServiceName: com.outscale.eu-west-2.oos - description: '' + description: The HTTP 200 response (OK). tags: - NetAccessPoint /CreateNetPeering: @@ -14119,25 +14221,25 @@ paths: IpRange: 10.0.0.0/16 AccountId: '123456789012' NetPeeringId: pcx-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NetPeering /CreateNic: @@ -14228,25 +14330,25 @@ paths: - PrivateDnsName: ip-10-0-0-5.eu-west-2.compute.internal PrivateIp: 10.0.0.5 IsPrimary: false - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /CreatePolicy: @@ -14289,7 +14391,7 @@ paths: Orn: orn:ows:idauth::012345678910:policy/example/example-user-policy IsLinkable: true LastModificationDate: 2010-10-01T12:34:56.789+0000 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /CreatePolicyVersion: @@ -14329,7 +14431,7 @@ paths: DefaultVersion: true CreationDate: 2017-05-10T12:34:56.789+0000 Body: '{"Statement": [ {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]} ]}' - description: '' + description: The HTTP 200 response (OK). tags: - Policy /CreateProductType: @@ -14361,14 +14463,14 @@ paths: Vendor: vendor-name ProductTypeId: pty-12345678 Description: Example of description - description: '' + description: The HTTP 200 response (OK). tags: - ProductType /CreatePublicIp: post: description: |- Acquires a public IP for your account.
- A public IP is a static IP designed for dynamic Cloud computing. It can be associated with a virtual machine (VM) in the public Cloud or in a Net, a network interface card (NIC), a NAT service.

+ A public IP is a static IP designed for dynamic Cloud computing. It can be associated with a virtual machine (VM) in the public Cloud, a VM in a Net, a network interface card (NIC), or a NAT service.

For more information, see [About Public IPs](https://docs.outscale.com/en/userguide/About-Public-IPs.html). operationId: CreatePublicIp requestBody: @@ -14394,25 +14496,25 @@ paths: Tags: [] PublicIpId: eipalloc-12345678 PublicIp: 192.0.2.0 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - PublicIp /CreateRoute: @@ -14469,25 +14571,25 @@ paths: RouteTableId: rtb-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Route /CreateRouteTable: @@ -14527,25 +14629,25 @@ paths: RouteTableId: rtb-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - RouteTable /CreateSecurityGroup: @@ -14595,25 +14697,25 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - SecurityGroup /CreateSecurityGroupRule: @@ -14720,25 +14822,25 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - SecurityGroupRule /CreateServerCertificate: @@ -14781,7 +14883,7 @@ paths: Name: server-cert-example ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - ServerCertificate /CreateSnapshot: @@ -14882,25 +14984,25 @@ paths: Tags: [] ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Snapshot /CreateSnapshotExportTask: @@ -14947,7 +15049,7 @@ paths: Progress: 0 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Snapshot /CreateSubnet: @@ -14987,31 +15089,31 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '409': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 409 response (Conflict). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Subnet /CreateTags: @@ -15070,25 +15172,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Tag /CreateUser: @@ -15126,7 +15228,7 @@ paths: Path: /documentation/ ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - User /CreateUserGroup: @@ -15163,7 +15265,7 @@ paths: Orn: orn:ows:idauth::012345678910:usergroup/example/usergroup-example Path: /example/ UserGroupId: ug-12345678 - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /CreateVirtualGateway: @@ -15199,7 +15301,7 @@ paths: Tags: [] ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VirtualGateway /CreateVmGroup: @@ -15257,25 +15359,25 @@ paths: VmTemplateId: vmtemplate-98765432109876543210987654321012 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VmGroup /CreateVmTemplate: @@ -15330,7 +15432,7 @@ paths: Ram: 2 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VmTemplate /CreateVms: @@ -15675,25 +15777,25 @@ paths: PrivateDnsName: ip-10-0-0-4.eu-west-2.compute.internal ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /CreateVolume: @@ -15762,25 +15864,25 @@ paths: Size: 10 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Volume /CreateVpnConnection: @@ -15831,7 +15933,7 @@ paths: VpnConnectionId: vpn-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VpnConnection /CreateVpnConnectionRoute: @@ -15862,7 +15964,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VpnConnection /DeleteAccessKey: @@ -15897,7 +15999,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -15931,7 +16033,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -15961,7 +16063,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -15993,7 +16095,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - ClientGateway /DeleteDedicatedGroup: @@ -16033,25 +16135,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - DedicatedGroup /DeleteDhcpOptions: @@ -16083,7 +16185,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - DhcpOption /DeleteDirectLink: @@ -16112,7 +16214,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - DirectLink /DeleteDirectLinkInterface: @@ -16139,7 +16241,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - DirectLinkInterface /DeleteExportTask: @@ -16179,7 +16281,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Task /DeleteFlexibleGpu: @@ -16208,7 +16310,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - FlexibleGpu /DeleteImage: @@ -16235,25 +16337,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Image /DeleteInternetService: @@ -16282,25 +16384,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - InternetService /DeleteKeypair: @@ -16334,25 +16436,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Keypair /DeleteListenerRule: @@ -16381,7 +16483,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Listener /DeleteLoadBalancer: @@ -16444,7 +16546,7 @@ paths: LoadBalancerPort: 443 LoadBalancerProtocol: HTTPS LoadBalancerName: private-lb-example - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /DeleteLoadBalancerListeners: @@ -16503,7 +16605,7 @@ paths: - eu-west-2a Listeners: [] LoadBalancerName: example-lbu - description: '' + description: The HTTP 200 response (OK). tags: - Listener /DeleteLoadBalancerPolicy: @@ -16568,7 +16670,7 @@ paths: LoadBalancerPort: 80 LoadBalancerProtocol: HTTP LoadBalancerName: example-lbu - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancerPolicy /DeleteLoadBalancerTags: @@ -16598,7 +16700,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /DeleteNatService: @@ -16627,25 +16729,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NatService /DeleteNet: @@ -16684,25 +16786,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Net /DeleteNetAccessPoint: @@ -16731,7 +16833,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - NetAccessPoint /DeleteNetPeering: @@ -16762,31 +16864,31 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '409': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 409 response (Conflict). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NetPeering /DeleteNic: @@ -16815,25 +16917,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /DeletePolicy: @@ -16862,7 +16964,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /DeletePolicyVersion: @@ -16895,7 +16997,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /DeleteProductType: @@ -16927,25 +17029,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - ProductType /DeletePublicIp: @@ -16974,25 +17076,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - PublicIp /DeleteRoute: @@ -17030,25 +17132,25 @@ paths: RouteTableId: rtb-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Route /DeleteRouteTable: @@ -17077,25 +17179,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - RouteTable /DeleteSecurityGroup: @@ -17125,25 +17227,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - SecurityGroup /DeleteSecurityGroupRule: @@ -17229,25 +17331,25 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - SecurityGroupRule /DeleteServerCertificate: @@ -17274,7 +17376,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - ServerCertificate /DeleteSnapshot: @@ -17303,25 +17405,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Snapshot /DeleteSubnet: @@ -17355,25 +17457,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Subnet /DeleteTags: @@ -17404,25 +17506,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Tag /DeleteUser: @@ -17449,7 +17551,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - User /DeleteUserGroup: @@ -17483,7 +17585,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /DeleteUserGroupPolicy: @@ -17517,7 +17619,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /DeleteUserPolicy: @@ -17550,7 +17652,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /DeleteVirtualGateway: @@ -17580,7 +17682,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VirtualGateway /DeleteVmGroup: @@ -17611,25 +17713,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VmGroup /DeleteVmTemplate: @@ -17661,7 +17763,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VmTemplate /DeleteVms: @@ -17695,25 +17797,25 @@ paths: CurrentState: shutting-down ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /DeleteVolume: @@ -17742,25 +17844,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Volume /DeleteVpnConnection: @@ -17789,7 +17891,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VpnConnection /DeleteVpnConnectionRoute: @@ -17817,7 +17919,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VpnConnection /DeregisterVmsInLoadBalancer: @@ -17851,7 +17953,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /DisableOutscaleLogin: @@ -17877,7 +17979,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - IdentityProvider /DisableOutscaleLoginForUsers: @@ -17903,7 +18005,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - IdentityProvider /DisableOutscaleLoginPerUsers: @@ -17932,7 +18034,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - IdentityProvider /EnableOutscaleLogin: @@ -17958,7 +18060,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - IdentityProvider /EnableOutscaleLoginForUsers: @@ -17984,7 +18086,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - IdentityProvider /EnableOutscaleLoginPerUsers: @@ -18013,7 +18115,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - IdentityProvider /LinkFlexibleGpu: @@ -18046,7 +18148,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - FlexibleGpu /LinkInternetService: @@ -18076,25 +18178,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - InternetService /LinkLoadBalancerBackendMachines: @@ -18140,7 +18242,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /LinkManagedPolicyToUserGroup: @@ -18173,7 +18275,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /LinkNic: @@ -18205,25 +18307,25 @@ paths: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 LinkNicId: eni-attach-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /LinkPolicy: @@ -18256,7 +18358,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /LinkPrivateIps: @@ -18292,25 +18394,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /LinkPublicIp: @@ -18358,25 +18460,25 @@ paths: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 LinkPublicIpId: eipassoc-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - PublicIp /LinkRouteTable: @@ -18407,25 +18509,25 @@ paths: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 LinkRouteTableId: rtbassoc-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - RouteTable /LinkVirtualGateway: @@ -18460,7 +18562,7 @@ paths: NetToVirtualGatewayLink: State: attached NetId: vpc-12345678 - description: '' + description: The HTTP 200 response (OK). tags: - VirtualGateway /LinkVolume: @@ -18491,25 +18593,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Volume /PutUserGroupPolicy: @@ -18545,7 +18647,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /PutUserPolicy: @@ -18580,7 +18682,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadAccessKeys: @@ -18618,7 +18720,7 @@ paths: ExpirationDate: 2063-04-05T00:00:00.000+0000 LastModificationDate: 2010-10-01T12:34:56.789+0000 Tag: Group1 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -18657,7 +18759,7 @@ paths: AccountId: '123456789012' CustomerId: '87654321' Email: example@example.com - description: '' + description: The HTTP 200 response (OK). tags: - Account /ReadAdminPassword: @@ -18692,25 +18794,25 @@ paths: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 AdminPassword: ... - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /ReadApiAccessPolicy: @@ -18741,25 +18843,25 @@ paths: ApiAccessPolicy: RequireTrustedEnv: false MaxAccessKeyExpirationSeconds: 0 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -18798,7 +18900,7 @@ paths: CaIds: [] Cns: [] Description: Allows all IPv4 domain - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -18856,7 +18958,7 @@ paths: QueryHeaderSize: 287 QueryDate: '2017-05-10T12:34:56.789Z' QueryHeaderRaw: 'Host: api.eu-west-2.outscale.com\nAccept: */*\nConnection: close\nUser-Agent: oAPI CLI v0.1 - 2018-09-28\nX-Osc-Date: 20170510T000000Z\nContent-Type: application/json; charset=utf-8\nAuthorization: *****\nContent-Length: 2\nAccept-Encoding: gzip, deflate\nX-Forwarded-For: 192.0.2.0' - description: '' + description: The HTTP 200 response (OK). tags: - ApiLog /ReadCO2EmissionAccount: @@ -18898,7 +19000,7 @@ paths: FactorDistribution: - Value: 1.2345 Factor: electricity - description: '' + description: The HTTP 200 response (OK). tags: - Account /ReadCas: @@ -18931,7 +19033,7 @@ paths: - Description: CA example CaId: ca-fedcba0987654321fedcba0987654321 CaFingerprint: 1234567890abcdef1234567890abcdef12345678 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -18969,7 +19071,7 @@ paths: Category: compute Service: TinaOS-FCU Operation: RunInstances-OD - description: '' + description: The HTTP 200 response (OK). tags: - Catalog /ReadCatalogs: @@ -19010,7 +19112,7 @@ paths: Category: compute Service: TinaOS-FCU Operation: RunInstances-OD - description: '' + description: The HTTP 200 response (OK). tags: - Catalog /ReadClientGateways: @@ -19054,7 +19156,7 @@ paths: ClientGatewayId: cgw-12345678 ConnectionType: ipsec.1 PublicIp: 192.0.2.0 - description: '' + description: The HTTP 200 response (OK). tags: - ClientGateway /ReadConsoleOutput: @@ -19087,25 +19189,25 @@ paths: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 ConsoleOutput: ... - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /ReadConsumptionAccount: @@ -19191,7 +19293,7 @@ paths: UnitPrice: 0.18 Price: 267.84 ResourceId: i-87654321 - description: '' + description: The HTTP 200 response (OK). tags: - Account /ReadDedicatedGroups: @@ -19238,25 +19340,25 @@ paths: Name: dedicated-group-example SubregionName: eu-west-2a DedicatedGroupId: ded-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - DedicatedGroup /ReadDhcpOptions: @@ -19304,7 +19406,7 @@ paths: DomainNameServers: - 192.0.2.0 - 198.51.100.0 - description: '' + description: The HTTP 200 response (OK). tags: - DhcpOption /ReadDirectLinkInterfaces: @@ -19347,7 +19449,7 @@ paths: State: available InterfaceType: private Location: PAR1 - description: '' + description: The HTTP 200 response (OK). tags: - DirectLinkInterface /ReadDirectLinks: @@ -19384,7 +19486,7 @@ paths: Location: PAR1 RegionName: eu-west-2 State: available - description: '' + description: The HTTP 200 response (OK). tags: - DirectLink /ReadEntitiesLinkedToPolicy: @@ -19443,7 +19545,7 @@ paths: - Id: ABCDEFGHIJKLMNOPQRSTUVWXYZ12345 Name: example-user Orn: orn:ows:idauth::012345678910:user/example/user-example - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadFlexibleGpuCatalog: @@ -19476,7 +19578,7 @@ paths: MaxCpu: 80 MaxRam: 512 ModelName: nvidia-p100 - description: '' + description: The HTTP 200 response (OK). security: [] tags: - FlexibleGpu @@ -19523,7 +19625,7 @@ paths: SubregionName: eu-west-2a VmId: i-12345678 Tags: [] - description: '' + description: The HTTP 200 response (OK). tags: - FlexibleGpu /ReadImageExportTasks: @@ -19563,7 +19665,7 @@ paths: DiskImageFormat: qcow2 State: pending/queued Progress: 0 - description: '' + description: The HTTP 200 response (OK). tags: - Image /ReadImages: @@ -19696,25 +19798,25 @@ paths: FileLocation: Outscale/RockyLinux-2010.10.01-0 Architecture: x86_64 ImageName: RockyLinux-2010.10.01-0 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Image /ReadInternetServices: @@ -19760,25 +19862,25 @@ paths: State: available NetId: vpc-12345678 InternetServiceId: igw-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - InternetService /ReadKeypairs: @@ -19812,25 +19914,25 @@ paths: KeypairName: keypair-example KeypairId: key-abcdef1234567890abcdef1234567890 KeypairFingerprint: 11:22:33:44:55:66:77:88:99:00:aa:bb:cc:dd:ee:ff - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Keypair /ReadLinkedPolicies: @@ -19870,7 +19972,7 @@ paths: PolicyId: ABCDEFGHIJKLMNOPQRSTUVWXYZ01234 MaxResultsLimit: 30 MaxResultsTruncated: false - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadListenerRules: @@ -19908,7 +20010,7 @@ paths: ListenerId: 123456 HostNamePattern: '*.example.com' ListenerRuleId: 1234 - description: '' + description: The HTTP 200 response (OK). tags: - Listener /ReadLoadBalancerTags: @@ -19940,7 +20042,7 @@ paths: Key: key1 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /ReadLoadBalancers: @@ -20005,7 +20107,7 @@ paths: LoadBalancerPort: 443 LoadBalancerProtocol: HTTPS LoadBalancerName: private-lb-example - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /ReadLocations: @@ -20038,7 +20140,7 @@ paths: Code: PAR1 - Name: Equinix Pantin, France Code: PAR4 - description: '' + description: The HTTP 200 response (OK). security: [] tags: - Location @@ -20081,7 +20183,7 @@ paths: Orn: orn:ows:idauth::012345678910:policy/example/example-user-policy PolicyId: ABCDEFGHIJKLMNOPQRSTUVWXYZ01234 PolicyName: example-policy - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadNatServices: @@ -20127,25 +20229,25 @@ paths: PublicIp: 192.0.2.0 NetId: vpc-12345678 State: available - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NatService /ReadNetAccessPointServices: @@ -20206,7 +20308,7 @@ paths: - 192.0.2.0 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). security: [] tags: - NetAccessPoint @@ -20251,7 +20353,7 @@ paths: State: available NetId: vpc-12345678 ServiceName: com.outscale.eu-west-2.oos - description: '' + description: The HTTP 200 response (OK). tags: - NetAccessPoint /ReadNetPeerings: @@ -20302,25 +20404,25 @@ paths: IpRange: 10.0.0.0/16 AccountId: '123456789012' NetPeeringId: pcx-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NetPeering /ReadNets: @@ -20361,25 +20463,25 @@ paths: Tenancy: default NetId: vpc-12345678 State: available - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Net /ReadNics: @@ -20441,25 +20543,25 @@ paths: - PrivateDnsName: ip-10-0-0-4.eu-west-2.compute.internal PrivateIp: 10.0.0.4 IsPrimary: true - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /ReadPolicies: @@ -20505,7 +20607,7 @@ paths: LastModificationDate: 2010-10-01T12:34:56.789+0000 MaxResultsLimit: 30 MaxResultsTruncated: false - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadPolicy: @@ -20543,7 +20645,7 @@ paths: LastModificationDate: 2010-10-01T12:34:56.789+0000 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadPolicyVersion: @@ -20576,7 +20678,7 @@ paths: DefaultVersion: true CreationDate: 2010-10-01T12:34:56.789+0000 Body: '{"Statement": [ {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]} ]}' - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadPolicyVersions: @@ -20612,7 +20714,7 @@ paths: CreationDate: 2010-10-01T12:34:56.789+0000 Body: '{"Statement": [ {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]} ]}' HasMoreItems: true - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadProductTypes: @@ -20644,7 +20746,7 @@ paths: ProductTypes: - ProductTypeId: '0001' Description: Linux - description: '' + description: The HTTP 200 response (OK). security: [] tags: - ProductType @@ -20680,7 +20782,7 @@ paths: Category: compute Service: TinaOS-FCU Operation: RunInstances-OD - description: '' + description: The HTTP 200 response (OK). security: [] tags: - PublicCatalog @@ -20710,7 +20812,7 @@ paths: PublicIps: - 198.51.100.0/24 - 203.0.113.0/24 - description: '' + description: The HTTP 200 response (OK). security: [] tags: - PublicIp @@ -20756,25 +20858,25 @@ paths: NicAccountId: '123456789012' NicId: eni-12345678 PrivateIp: 10.0.0.4 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - PublicIp /ReadQuotas: @@ -20847,7 +20949,7 @@ paths: UsedValue: 1 Name: other_example_limit QuotaType: vpc-12345678 - description: '' + description: The HTTP 200 response (OK). tags: - Quota /ReadRegions: @@ -20882,7 +20984,7 @@ paths: Endpoint: api.us-east-2.outscale.com - RegionName: us-west-1 Endpoint: api.us-west-1.outscale.com - description: '' + description: The HTTP 200 response (OK). security: [] tags: - Region @@ -20935,25 +21037,25 @@ paths: RouteTableId: rtb-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - RouteTable /ReadSecurityGroups: @@ -21009,25 +21111,25 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - SecurityGroup /ReadServerCertificates: @@ -21061,7 +21163,7 @@ paths: Name: server-cert-example ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - ServerCertificate /ReadSnapshotExportTasks: @@ -21098,10 +21200,10 @@ paths: DiskImageFormat: qcow2 State: pending SnapshotId: snap-12345678 - Progress: 99 + Progress: 13 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Snapshot /ReadSnapshots: @@ -21170,25 +21272,25 @@ paths: Key: env ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Snapshot /ReadSubnets: @@ -21236,25 +21338,25 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Subnet /ReadSubregions: @@ -21314,7 +21416,7 @@ paths: LocationCode: PAR4 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Subregion /ReadTags: @@ -21350,25 +21452,25 @@ paths: Key: key1 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Tag /ReadUnitPrice: @@ -21404,7 +21506,7 @@ paths: Operation: CreateVolume Type: BSU:VolumeIOPS:io1 Service: TinaOS-FCU - description: '' + description: The HTTP 200 response (OK). tags: - Catalog /ReadUserGroup: @@ -21446,7 +21548,7 @@ paths: UserEmail: user@example.com UserId: ABCDEFGHIJKLMNOPQRSTUVWXYZ12345 UserName: example-user - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /ReadUserGroupPolicies: @@ -21482,7 +21584,7 @@ paths: Policies: - Body: '{"Statement": [ {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]} ]}' Name: example-policy - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadUserGroupPolicy: @@ -21514,7 +21616,7 @@ paths: Policy: Body: '{"Statement": [ {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]} ]}' Name: example-policy - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadUserGroups: @@ -21558,7 +21660,7 @@ paths: Orn: orn:ows:idauth::012345678910:usergroup/example/usergroup-example Path: /example/ UserGroupId: ug-12345678 - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /ReadUserGroupsPerUser: @@ -21593,7 +21695,7 @@ paths: Orn: orn:ows:idauth::012345678910:usergroup/example/usergroup-example Path: /example/ UserGroupId: ug-12345678 - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /ReadUserPolicies: @@ -21622,7 +21724,7 @@ paths: - example-policy ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadUserPolicy: @@ -21651,7 +21753,7 @@ paths: PolicyDocument: '{"Statement": [ {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]} ]}' PolicyName: example-user-policy UserName: example-user - description: '' + description: The HTTP 200 response (OK). tags: - Policy /ReadUsers: @@ -21687,7 +21789,7 @@ paths: Path: /documentation/ ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - User /ReadVirtualGateways: @@ -21742,7 +21844,7 @@ paths: Tags: [] ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VirtualGateway /ReadVmGroups: @@ -21790,25 +21892,25 @@ paths: Tags: - Value: value1 Key: key1 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VmGroup /ReadVmTemplates: @@ -21859,7 +21961,7 @@ paths: Ram: 2 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VmTemplate /ReadVmTypes: @@ -21895,7 +21997,7 @@ paths: VcoreCount: 1 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). security: [] tags: - Vm @@ -22004,25 +22106,25 @@ paths: PrivateDnsName: ip-10-0-0-4.eu-west-2.compute.internal ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /ReadVmsHealth: @@ -22059,7 +22161,7 @@ paths: StateReason: ELB State: DOWN Description: Instance registration is pending - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /ReadVmsState: @@ -22109,25 +22211,76 @@ paths: MaintenanceEvents: [] ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). + tags: + - Vm + /ReadVmsStopHistory: + post: + description: Lists the stop history of one or more VMs. + operationId: ReadVmsStopHistory + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ReadVmsStopHistoryRequest' + examples: + ex1: + value: + Filters: + VmIds: + - i-12345678 + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ReadVmsStopHistoryResponse' + examples: + ex1: + value: + VmsStopHistory: + - VmId: i-12345678 + StopDate: '2017-05-10T12:34:56.789Z' + StateReason: Server.InternalError + ResponseContext: + RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 + description: The HTTP 200 response (OK). + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: The HTTP 400 response (Bad Request). + '401': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: The HTTP 401 response (Unauthorized). + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /ReadVolumeUpdateTasks: @@ -22174,25 +22327,25 @@ paths: Progress: 100 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). summary: Lists one or more update tasks of volumes tags: - Volume @@ -22245,25 +22398,25 @@ paths: Size: 10 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Volume /ReadVpnConnections: @@ -22315,7 +22468,7 @@ paths: VpnConnectionId: vpn-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VpnConnection /RebootVms: @@ -22345,25 +22498,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /RegisterVmsInLoadBalancer: @@ -22398,7 +22551,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /RejectNetPeering: @@ -22427,31 +22580,31 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '409': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 409 response (Conflict). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - NetPeering /RemoveUserFromUserGroup: @@ -22481,7 +22634,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /ScaleDownVmGroup: @@ -22515,25 +22668,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VmGroup /ScaleUpVmGroup: @@ -22567,25 +22720,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VmGroup /SetDefaultPolicyVersion: @@ -22619,7 +22772,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /StartVms: @@ -22653,25 +22806,25 @@ paths: CurrentState: pending ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /StopVms: @@ -22705,25 +22858,25 @@ paths: CurrentState: stopping ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /UnlinkFlexibleGpu: @@ -22752,7 +22905,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - FlexibleGpu /UnlinkInternetService: @@ -22782,25 +22935,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - InternetService /UnlinkLoadBalancerBackendMachines: @@ -22844,7 +22997,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /UnlinkManagedPolicyFromUserGroup: @@ -22877,7 +23030,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /UnlinkNic: @@ -22906,25 +23059,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /UnlinkPolicy: @@ -22957,7 +23110,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - Policy /UnlinkPrivateIps: @@ -22987,25 +23140,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /UnlinkPublicIp: @@ -23036,25 +23189,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - PublicIp /UnlinkRouteTable: @@ -23083,25 +23236,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - RouteTable /UnlinkVirtualGateway: @@ -23131,7 +23284,7 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VirtualGateway /UnlinkVolume: @@ -23160,25 +23313,25 @@ paths: value: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Volume /UpdateAccessKey: @@ -23240,7 +23393,7 @@ paths: CreationDate: 2010-10-01T12:34:56.789+0000 LastModificationDate: 2017-05-10T12:34:56.789+0000 Tag: Group1 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -23285,7 +23438,7 @@ paths: AccountId: '123456789012' CustomerId: '87654321' Email: example@example.com - description: '' + description: The HTTP 200 response (OK). tags: - Account /UpdateApiAccessPolicy: @@ -23348,25 +23501,25 @@ paths: ApiAccessPolicy: RequireTrustedEnv: false MaxAccessKeyExpirationSeconds: 0 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -23411,7 +23564,7 @@ paths: CaIds: [] Cns: [] Description: Allows all IPv4 domain - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -23446,7 +23599,7 @@ paths: Description: New description CaId: ca-fedcba0987654321fedcba0987654321 CaFingerprint: 1234567890abcdef1234567890abcdef12345678 - description: '' + description: The HTTP 200 response (OK). security: - ApiKeyAuthSec: [] - BasicAuth: [] @@ -23487,25 +23640,25 @@ paths: Name: New-dedicated-group-name SubregionName: eu-west-2a DedicatedGroupId: ded-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - DedicatedGroup /UpdateDirectLinkInterface: @@ -23547,7 +23700,7 @@ paths: State: available InterfaceType: private Location: PAR1 - description: '' + description: The HTTP 200 response (OK). tags: - DirectLinkInterface /UpdateFlexibleGpu: @@ -23583,7 +23736,7 @@ paths: State: allocated SubregionName: eu-west-2a Tags: [] - description: '' + description: The HTTP 200 response (OK). tags: - FlexibleGpu /UpdateImage: @@ -23762,25 +23915,25 @@ paths: FileLocation: 123456789012/image-example Architecture: x86_64 ImageName: image-example - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Image /UpdateListenerRule: @@ -23819,7 +23972,7 @@ paths: ListenerId: 123456 HostNamePattern: '*.newhost.com' ListenerRuleId: 1234 - description: '' + description: The HTTP 200 response (OK). tags: - Listener /UpdateLoadBalancer: @@ -24049,7 +24202,7 @@ paths: LoadBalancerPort: 443 LoadBalancerProtocol: HTTPS LoadBalancerName: private-lb-example - description: '' + description: The HTTP 200 response (OK). tags: - LoadBalancer /UpdateNet: @@ -24084,25 +24237,25 @@ paths: Tenancy: default NetId: vpc-12345678 State: available - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Net /UpdateNetAccessPoint: @@ -24162,7 +24315,7 @@ paths: State: available NetId: vpc-12345678 ServiceName: com.outscale.eu-west-2.oos - description: '' + description: The HTTP 200 response (OK). tags: - NetAccessPoint /UpdateNic: @@ -24293,25 +24446,25 @@ paths: - PrivateDnsName: ip-10-0-0-4.eu-west-2.compute.internal PrivateIp: 10.0.0.4 IsPrimary: true - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Nic /UpdateRoute: @@ -24371,25 +24524,25 @@ paths: RouteTableId: rtb-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Route /UpdateRoutePropagation: @@ -24432,7 +24585,7 @@ paths: RouteTableId: rtb-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VirtualGateway /UpdateRouteTableLink: @@ -24463,25 +24616,25 @@ paths: ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 LinkRouteTableId: rtbassoc-12345678 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - RouteTable /UpdateServerCertificate: @@ -24513,7 +24666,7 @@ paths: Name: new-name ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - ServerCertificate /UpdateSnapshot: @@ -24639,25 +24792,25 @@ paths: Tags: [] ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Snapshot /UpdateSubnet: @@ -24694,25 +24847,25 @@ paths: NetId: vpc-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Subnet /UpdateUser: @@ -24749,7 +24902,7 @@ paths: Path: /product/ ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - User /UpdateUserGroup: @@ -24793,7 +24946,7 @@ paths: UserEmail: user@example.com UserId: ABCDEFGHIJKLMNOPQRSTUVWXYZ12345 UserName: example-user - description: '' + description: The HTTP 200 response (OK). tags: - UserGroup /UpdateVm: @@ -24833,7 +24986,7 @@ paths: value: Vm: VmType: tinav5.c2r2p2 - VmInitiatedShutdownBehavior: stop + VmInitiatedShutdownBehavior: restart State: stopped StateReason: '' RootDeviceType: ebs @@ -24898,6 +25051,9 @@ paths: PrivateDnsName: ip-10-0-0-4.eu-west-2.compute.internal ActionsOnNextBoot: SecureBoot: none + ShutdownBehaviorConfiguration: + HostAction: restart + GuestAction: stop ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 ex2: @@ -24969,27 +25125,30 @@ paths: PrivateDnsName: ip-10-0-0-4.eu-west-2.compute.internal ActionsOnNextBoot: SecureBoot: none + ShutdownBehaviorConfiguration: + HostAction: restart + GuestAction: stop ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Vm /UpdateVmGroup: @@ -25042,25 +25201,25 @@ paths: Tags: [] ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VmGroup /UpdateVmTemplate: @@ -25104,7 +25263,7 @@ paths: Ram: 2 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). tags: - VmTemplate /UpdateVolume: @@ -25173,25 +25332,25 @@ paths: TaskId: vol-update-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - Volume /UpdateVpnConnection: @@ -25237,25 +25396,25 @@ paths: VpnConnectionId: vpn-12345678 ResponseContext: RequestId: 0475ca1e-d0c5-441d-712a-da55a4175157 - description: '' + description: The HTTP 200 response (OK). '400': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 400 response (Bad Request). '401': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 401 response (Unauthorized). '500': content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - description: '' + description: The HTTP 500 response (Internal Server Error). tags: - VpnConnection security: diff --git a/osc_sdk_python/resources/osc/cfg.yaml b/osc_sdk_python/resources/osc/cfg.yaml new file mode 100644 index 0000000..e34f446 --- /dev/null +++ b/osc_sdk_python/resources/osc/cfg.yaml @@ -0,0 +1,2 @@ +spec: ./api.yaml +overlay: ./patch.yaml diff --git a/osc_sdk_python/resources/osc/patch.yaml b/osc_sdk_python/resources/osc/patch.yaml new file mode 100644 index 0000000..e1afe4d --- /dev/null +++ b/osc_sdk_python/resources/osc/patch.yaml @@ -0,0 +1,899 @@ +overlay: 1.0.0 +info: + title: "Example to indicate how to use the OpenAPI Overlay specification (https://github.com/OAI/Overlay-Specification)" + version: 1.0.0 +actions: +- target: $.components.schemas.Vm + description: Required fields for Vm + update: + required: + - ActionsOnNextBoot + - Architecture + - BlockDeviceMappings + - BootMode + - CreationDate + - DeletionProtection + - Hypervisor + - ImageId + - LaunchNumber + - NestedVirtualization + - Nics + - OsFamily + - Performance + - Placement + - PrivateIp + - ProductCodes + - ReservationId + - RootDeviceName + - RootDeviceType + - SecurityGroups + - State + - StateReason + - Tags + - TpmEnabled + - UserData + - VmId + - VmInitiatedShutdownBehavior + - VmType +- target: $.components.schemas.VmState + description: Rename VmState type + update: + x-rs-name: VmStateInfo +- target: $.components.schemas.Vm.*.State + description: Type for Vm.State + update: + enum: + - pending + - running + - stopping + - stopped + - shutting-down + - terminated + - quarantine +- target: $.components.schemas.FiltersVm.*.VmStateNames.items + description: + update: + x-rs-type: VmState +- target: $.components.schemas.VmStates + description: Required fields for VmStates + update: + required: + - MaintenanceEvents + - SubregionName + - VmId + - VmState +- target: $.components.schemas.VmStates.*.VmState + description: + update: + x-rs-type: VmState +- target: $.components.schemas.FiltersVmsState.*.VmStates.items + description: + update: + x-rs-type: VmState +- target: $.components.schemas.BlockDeviceMappingCreated + description: Required fields for BlockDeviceMappingCreated + update: + required: + - Bsu + - DeviceName +- target: $.components.schemas.BsuCreated + description: Required fields for BsuCreated + update: + required: + - DeleteOnVmDeletion + - LinkDate + - State + - VolumeId +- target: $.components.schemas.Nic + description: Required fields for Nic + update: + required: + - AccountId + - Description + - IsSourceDestChecked + - MacAddress + - NetId + - NicId + - PrivateDnsName + - PrivateIps + - SecurityGroups + - State + - SubnetId + - SubregionName + - Tags +- target: $.components.schemas.NicLight + description: Required fields for NicLight + update: + required: + - AccountId + - Description + - IsSourceDestChecked + - MacAddress + - NetId + - NicId + - PrivateDnsName + - PrivateIps + - SecurityGroups + - State + - SubnetId +- target: $.components.schemas.LinkNic + description: Required fields for LinkNic + update: + required: + - LinkNicId + - DeleteOnVmDeletion + - DeviceNumber + - MacAddress + - LinkNicId + - State + - VmAccountId + - VmId +- target: $.components.schemas.LinkNicLight + description: Required fields for LinkNicLight + update: + required: + - DeleteOnVmDeletion + - DeviceNumber + - MacAddress + - LinkNicId + - State +- target: $.components.schemas.LinkPublicIp + description: Required fields for LinkPublicIp + update: + required: + - LinkPublicIpId + - PublicIpId + - PublicDnsName + - PublicIp + - PublicIpAccountId +- target: $.components.schemas.LinkPublicIpLightForVm + description: Required fields for LinkPublicIpLightForVm + update: + required: + - PublicDnsName + - PublicIp + - PublicIpAccountId +- target: $.components.schemas.PrivateIp + description: Required fields for PrivateIp + update: + required: + - IsPrimary + - PrivateDnsName + - PrivateIp +- target: $.components.schemas.PrivateIpLight + description: Required fields for PrivateIpLight + update: + required: + - IsPrimary + - PrivateDnsName + - PrivateIp +- target: $.components.schemas.PrivateIpLightForVm + description: Required fields for PrivateIpLightForVm + update: + required: + - IsPrimary + - PrivateDnsName + - PrivateIp +- target: $.components.schemas.Placement + description: Required fields for Placement + update: + required: + - SubregionName + - Tenancy +- target: $.components.schemas.SecurityGroupLight + description: Required fields for SecurityGroupLight + update: + required: + - SecurityGroupId + - SecurityGroupName +- target: $.components.schemas.CreateVmsRequest.*.*[?(@.type == 'array')] + description: Remove pointer from CreateVmsRequest attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.UpdateVmRequest.*.*[?(@.type == 'array')] + description: Remove pointer from UpdateVmRequest attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.BsuToUpdateVm + description: Required fields for BsuToUpdateVm + update: + required: + - DeleteOnVmDeletion + - VolumeId +- target: $.components.schemas.SecurityGroup + description: Required fields for SecurityGroup + update: + required: + - AccountId + - Description + - InboundRules + - OutboundRules + - SecurityGroupId + - SecurityGroupName + - Tags +- target: $.components.schemas.SecurityGroupRule.*.* + description: Remove pointer from SecurityGroupRule attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.SecurityGroupsMember + description: Required fields for SecurityGroupsMember + update: + required: + - SecurityGroupId +- target: $.components.schemas.CreateSecurityGroupRuleRequest.*.Rules + description: Remove pointer from CreateSecurityGroupRuleRequest attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.DeleteSecurityGroupRuleRequest.*.Rules + description: Remove pointer from DeleteSecurityGroupRuleRequest attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.SecurityGroupsMember + description: Required fields for SecurityGroupsMember + update: + required: + - SecurityGroupId +- target: $.components.schemas.Net + description: Required fields for Net + update: + required: + - DhcpOptionsSetId + - IpRange + - NetId + - State + - Tags + - Tenancy +- target: $.components.schemas.Net.*.State + description: Type for Net.State + update: + enum: + - pending + - available + - deleting +- target: $.components.schemas.FiltersNet.*.States.items + description: + update: + x-rs-type: NetState +- target: $.components.schemas.Subnet + description: Required fields for Subnet + update: + required: + - AvailableIpsCount + - IpRange + - MapPublicIpOnLaunch + - NetId + - State + - SubnetId + - SubregionName + - Tags +- target: $.components.schemas.Subnet.*.State + description: Type for Subnet.State + update: + enum: + - pending + - available + - deleted +- target: $.components.schemas.FiltersSubnet.*.States.items + description: + update: + x-rs-type: SubnetState +- target: $.components.schemas.InternetService + description: Required fields for InternetService + update: + required: + - InternetServiceId + - NetId + - State + - Tags +- target: $.components.schemas.NatService + description: Required fields for NatService + update: + required: + - NatServiceId + - NetId + - PublicIps + - State + - SubnetId + - Tags +- target: $.components.schemas.NatService.*.State + description: Type for NatService.State + update: + enum: + - pending + - available + - deleting + - deleted +- target: $.components.schemas.FiltersNatService.*.States.items + description: + update: + x-rs-type: NatServiceState +- target: $.components.schemas.NetPeering + description: Required fields for NetPeering + update: + required: + - AccepterNet + - NetPeeringId + - SourceNet + - State + - Tags +- target: $.components.schemas.NetPeeringState + description: Required fields for NetPeeringState + update: + required: + - Message + - Name +- target: $.components.schemas.NetPeeringState.*.Name + description: Type for NetPeeringState.Name + update: + enum: + - pending-acceptance + - active + - rejected + - failed + - expired + - deleted +- target: $.components.schemas.FiltersNetPeering.*.StateNames.items + description: + update: + x-rs-type: NetPeeringStateName +- target: $.components.schemas.Image + description: Required fields for Image + update: + required: + - AccountId + - Architecture + - BlockDeviceMapping + - CreationDate + - ImageId + - State + - BootModes + - RootDeviceType + - SecureBoot + - Tags +- target: $.components.schemas.Image.*.State + description: Type for Image.State + update: + enum: + - pending + - available + - failed +- target: $.components.schemas.FiltersImage.*.States.items + description: + update: + x-rs-type: ImageState +- target: $.components.schemas.Snapshot + description: Required fields for Snapshot + update: + required: + - CreationDate + - SnapshotId + - State + - AccountId + - VolumeId + - VolumeSize +- target: $.components.schemas.Snapshot.*.State + description: Type for Snapshot.State + update: + enum: + - in-queue + - pending + - completed + - error + - deleting +- target: $.components.schemas.FiltersSnapshot.*.States.items + description: + update: + x-rs-type: SnapshotState +- target: $.components.schemas.SnapshotExportTask + description: Required fields for SnapshotExportTask + update: + required: + - Comment + - OsuExport + - Progress + - SnapshotId + - State + - Tags + - TaskId +- target: $.components.schemas.SnapshotExportTask.*.State + description: Type for SnapshotExportTask.State + update: + enum: + - pending + - active + - completed + - cancelled + - failed +- target: $.components.schemas.ErrorResponse + description: Required fields for ErrorResponse + update: + required: + - Errors +- target: $.components.schemas.Errors + description: Required fields for Errors + update: + required: + - Code + - Details + - Type +- target: $.components.schemas.BackendVmHealth.*.State + description: Type for BackendVmHealth.State + update: + enum: + - InService + - OutOfService + - Unknown +- target: $.components.schemas.Volume + description: Required fields for Volume + update: + required: + - VolumeId + - CreationDate + - Iops + - Size + - State + - SubregionName + - Tags + - VolumeType + - LinkedVolumes +- target: $.components.schemas + description: Create type VolumeType + update: + VolumeType: + type: string + enum: + - io1 + - gp2 + - standard +- target: $.components.schemas.Volume.*.VolumeType + description: Type for Volume.VolumeType + update: + x-rs-type: VolumeType +- target: $.components.schemas.FiltersImage.*.BlockDeviceMappingVolumeTypes.items + description: + update: + $ref: "#/components/schemas/VolumeType" +- target: $.components.schemas.FiltersVolume.*.VolumeTypes.items + description: + update: + $ref: "#/components/schemas/VolumeType" +- target: $.components.schemas.CreateVolumeRequest.*.VolumeType + description: + update: + x-rs-type: VolumeType +- target: $.components.schemas.UpdateVolumeRequest.*.VolumeType + description: + update: + x-rs-type: VolumeType +- target: $.components.schemas.BsuToCreate.*.VolumeType + description: + update: + x-rs-type: VolumeType +- target: $.components.schemas.Volume.*.State + description: Type for Volume.State + update: + enum: + - creating + - available + - in-use + - deleting + - error +- target: $.components.schemas.FiltersVolume.*.VolumeStates.items + description: + update: + x-rs-type: VolumeState +- target: $.components.schemas.BlockDeviceMappingCreated + description: Required fields for BlockDeviceMappingCreated + update: + required: + - Bsu + - DeviceName +- target: $.components.schemas.BsuCreated + description: Required fields for BsuCreated + update: + required: + - VolumeId + - DeleteOnVmDeletion + - LinkDate + - State +- target: $.components.schemas.BsuCreated.*.State + description: + update: + x-rs-type: LinkedVolumeState +- target: $.components.schemas.LinkedVolume + description: Required fields for LinkedVolume + update: + required: + - DeleteOnVmDeletion + - DeviceName + - State + - VmId + - VolumeId +- target: $.components.schemas.LinkedVolume.*.State + description: Type for LinkedVolume.State + update: + enum: + - attaching + - detaching + - attached + - detached +- target: $.components.schemas.FiltersVolume.*.LinkVolumeLinkStates.items + description: + update: + x-rs-type: LinkedVolumeState +- target: $.components.schemas.FlexibleGpu + description: Required fields for FlexibleGpu + update: + required: + - DeleteOnVmDeletion + - FlexibleGpuId + - Generation + - ModelName + - State + - SubregionName +- target: $.components.schemas.FlexibleGpu.*.State + description: Type for FlexibleGpu.State + update: + enum: + - allocated + - attaching + - attached + - detaching +- target: $.components.schemas.FiltersFlexibleGpu.*.States.items + description: + update: + x-rs-type: FlexibleGpuState +- target: $.components.schemas.NetAccessPoint.*.State + description: Type for NetAccessPoint.State + update: + enum: + - pending + - available + - deleting + - deleted +- target: $.components.schemas.FiltersNetAccessPoint.*.States.items + description: + update: + x-rs-type: NetAccessPointState +- target: $.components.schemas.AccessKey.*.State + description: Type for AccessKey.State + update: + enum: + - ACTIVE + - INACTIVE +- target: $.components.schemas.FiltersAccessKeys.*.States.items + description: + update: + x-rs-type: AccessKeyState +- target: $.components.schemas.Nic.*.State + description: Type for Nic.State + update: + enum: + - available + - attaching + - in-use + - detaching +- target: $.components.schemas.NicLight.*.State + description: + update: + x-rs-type: NicState +- target: $.components.schemas.FiltersNic.*.States.items + description: + update: + x-rs-type: NicState +- target: $.components.schemas.LinkNic.*.State + description: Type for LinkNic.State + update: + enum: + - attaching + - attached + - detaching + - detached +- target: $.components.schemas.FiltersLoadBalancer.*.States.items + description: + update: + x-rs-type: LoadBalancerState +- target: $.components.schemas.LoadBalancer.*.State + description: Type for LoadBalancer.State + update: + enum: + - provisioning + - starting + - reloading + - active + - reconfiguring + - deleting + - deleted +- target: $.components.schemas.LinkNicLight.*.State + description: + update: + x-rs-type: LinkNicState +- target: $.components.schemas.PublicIp + description: Required fields for PublicIp + update: + required: + - PublicIpId + - PublicIp + - Tags +- target: $.components.schemas.PublicIpLight + description: Required fields for PublicIpLight + update: + required: + - PublicIpId + - PublicIp +- target: $.components.schemas.LinkPublicIpLightForVm + description: Required fields for LinkPublicIpLightForVm + update: + required: + - PublicDnsName + - PublicIp +- target: $.components.schemas.PrivateIpLightForVm + description: Required fields for PrivateIpLightForVm + update: + required: + - IsPrimary + - PrivateDnsName + - PrivateIp +- target: $.components.schemas.Listener + description: Required fields for Listener + update: + required: + - BackendPort + - BackendProtocol + - LoadBalancerPort + - LoadBalancerProtocol +- target: $.components.schemas.Listener.*.PolicyNames + description: Remove pointer from Listener attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.AccessLog + description: Required fields for AccessLog + update: + required: + - IsEnabled +- target: $.components.schemas.LoadBalancer + description: Required fields for LoadBalancer + update: + required: + - State + - AccessLog + - ApplicationStickyCookiePolicies + - BackendIps + - BackendVmIds + - DnsName + - HealthCheck + - Listeners + - LoadBalancerName + - LoadBalancerStickyCookiePolicies + - LoadBalancerType + - SecuredCookies + - SecurityGroups + - SourceSecurityGroup + - SubregionNames + - Tags +- target: $.components.schemas.LoadBalancer.*.Subnets + description: Remove pointer from LoadBalancer attributes + update: + x-rs-type-skip-optional-pointer: true +- target: $.components.schemas.LoadBalancerTag + description: Required fields for LoadBalancerTag + update: + required: + - LoadBalancerName + - Key + - Value +- target: $.components.schemas.Tag + description: Required fields for Tag + update: + required: + - ResourceId + - ResourceType + - Key + - Value +- target: $.components.schemas.Tag.*.ResourceType + description: Enum for Tag.ResourceType + update: + enum: + - customer-gateway + - dhcpoptions + - flexible-gpu + - image + - instance + - keypair + - natgateway + - network-interface + - public-ip + - route-table + - security-group + - snapshot + - subnet + - task + - virtual-private-gateway + - volume + - vpc + - vpc-endpoint + - vpc-peering-connection + - vpn-connection + x-enum-varnames: + - ClientGateway + - DHCPOptions + - flexible-gpu + - image + - vm + - keypair + - NatServiceOrNetAccessPoint + - nic + - public-ip + - route-table + - security-group + - snapshot + - subnet + - task + - VirtualGateway + - volume + - Net + - NetEndpoint + - NetPeering + - vpn-connection +- target: $.components.schemas.RouteTable + description: Required fields for RouteTable + update: + required: + - LinkRouteTables + - NetId + - RoutePropagatingVirtualGateways + - RouteTableId + - Routes + - Tags +- target: $.components.schemas.LinkRouteTable + description: Required fields for LinkRouteTable + update: + required: + - LinkRouteTableId + - Main + - NetId + - RouteTableId + - SubnetId +- target: $.components.schemas.Route + description: Required fields for Route + update: + required: + - CreationMethod + - DestinationIpRange + - State +- target: $.components.schemas.RouteLight + description: Required fields for RouteLight + update: + required: + - DestinationIpRange + - RouteType + - State +- target: $.components.schemas.VirtualGateway + description: Required fields for VirtualGateway + update: + required: + - ConnectionType + - NetToVirtualGatewayLinks + - State + - Tags + - VirtualGatewayId +- target: $.components.schemas.ClientGateway + description: Required fields for ClientGateway + update: + required: + - BgpAsn + - ConnectionType + - PublicIp + - State + - Tags + - ClientGatewayId +- target: $.components.schemas.ClientGateway.*.State + description: Type for ClientGateway.State + update: + enum: + - pending + - available + - deleting + - deleted +- target: $.components.schemas.FiltersClientGateway.*.States.items + description: + update: + x-rs-type: ClientGatewayState +- target: $.components.schemas.VpnConnection + description: Required fields for VpnConnection + update: + required: + - ClientGatewayId + - ConnectionType + - Routes + - VirtualGatewayId + - VgwTelemetries + - StaticRoutesOnly + - State + - Tags + - VpnConnectionId +- target: $.components.schemas.NetAccessPoint + description: Required fields for NetAccessPoint + update: + required: + - NetId + - RouteTableIds + - ServiceName + - State + - Tags + - NetAccessPointId +- target: $.components.schemas.*.*.NextPageToken.format + description: Switch pagination to string + remove: true +- target: $.paths["/DeleteSecurityGroup"].post.responses + update: + '409': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 409 response (Conflict). +- target: $.paths["/DeleteSubnet"].post.responses + update: + '409': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 409 response (Conflict). +- target: $.paths["/CreateLoadBalancer"].post.responses + update: + '409': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 409 response (Conflict). +- target: $.paths["/UpdateLoadBalancer"].post.responses + update: + '409': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 409 response (Conflict). +- target: $.paths["/CreateNatService"].post.responses + update: + '409': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 409 response (Conflict). +- target: $.paths["/UnlinkInternetService"].post.responses + update: + '424': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 424 response (Failed Dependency). +- target: $.paths["/AddUserToUserGroup"].post.responses + update: + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 404 response (Not Found). +- target: $.paths.*.post.responses + update: + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 400 response (Bad Request). +- target: $.paths.*.post.responses + update: + '500': + content: + application/json: + schema: + "$ref": "#/components/schemas/ErrorResponse" + description: The HTTP 500 response (Internal Server Error). diff --git a/osc_sdk_python/retry.py b/osc_sdk_python/retry.py deleted file mode 100644 index bf4979a..0000000 --- a/osc_sdk_python/retry.py +++ /dev/null @@ -1,138 +0,0 @@ -import requests -import time -import random -from requests.exceptions import JSONDecodeError -from .problem import ProblemDecoder, LegacyProblemDecoder, LegacyProblem, Problem - -MAX_RETRIES = 3 -RETRY_BACKOFF_FACTOR = 1.0 -RETRY_BACKOFF_JITTER = 3.0 -RETRY_BACKOFF_MAX = 30.0 - - -class Retry: - """ - Hold a request attempt and try to execute it - """ - - def __init__(self, session: requests.Session, method: str, url: str, **kwargs): - self.session = session - self.method: str = method - self.url: str = url - self.request_kwargs = kwargs - - # Extract all retry parameters - self.attempt: int = int(self.request_kwargs.pop("attempt", 0)) - self.max_retries: int = int( - self.request_kwargs.get("max_retries", MAX_RETRIES) - ) - self.backoff_factor: float = float( - self.request_kwargs.get("backoff_factor", RETRY_BACKOFF_FACTOR) - ) - self.backoff_jitter: float = float( - self.request_kwargs.get("backoff_jitter", RETRY_BACKOFF_JITTER) - ) - self.backoff_max: float = float( - self.request_kwargs.get("backoff_max", RETRY_BACKOFF_MAX) - ) - - def execute_once(self) -> requests.Response: - """ - Execute the request without retry - """ - return self.session.request(self.method, self.url, **self.request_kwargs) - - def increment(self) -> "Retry": - """ - Return a copy of the retry with an incremented attempt count - """ - new_kwargs = self.request_kwargs.copy() - new_kwargs["attempt"] = self.attempt + 1 - return Retry(self.session, self.method, self.url, **new_kwargs) - - def should_retry(self, e: requests.exceptions.RequestException) -> bool: - if isinstance(e, requests.exceptions.TooManyRedirects): - return False - - if isinstance(e, requests.exceptions.URLRequired): - return False - - if isinstance(e, ValueError): - # can be raised on bogus request - return False - - if e.response is not None: - if 400 <= e.response.status_code < 500 and e.response.status_code != 429: - return False - - return self.attempt < self.max_retries - - def get_backoff_time(self) -> float: - """ - {backoff factor} * (2 ** ({number of previous retries})) - random.uniform(0, {backoff jitter}) - """ - - backoff: float = self.backoff_factor * (2**self.attempt) - backoff += random.uniform(0, self.backoff_jitter) - return min(backoff, self.backoff_max) - - def execute(self) -> requests.Response: - try: - res = self.execute_once() - raise_for_status(res) - return res - except requests.exceptions.RequestException as e: - if self.should_retry(e): - sleep_time = self.get_backoff_time() - time.sleep(sleep_time) - return self.increment().execute() - else: - raise e - - -def raise_for_status(response: requests.Response): - http_error_msg = "" - problem = None - reason = get_default_reason(response) - - try: - ct = response.headers.get("content-type") or "" - if "application/json" in ct: - problem = response.json(cls=LegacyProblemDecoder) - problem.status = problem.status or str(response.status_code) - problem.url = response.url - elif "application/problem+json" in ct: - problem = response.json(cls=ProblemDecoder) - problem.status = problem.status or str(response.status_code) - except JSONDecodeError: - pass - else: - if 400 <= response.status_code < 500: - if isinstance(problem, LegacyProblem) or isinstance(problem, Problem): - http_error_msg = f"Client Error --> {problem.msg()}" - else: - http_error_msg = f"{response.status_code} Client Error: {reason} for url: {response.url}" - - elif 500 <= response.status_code < 600: - if isinstance(problem, LegacyProblem) or isinstance(problem, Problem): - http_error_msg = f"Server Error --> {problem.msg()}" - else: - http_error_msg = f"{response.status_code} Server Error: {reason} for url: {response.url}" - - if http_error_msg: - raise requests.HTTPError(http_error_msg, response=response) - - -def get_default_reason(response): - if isinstance(response.reason, bytes): - # We attempt to decode utf-8 first because some servers - # choose to localize their reason strings. If the string - # isn't utf-8, we fall back to iso-8859-1 for all other - # encodings. (See PR #3538) - try: - return response.reason.decode("utf-8") - except UnicodeDecodeError: - return response.reason.decode("iso-8859-1") - else: - return response.reason diff --git a/osc_sdk_python/runtime/__init__.py b/osc_sdk_python/runtime/__init__.py new file mode 100644 index 0000000..f651ba2 --- /dev/null +++ b/osc_sdk_python/runtime/__init__.py @@ -0,0 +1 @@ +"""Shared SDK runtime implementations.""" diff --git a/osc_sdk_python/runtime/call.py b/osc_sdk_python/runtime/call.py new file mode 100644 index 0000000..6b54137 --- /dev/null +++ b/osc_sdk_python/runtime/call.py @@ -0,0 +1,274 @@ +import json +import logging +import warnings +from datetime import timedelta +from urllib.parse import urlsplit + +import httpx + +from ..credentials import Profile +from ..exceptions import ( + SdkError, + SdkResponseError, + SdkTransportError, + SdkValidationError, +) +from .request import RequestSpec +from .transport import ( + AsyncSdkTransport, + DEFAULT_USER_AGENT, + RateLimiter, + RetryPolicy, + SdkAuth, + SdkTransport, +) + +logger = logging.getLogger("osc_sdk_python") + + +def _json_payload(value): + try: + return "" if value is None else json.dumps(value) + except (TypeError, ValueError) as error: + raise SdkValidationError("Request body is not JSON serializable") from error + + +def _decode_json_response(response): + try: + return response.json() + except ValueError as error: + raise SdkResponseError("Response body is not valid JSON") from error + + +class Call(object): + def __init__(self, limiter=None, **kwargs): + self.version = kwargs.pop("version", "latest") + self.host = kwargs.pop("host", None) + self.ssl = kwargs.pop("_ssl", True) + self.user_agent = kwargs.pop("user_agent", DEFAULT_USER_AGENT) + self.limiter: RateLimiter | None = limiter + self.retry_kwargs = {} + + kwargs = self.update_limiter(**kwargs) + kwargs = self.update_retry(**kwargs) + self.update_profile(**kwargs) + self.session = self._make_client() + + def _make_client(self): + return httpx.Client( + verify=not self.profile.tls_skip_verify, + transport=SdkTransport( + limiter=self.limiter, + retry_policy=RetryPolicy(**self.retry_kwargs), + verify=not self.profile.tls_skip_verify, + ), + ) + + def update_credentials(self, **kwargs): + warnings.warn( + "update_credentials is deprecated, use update_profile instead", + DeprecationWarning, + stacklevel=2, + ) + return self.update_profile(**kwargs) + + def update_profile(self, **kwargs): + self.profile = Profile.from_standard_configuration( + kwargs.pop("path", None), kwargs.pop("profile", None) + ) + self.profile.merge(Profile(**kwargs)) + if hasattr(self, "session"): + old_session = self.session + self.session = self._make_client() + old_session.close() + return kwargs + + def update_limiter(self, **kwargs): + limiter_window = kwargs.pop("limiter_window", None) + if limiter_window is not None and self.limiter is not None: + self.limiter.window = timedelta(seconds=int(limiter_window)) + + limiter_max_requests = kwargs.pop("limiter_max_requests", None) + if limiter_max_requests is not None and self.limiter is not None: + self.limiter.max_requests = limiter_max_requests + + return kwargs + + def update_retry(self, **kwargs): + max_retries = kwargs.pop("max_retries", None) + if max_retries is not None: + self.retry_kwargs["max_retries"] = int(max_retries) + + for key in ["backoff_factor", "backoff_jitter", "backoff_max"]: + value = kwargs.pop(f"retry_{key}", None) + if value is not None: + self.retry_kwargs[key] = float(value) + return kwargs + + def request(self, spec: RequestSpec, path_params=None): + path = spec.resolved_path(path_params) + endpoint = ( + self.profile.get_endpoint(spec.service).rstrip("/") + "/" + path.lstrip("/") + ) + uri = urlsplit(endpoint).path + payload = _json_payload(spec.json_body) + + logger.info( + "mode: sync\nservice: %s\nmethod: %s\nuri: %s\npayload:\n%s", + spec.service, + spec.method.upper(), + uri, + json.dumps(spec.json_body, indent=2), + ) + + try: + response = self.session.request( + spec.method.upper(), + endpoint, + content=payload, + params=spec.query_params, + auth=SdkAuth( + self.profile, + service=spec.service, + user_agent=self.user_agent, + ), + ) + except SdkError: + raise + except httpx.HTTPError as error: + raise SdkTransportError( + str(error), + request=getattr(error, "request", None), + response=getattr(error, "response", None), + ) from error + return _decode_json_response(response) + + def api(self, action, service="api", **data): + return self.request( + RequestSpec( + service=service, + method="POST", + path="/" + action, + json_body=data, + ) + ) + + def close(self): + if self.session: + self.session.close() + + +class AsyncCall(object): + def __init__(self, limiter=None, **kwargs): + self.version = kwargs.pop("version", "latest") + self.host = kwargs.pop("host", None) + self.ssl = kwargs.pop("_ssl", True) + self.user_agent = kwargs.pop("user_agent", DEFAULT_USER_AGENT) + self.limiter: RateLimiter | None = limiter + self.retry_kwargs = {} + + kwargs = self.update_limiter(**kwargs) + kwargs = self.update_retry(**kwargs) + self.update_profile(**kwargs) + self.client = self._make_client() + + def _make_client(self): + return httpx.AsyncClient( + verify=not self.profile.tls_skip_verify, + transport=AsyncSdkTransport( + limiter=self.limiter, + retry_policy=RetryPolicy(**self.retry_kwargs), + verify=not self.profile.tls_skip_verify, + ), + ) + + def update_credentials(self, **kwargs): + warnings.warn( + "update_credentials is deprecated, use update_profile instead", + DeprecationWarning, + stacklevel=2, + ) + return self.update_profile(**kwargs) + + def update_profile(self, **kwargs): + self.profile = Profile.from_standard_configuration( + kwargs.pop("path", None), kwargs.pop("profile", None) + ) + self.profile.merge(Profile(**kwargs)) + if hasattr(self, "client"): + self.client = self._make_client() + return kwargs + + def update_limiter(self, **kwargs): + limiter_window = kwargs.pop("limiter_window", None) + if limiter_window is not None and self.limiter is not None: + self.limiter.window = timedelta(seconds=int(limiter_window)) + + limiter_max_requests = kwargs.pop("limiter_max_requests", None) + if limiter_max_requests is not None and self.limiter is not None: + self.limiter.max_requests = limiter_max_requests + + return kwargs + + def update_retry(self, **kwargs): + max_retries = kwargs.pop("max_retries", None) + if max_retries is not None: + self.retry_kwargs["max_retries"] = int(max_retries) + + for key in ["backoff_factor", "backoff_jitter", "backoff_max"]: + value = kwargs.pop(f"retry_{key}", None) + if value is not None: + self.retry_kwargs[key] = float(value) + return kwargs + + async def request(self, spec: RequestSpec, path_params=None): + path = spec.resolved_path(path_params) + endpoint = ( + self.profile.get_endpoint(spec.service).rstrip("/") + "/" + path.lstrip("/") + ) + uri = urlsplit(endpoint).path + payload = _json_payload(spec.json_body) + + logger.info( + "mode: async\nservice: %s\nmethod: %s\nuri: %s\npayload:\n%s", + spec.service, + spec.method.upper(), + uri, + json.dumps(spec.json_body, indent=2), + ) + + try: + response = await self.client.request( + spec.method.upper(), + endpoint, + content=payload, + params=spec.query_params, + auth=SdkAuth( + self.profile, + service=spec.service, + user_agent=self.user_agent, + ), + ) + except SdkError: + raise + except httpx.HTTPError as error: + raise SdkTransportError( + str(error), + request=getattr(error, "request", None), + response=getattr(error, "response", None), + ) from error + return _decode_json_response(response) + + async def api(self, action, service="api", **data): + return await self.request( + RequestSpec( + service=service, + method="POST", + path="/" + action, + json_body=data, + ) + ) + + async def close(self): + if self.client: + await self.client.aclose() diff --git a/osc_sdk_python/runtime/request.py b/osc_sdk_python/runtime/request.py new file mode 100644 index 0000000..bc6fabd --- /dev/null +++ b/osc_sdk_python/runtime/request.py @@ -0,0 +1,28 @@ +from dataclasses import dataclass, field +import re +from urllib.parse import quote + +from ..exceptions import SdkValidationError + + +PATH_PLACEHOLDER_RE = re.compile(r"{([^{}]+)}") + + +@dataclass +class RequestSpec: + service: str + method: str + path: str + json_body: dict | list | None = None + query_params: dict = field(default_factory=dict) + + def resolved_path(self, path_params: dict | None = None) -> str: + path = self.path + for name, value in (path_params or {}).items(): + path = path.replace("{" + name + "}", quote(str(value), safe="")) + missing = PATH_PLACEHOLDER_RE.findall(path) + if missing: + raise SdkValidationError( + "Missing path parameter(s): {}".format(", ".join(sorted(set(missing)))) + ) + return path diff --git a/osc_sdk_python/runtime/transport.py b/osc_sdk_python/runtime/transport.py new file mode 100644 index 0000000..9eeee3e --- /dev/null +++ b/osc_sdk_python/runtime/transport.py @@ -0,0 +1,424 @@ +import asyncio +import base64 +import hashlib +import hmac +import json +import random +import time +from datetime import datetime, timedelta, timezone +from email.utils import parsedate_to_datetime +from threading import Lock +from urllib.parse import urlencode + +import httpx + +from ..problem import LegacyProblem, LegacyProblemDecoder, Problem, ProblemDecoder +from ..exceptions import ( + SdkClientError, + SdkConfigurationError, + SdkHttpError, + SdkServerError, + SdkTransportError, + SdkUsageError, +) +from ..version import get_version + +MAX_RETRIES = 3 +RETRY_BACKOFF_FACTOR = 1.0 +RETRY_BACKOFF_JITTER = 3.0 +RETRY_BACKOFF_MAX = 30.0 +DEFAULT_USER_AGENT = "osc-sdk-python/" + get_version() + + +class RateLimiter: + def __init__(self, window: timedelta, max_requests: int, datetime_cls=datetime): + self.datetime_cls = datetime_cls + self.window: timedelta = window + self.max_requests: int = max_requests + self.requests = [] + self._lock = Lock() + + def acquire(self): + with self._lock: + now = self.datetime_cls.now(timezone.utc) + + self.clean_old_requests(now) + + if len(self.requests) >= self.max_requests: + oldest = self.requests[0] + wait_time = self.window - (now - oldest) + time.sleep(wait_time.total_seconds()) + + now = self.datetime_cls.now(timezone.utc) + self.clean_old_requests(now) + + self.requests.append(now) + + async def async_acquire(self): + await asyncio.to_thread(self._lock.acquire) + try: + now = self.datetime_cls.now(timezone.utc) + + self.clean_old_requests(now) + + if len(self.requests) >= self.max_requests: + oldest = self.requests[0] + wait_time = self.window - (now - oldest) + await asyncio.sleep(wait_time.total_seconds()) + + now = self.datetime_cls.now(timezone.utc) + self.clean_old_requests(now) + + self.requests.append(now) + finally: + self._lock.release() + + def clean_old_requests(self, now): + while len(self.requests) > 0 and self.requests[0] <= now - self.window: + self.requests.pop(0) + + +class SdkAuth(httpx.Auth): + def __init__( + self, + profile, + *, + service="api", + content_type="application/json; charset=utf-8", + algorithm="OSC4-HMAC-SHA256", + signed_headers="content-type;host;x-osc-date", + user_agent=None, + ): + if service in profile.iam_v2_services: + self.access_key = profile.access_key_v2 + self.secret_key = profile.secret_key_v2 + else: + self.access_key = profile.access_key + self.secret_key = profile.secret_key + self.login = profile.login + self.password = profile.password + self.region = profile.region + self.service = service + self.content_type = content_type + self.algorithm = algorithm + self.signed_headers = signed_headers + self.user_agent = user_agent or DEFAULT_USER_AGENT + + def auth_flow(self, request: httpx.Request): + if self.service == "oks": + request.headers.update(self.forge_headers_oks()) + elif self.is_basic_auth_configured(): + request.headers.update(self.get_basic_auth_header()) + else: + request.headers.update(self.forge_headers_signed(request)) + yield request + + def ensure_signed_auth_configured(self): + if self.access_key is None or self.secret_key is None: + raise SdkConfigurationError("access key and secret key must be set") + + def forge_headers_signed(self, request: httpx.Request): + self.ensure_signed_auth_configured() + date_iso, date = self.build_dates() + credential_scope = f"{date}/{self.region}/{self.service}/osc4_request" + canonical_request = self.build_canonical_request(request, date_iso) + str_to_sign = self.create_string_to_sign( + date_iso, + credential_scope, + canonical_request, + ) + signature = self.compute_signature(date, str_to_sign) + return { + "Content-Type": self.content_type, + "X-Osc-Date": date_iso, + "Authorization": self.build_authorization_header( + credential_scope, + signature, + ), + "User-Agent": self.user_agent, + } + + def forge_headers_oks(self): + self.ensure_signed_auth_configured() + return { + "AccessKey": self.access_key, + "SecretKey": self.secret_key, + "User-Agent": self.user_agent, + } + + def build_dates(self): + now = datetime.now(timezone.utc) + return now.strftime("%Y%m%dT%H%M%SZ"), now.strftime("%Y%m%d") + + def sign(self, key, msg): + return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest() + + def get_signature_key(self, key, date_stamp_value): + k_date = self.sign(("OSC4" + key).encode("utf-8"), date_stamp_value) + k_region = self.sign(k_date, self.region) + k_service = self.sign(k_region, self.service) + return self.sign(k_service, "osc4_request") + + def build_canonical_request(self, request: httpx.Request, date_iso: str): + canonical_headers = ( + f"content-type:{self.content_type}\n" + f"host:{request.url.host}\n" + f"x-osc-date:{date_iso}\n" + ) + canonical_querystring = urlencode( + sorted(request.url.params.multi_items()), + doseq=True, + ) + payload_hash = hashlib.sha256(request.content).hexdigest() + return ( + f"{request.method}\n" + f"{request.url.path}\n" + f"{canonical_querystring}\n" + f"{canonical_headers}\n" + f"{self.signed_headers}\n" + f"{payload_hash}" + ) + + def create_string_to_sign(self, date_iso, credential_scope, canonical_request): + return ( + f"{self.algorithm}\n" + f"{date_iso}\n" + f"{credential_scope}\n" + f"{hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()}" + ) + + def compute_signature(self, date, string_to_sign): + signing_key = self.get_signature_key(self.secret_key, date) + return hmac.new( + signing_key, + string_to_sign.encode("utf-8"), + hashlib.sha256, + ).hexdigest() + + def build_authorization_header(self, credential_scope, signature): + return ( + f"{self.algorithm} " + f"Credential={self.access_key}/{credential_scope}, " + f"SignedHeaders={self.signed_headers}, " + f"Signature={signature}" + ) + + def is_basic_auth_configured(self): + return self.login is not None and self.password is not None + + def get_basic_auth_header(self): + if not self.is_basic_auth_configured(): + raise SdkUsageError("email or password not set") + creds = f"{self.login}:{self.password}" + b64_creds = base64.b64encode(creds.encode("utf-8")).decode("utf-8") + date_iso, _ = self.build_dates() + return { + "Content-Type": self.content_type, + "X-Osc-Date": date_iso, + "Authorization": "Basic " + b64_creds, + } + + +class RetryPolicy: + def __init__( + self, + *, + max_retries=MAX_RETRIES, + backoff_factor=RETRY_BACKOFF_FACTOR, + backoff_jitter=RETRY_BACKOFF_JITTER, + backoff_max=RETRY_BACKOFF_MAX, + ): + self.max_retries = int(max_retries) + self.backoff_factor = float(backoff_factor) + self.backoff_jitter = float(backoff_jitter) + self.backoff_max = float(backoff_max) + + def should_retry(self, error: httpx.HTTPError | SdkHttpError, attempt: int) -> bool: + if isinstance(error, httpx.TooManyRedirects): + return False + if isinstance(error, httpx.InvalidURL | httpx.UnsupportedProtocol): + return False + + response = getattr(error, "response", None) + if response is not None: + if 400 <= response.status_code < 500 and response.status_code != 429: + return False + return attempt < self.max_retries + + def backoff_time(self, attempt: int) -> float: + backoff = self.backoff_factor * (2**attempt) + backoff += random.uniform(0, self.backoff_jitter) + return min(backoff, self.backoff_max) + + def retry_after_time(self, error: httpx.HTTPError | SdkHttpError): + response = getattr(error, "response", None) + if response is None: + return None + + retry_after = response.headers.get("Retry-After") + if retry_after is None: + return None + + try: + return max(0.0, float(retry_after)) + except ValueError: + pass + + try: + retry_date = parsedate_to_datetime(retry_after) + except (TypeError, ValueError): + return None + + if retry_date.tzinfo is None: + retry_date = retry_date.replace(tzinfo=timezone.utc) + return max(0.0, (retry_date - datetime.now(timezone.utc)).total_seconds()) + + +def get_default_reason(response: httpx.Response) -> str: + reason = getattr(response, "reason", None) + if reason is None: + return getattr(response, "reason_phrase", "") + if isinstance(reason, bytes): + try: + return reason.decode("utf-8") + except UnicodeDecodeError: + return reason.decode("iso-8859-1") + return reason + + +def _response_url(response: httpx.Response, request: httpx.Request | None) -> str: + try: + return str(response.url) + except (AttributeError, RuntimeError): + if request is not None: + return str(request.url) + return "" + + +def _error_attr(error: Exception, name: str): + try: + return getattr(error, name) + except (AttributeError, RuntimeError): + return None + + +def raise_for_status( + response: httpx.Response, + request: httpx.Request | None = None, +) -> None: + problem = None + reason = get_default_reason(response) + url = _response_url(response, request) + + try: + ct = response.headers.get("content-type") or "" + if "application/problem+json" in ct: + problem = json.loads(response.text, cls=ProblemDecoder) + problem.status = problem.status or str(response.status_code) + elif "application/json" in ct: + problem = json.loads(response.text, cls=LegacyProblemDecoder) + problem.status = problem.status or str(response.status_code) + problem.url = url + except json.JSONDecodeError: + pass + + http_error_msg = "" + if 400 <= response.status_code < 500: + if isinstance(problem, (LegacyProblem, Problem)): + http_error_msg = f"Client Error --> {problem.msg()}" + else: + http_error_msg = ( + f"{response.status_code} Client Error: {reason} for url: {url}" + ) + elif 500 <= response.status_code < 600: + if isinstance(problem, (LegacyProblem, Problem)): + http_error_msg = f"Server Error --> {problem.msg()}" + else: + http_error_msg = ( + f"{response.status_code} Server Error: {reason} for url: {url}" + ) + + if http_error_msg: + error_cls = ( + SdkClientError if 400 <= response.status_code < 500 else SdkServerError + ) + raise error_cls( + http_error_msg, + status_code=response.status_code, + request=request or response.request, + response=response, + problem=problem, + url=url, + ) + + +class SdkTransport(httpx.BaseTransport): + def __init__(self, *, limiter=None, retry_policy=None, **kwargs): + self.limiter = limiter + self.retry_policy = retry_policy or RetryPolicy() + self._transport = httpx.HTTPTransport(**kwargs) + + def handle_request(self, request: httpx.Request) -> httpx.Response: + attempt = 0 + request.read() + while True: + if self.limiter is not None: + self.limiter.acquire() + try: + response = self._transport.handle_request(request) + response.read() + raise_for_status(response, request) + return response + except (httpx.HTTPError, SdkHttpError) as error: + if not self.retry_policy.should_retry(error, attempt): + if isinstance(error, SdkHttpError): + raise + raise SdkTransportError( + str(error), + request=_error_attr(error, "request"), + response=_error_attr(error, "response"), + ) from error + sleep_time = self.retry_policy.retry_after_time(error) + if sleep_time is None: + sleep_time = self.retry_policy.backoff_time(attempt) + time.sleep(sleep_time) + attempt += 1 + + def close(self) -> None: + self._transport.close() + + +class AsyncSdkTransport(httpx.AsyncBaseTransport): + def __init__(self, *, limiter=None, retry_policy=None, **kwargs): + self.limiter = limiter + self.retry_policy = retry_policy or RetryPolicy() + self._transport = httpx.AsyncHTTPTransport(**kwargs) + + async def handle_async_request(self, request: httpx.Request) -> httpx.Response: + attempt = 0 + await request.aread() + while True: + if self.limiter is not None: + await self.limiter.async_acquire() + try: + response = await self._transport.handle_async_request(request) + await response.aread() + raise_for_status(response, request) + return response + except (httpx.HTTPError, SdkHttpError) as error: + if not self.retry_policy.should_retry(error, attempt): + if isinstance(error, SdkHttpError): + raise + raise SdkTransportError( + str(error), + request=_error_attr(error, "request"), + response=_error_attr(error, "response"), + ) from error + sleep_time = self.retry_policy.retry_after_time(error) + if sleep_time is None: + sleep_time = self.retry_policy.backoff_time(attempt) + await asyncio.sleep(sleep_time) + attempt += 1 + + async def aclose(self) -> None: + await self._transport.aclose() diff --git a/pyproject.toml b/pyproject.toml index ee875c3..34af555 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,15 @@ [project] name = "osc_sdk_python" -version = "0.41.0" +version = "0.42.0" description = "Outscale Gateway python SDK" authors = [ - { name = "Outscal SAS", email = "opensource@outscale.com" } + { name = "Outscale SAS", email = "opensource@outscale.com" } ] readme = "README.md" requires-python = ">=3.10" license = { text = "BSD" } keywords = [] classifiers = [ - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -20,9 +19,9 @@ classifiers = [ "Operating System :: OS Independent" ] dependencies = [ - "requests>=2.20.0", + "httpx>=0.28.0", + "pydantic>=2.0.0", "ruamel.yaml==0.19.1", - "urllib3>=2.6.3", ] [dependency-groups] diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/async_/__init__.py b/tests/integration/async_/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/async_/helpers/__init__.py b/tests/integration/async_/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/async_/helpers/async_integration_utils.py b/tests/integration/async_/helpers/async_integration_utils.py new file mode 100644 index 0000000..31ee3ee --- /dev/null +++ b/tests/integration/async_/helpers/async_integration_utils.py @@ -0,0 +1,70 @@ +from osc_sdk_python.generated.osc import ( + CreateTagsRequest, + ReadImagesRequest, + ReadSubregionsRequest, +) +from tests.integration.helpers.integration_utils import ( + build_name_tag_request, + get_first_item, + get_linux_http_user_data, + get_tagged_name, + log_test_step, +) + + +async def get_first_subregion_name(client): + subregions = await client.osc.read_subregions(ReadSubregionsRequest()) + subregion = get_first_item(subregions.subregions, "No subregions returned") + if not subregion.subregion_name: + raise AssertionError("SubregionName is missing") + return subregion.subregion_name + + +async def get_latest_public_ubuntu_image_id(client): + images = await client.osc.read_images( + ReadImagesRequest( + filters={ + "AccountAliases": ["Outscale"], + "ImageNames": ["Ubuntu*"], + "PermissionsToLaunchGlobalPermission": True, + "States": ["available"], + }, + results_per_page=10, + ) + ) + image = get_first_item( + sorted(images.images or [], key=lambda item: item.creation_date or "", reverse=True), + "No public Ubuntu image returned", + ) + if not image.image_id: + raise AssertionError("ImageId is missing") + return image.image_id + + +async def read_single_resource(client, method_name, request_cls, key, resource_id_key, resource_id): + response = await getattr(client.osc, method_name)( + request_cls(filters={resource_id_key: [resource_id]}) + ) + items = getattr(response, key) + if not items or len(items) != 1: + raise AssertionError( + "{} did not return exactly one resource for {}".format( + method_name, resource_id + ) + ) + return items[0] + + +def build_name_tag_typed_request(resource_id): + return CreateTagsRequest(**build_name_tag_request(resource_id)) + + +__all__ = [ + "build_name_tag_typed_request", + "get_first_subregion_name", + "get_latest_public_ubuntu_image_id", + "get_linux_http_user_data", + "get_tagged_name", + "log_test_step", + "read_single_resource", +] diff --git a/tests/integration/async_/oks/__init__.py b/tests/integration/async_/oks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/async_/oks/test_async_oks_project.py b/tests/integration/async_/oks/test_async_oks_project.py new file mode 100644 index 0000000..88c098e --- /dev/null +++ b/tests/integration/async_/oks/test_async_oks_project.py @@ -0,0 +1,144 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient, SdkHttpError +from osc_sdk_python.generated.oks import ( + CreateProjectRequest, + DeleteProjectRequest, + DetailResponse, + GetProjectRequest, + GetProjectTemplateRequest, + Project, + ProjectInput, + ProjectResponse, + ProjectUpdate, + TemplateResponse_ProjectInput, + UpdateProjectRequest, +) +from tests.integration.helpers.integration_utils import get_tagged_name, log_test_step + + +PROJECT_READY_STATUS = "ready" + + +async def wait_project_ready(client, project_id): + for _ in range(36): + response = await client.oks.get_project( + GetProjectRequest(project_id=project_id) + ) + project = response.project + log_test_step( + "OKS project {} status={} (async)".format(project_id, project.status) + ) + if project.status == PROJECT_READY_STATUS: + return project + await asyncio.sleep(10) + + raise AssertionError("OKS project {} did not become ready".format(project_id)) + + +async def delete_project_when_ready(client, project_id): + for _ in range(36): + try: + delete_response = await client.oks.delete_project( + DeleteProjectRequest(project_id=project_id) + ) + log_test_step("Deleted OKS project {} (async)".format(project_id)) + return delete_response + except SdkHttpError as err: + if err.response is None or err.response.status_code != 503: + raise + log_test_step( + "OKS project {} is not ready for deletion yet (async)".format( + project_id + ) + ) + await asyncio.sleep(10) + + raise AssertionError("OKS project {} could not be deleted".format(project_id)) + + +class TestAsyncOksProject(unittest.TestCase): + def test_project_lifecycle(self): + async def run(): + async with AsyncClient() as client: + project_id = None + project_name = get_tagged_name("osc-sdk-python-oks-project") + updated_description = "Updated OKS project lifecycle test" + + try: + log_test_step("Reading OKS project template (async)") + template_response = await client.oks.get_project_template( + GetProjectTemplateRequest() + ) + self.assertIsInstance( + template_response, TemplateResponse_ProjectInput + ) + project_input = template_response.template.model_copy( + update={ + "name": project_name, + "description": "OKS project lifecycle test", + "tags": {"Name": project_name}, + } + ) + self.assertIsInstance(project_input, ProjectInput) + + log_test_step( + "Creating OKS project {} (async)".format(project_name) + ) + create_response = await client.oks.create_project( + CreateProjectRequest(body=project_input) + ) + self.assertIsInstance(create_response, ProjectResponse) + project = create_response.project + self.assertIsInstance(project, Project) + project_id = project.id + self.assertTrue(project_id) + self.assertEqual(project.name, project_name) + log_test_step("Created OKS project {} (async)".format(project_id)) + + log_test_step("Reading OKS project {} (async)".format(project_id)) + get_response = await client.oks.get_project( + GetProjectRequest(project_id=project_id) + ) + self.assertIsInstance(get_response, ProjectResponse) + read_project = get_response.project + self.assertIsInstance(read_project, Project) + self.assertEqual(read_project.id, project_id) + self.assertEqual(read_project.name, project_name) + + read_project = await wait_project_ready(client, project_id) + self.assertEqual(read_project.id, project_id) + self.assertEqual(read_project.name, project_name) + + log_test_step("Updating OKS project {} (async)".format(project_id)) + update_response = await client.oks.update_project( + UpdateProjectRequest( + project_id=project_id, + body=ProjectUpdate( + description=updated_description, + tags={"Name": project_name, "Updated": "true"}, + ), + ) + ) + self.assertIsInstance(update_response, ProjectResponse) + updated_project = update_response.project + self.assertIsInstance(updated_project, Project) + self.assertEqual(updated_project.id, project_id) + self.assertEqual(updated_project.name, project_name) + self.assertEqual(updated_project.description, updated_description) + finally: + if project_id: + log_test_step( + "Deleting OKS project {} (async)".format(project_id) + ) + delete_response = await delete_project_when_ready( + client, project_id + ) + self.assertIsInstance(delete_response, DetailResponse) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/__init__.py b/tests/integration/async_/osc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/async_/osc/test_async_eim_user.py b/tests/integration/async_/osc/test_async_eim_user.py new file mode 100644 index 0000000..97b6989 --- /dev/null +++ b/tests/integration/async_/osc/test_async_eim_user.py @@ -0,0 +1,65 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + CreateUserResponse, + CreateUserRequest, + DeleteUserRequest, + ReadUsersResponse, + ReadUsersRequest, + User, +) +from tests.integration.async_.helpers.async_integration_utils import get_tagged_name, log_test_step + + +class TestAsyncEimUser(unittest.TestCase): + def test_eim_user_lifecycle(self): + async def run(): + async with AsyncClient() as client: + user_name = get_tagged_name("osc-sdk-python-user-async") + user_email = "{}@example.com".format(user_name) + user_id = None + try: + log_test_step("Creating EIM user {} (async)".format(user_name)) + response = await client.osc.create_user( + CreateUserRequest( + path="/", + user_email=user_email, + user_name=user_name, + ) + ) + self.assertIsInstance(response, CreateUserResponse) + user = response.user + self.assertIsInstance(user, User) + user_id = user.user_id + self.assertTrue(user_id) + log_test_step("Created EIM user {} (async)".format(user_id)) + self.assertEqual(user.user_name, user_name) + self.assertEqual(user.user_email, user_email) + self.assertEqual(user.path, "/") + + log_test_step("Reading EIM user {} (async)".format(user_id)) + read_response = await client.osc.read_users( + ReadUsersRequest(filters={"UserIds": [user_id]}) + ) + self.assertIsInstance(read_response, ReadUsersResponse) + users = read_response.users + self.assertIsInstance(users, list) + self.assertEqual(len(users), 1) + self.assertIsInstance(users[0], User) + self.assertEqual(users[0].user_id, user_id) + self.assertEqual(users[0].user_name, user_name) + self.assertEqual(users[0].user_email, user_email) + finally: + if user_id: + log_test_step("Deleting EIM user {} (async)".format(user_name)) + await client.osc.delete_user( + DeleteUserRequest(user_name=user_name) + ) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_exceptions.py b/tests/integration/async_/osc/test_async_exceptions.py new file mode 100644 index 0000000..bf20834 --- /dev/null +++ b/tests/integration/async_/osc/test_async_exceptions.py @@ -0,0 +1,18 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient, SdkValidationError + + +class TestAsyncExcept(unittest.TestCase): + def test_listing(self): + async def run(): + async with AsyncClient() as client: + with self.assertRaises(SdkValidationError): + await client.osc.read_vms({"filters": "a"}) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_exceptions_500.py b/tests/integration/async_/osc/test_async_exceptions_500.py new file mode 100644 index 0000000..c79152e --- /dev/null +++ b/tests/integration/async_/osc/test_async_exceptions_500.py @@ -0,0 +1,68 @@ +import asyncio +import copy +import os +import threading +import time +import unittest +from http.server import BaseHTTPRequestHandler +from socketserver import TCPServer + +from osc_sdk_python import AsyncClient, SdkServerError +from osc_sdk_python.generated.osc import ReadVmsRequest + + +class EnvironManager: + def __enter__(self): + self.env = copy.deepcopy(os.environ) + + def __exit__(self, *args): + os.environ = self.env + + +class Send500(BaseHTTPRequestHandler): + def do_POST(self): + self.send_response(500) + self.send_header("Content-type", "application/json") + self.send_header("x-amz-requestid", "00000001") + self.end_headers() + self.wfile.write( + b'{"error": "Internal Server Error", "message": "test", "__type": 9}' + ) + + +@unittest.skip("this test is flaky") +class TestAsyncServerError(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.server = None + cls.thread = None + + def start_server(): + with TCPServer(("localhost", 8000), Send500) as httpd: + cls.server = httpd + httpd.serve_forever() + + cls.thread = threading.Thread(target=start_server) + cls.thread.daemon = True + cls.thread.start() + time.sleep(1) + + @classmethod + def tearDownClass(cls): + if cls.server: + cls.server.shutdown() + cls.thread.join() + + def test_server_error(self): + async def run(): + with EnvironManager(): + os.environ["OSC_ENDPOINT_API"] = "http://127.0.0.1:8000" + async with AsyncClient() as client: + with self.assertRaises(SdkServerError): + await client.osc.read_vms(ReadVmsRequest()) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_keypair.py b/tests/integration/async_/osc/test_async_keypair.py new file mode 100644 index 0000000..63420d0 --- /dev/null +++ b/tests/integration/async_/osc/test_async_keypair.py @@ -0,0 +1,42 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + CreateKeypairRequest, + CreateKeypairResponse, + DeleteKeypairRequest, + KeypairCreated, +) +from tests.integration.async_.helpers.async_integration_utils import get_tagged_name, log_test_step + + +class TestAsyncKeypair(unittest.TestCase): + def test_keypair_lifecycle(self): + async def run(): + async with AsyncClient() as client: + keypair_name = get_tagged_name("osc-sdk-python-keypair-async") + keypair_id = None + try: + log_test_step("Creating keypair {} (async)".format(keypair_name)) + response = await client.osc.create_keypair( + CreateKeypairRequest(keypair_name=keypair_name) + ) + self.assertIsInstance(response, CreateKeypairResponse) + keypair = response.keypair + self.assertIsInstance(keypair, KeypairCreated) + keypair_id = keypair.keypair_id + self.assertTrue(keypair_id) + log_test_step("Created keypair {} (async)".format(keypair_id)) + finally: + if keypair_id: + log_test_step("Deleting keypair {} (async)".format(keypair_id)) + await client.osc.delete_keypair( + DeleteKeypairRequest(keypair_id=keypair_id) + ) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_load_balancer_backend.py b/tests/integration/async_/osc/test_async_load_balancer_backend.py new file mode 100644 index 0000000..eb2888f --- /dev/null +++ b/tests/integration/async_/osc/test_async_load_balancer_backend.py @@ -0,0 +1,214 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + BackendVmHealth, + CreateLoadBalancerRequest, + CreateLoadBalancerResponse, + CreateVmsRequest, + CreateVmsResponse, + DeleteLoadBalancerRequest, + DeleteVmsRequest, + LinkLoadBalancerBackendMachinesRequest, + LoadBalancer, + ReadLoadBalancersRequest, + ReadLoadBalancersResponse, + ReadVmsHealthRequest, + ReadVmsHealthResponse, + ReadVmsRequest, + Vm, +) +from tests.integration.async_.helpers.async_integration_utils import ( + build_name_tag_typed_request, + get_first_subregion_name, + get_latest_public_ubuntu_image_id, + get_linux_http_user_data, + get_tagged_name, + log_test_step, + read_single_resource, +) + + +class TestAsyncLoadBalancerBackend(unittest.TestCase): + def test_load_balancer_backend_lifecycle(self): + async def run(): + async with AsyncClient() as client: + subregion_name = await get_first_subregion_name(client) + image_id = await get_latest_public_ubuntu_image_id(client) + log_test_step( + "Using subregion {} and image {} (async)".format( + subregion_name, image_id + ) + ) + vm_id = None + load_balancer_name = get_tagged_name("osc-sdk-python-lb") + load_balancer_created = False + try: + log_test_step("Creating backend VM (async)") + vm_response = await client.osc.create_vms( + CreateVmsRequest( + image_id=image_id, + min_vms_count=1, + max_vms_count=1, + placement={ + "SubregionName": subregion_name, + "Tenancy": "default", + }, + user_data=get_linux_http_user_data(), + vm_type="tinav6.c1r1p2", + ) + ) + self.assertIsInstance(vm_response, CreateVmsResponse) + vms = vm_response.vms + self.assertIsInstance(vms, list) + self.assertEqual(len(vms), 1) + self.assertIsInstance(vms[0], Vm) + vm_id = vms[0].vm_id + self.assertTrue(vm_id) + log_test_step("Created backend VM {} (async)".format(vm_id)) + + await client.osc.create_tags(build_name_tag_typed_request(vm_id)) + log_test_step("Tagged backend VM {} (async)".format(vm_id)) + + for _ in range(36): + vm = await read_single_resource( + client, + "read_vms", + ReadVmsRequest, + "vms", + "VmIds", + vm_id, + ) + self.assertIsInstance(vm, Vm) + log_test_step( + "VM {} state={} (async)".format(vm_id, vm.state) + ) + if vm.state == "running": + break + if vm.state in ("stopped", "terminated", "shutting-down"): + self.fail( + "VM {} entered unexpected state {}".format( + vm_id, vm.state + ) + ) + await asyncio.sleep(10) + + log_test_step("Creating load balancer {} (async)".format(load_balancer_name)) + load_balancer_response = await client.osc.create_load_balancer( + CreateLoadBalancerRequest( + load_balancer_name=load_balancer_name, + listeners=[ + { + "BackendPort": 80, + "LoadBalancerPort": 80, + "LoadBalancerProtocol": "TCP", + "BackendProtocol": "TCP", + } + ], + subregion_names=[subregion_name], + tags=[ + { + "Key": "Name", + "Value": get_tagged_name( + "osc-sdk-python-lb-tag-async" + ), + } + ], + ) + ) + self.assertIsInstance( + load_balancer_response, CreateLoadBalancerResponse + ) + self.assertIsInstance( + load_balancer_response.load_balancer, LoadBalancer + ) + load_balancer_created = True + + log_test_step( + "Linking backend VM {} to {} (async)".format( + vm_id, load_balancer_name + ) + ) + await client.osc.link_load_balancer_backend_machines( + LinkLoadBalancerBackendMachinesRequest( + load_balancer_name=load_balancer_name, + backend_vm_ids=[vm_id], + ) + ) + + log_test_step( + "Reading load balancer {} (async)".format(load_balancer_name) + ) + read_balancers = await client.osc.read_load_balancers( + ReadLoadBalancersRequest( + filters={"LoadBalancerNames": [load_balancer_name]} + ) + ) + self.assertIsInstance(read_balancers, ReadLoadBalancersResponse) + balancers = read_balancers.load_balancers + self.assertIsInstance(balancers, list) + self.assertEqual(len(balancers), 1) + self.assertIsInstance(balancers[0], LoadBalancer) + self.assertIn(vm_id, balancers[0].backend_vm_ids or []) + + health = None + for _ in range(18): + health = await client.osc.read_vms_health( + ReadVmsHealthRequest( + load_balancer_name=load_balancer_name, + backend_vm_ids=[vm_id], + ) + ) + self.assertIsInstance(health, ReadVmsHealthResponse) + entry_count = len(health.backend_vm_health or []) + if health.backend_vm_health: + self.assertIsInstance( + health.backend_vm_health[0], BackendVmHealth + ) + log_test_step( + "Backend health entries for {}: {} (async)".format( + load_balancer_name, entry_count + ) + ) + if any( + entry.vm_id == vm_id + for entry in health.backend_vm_health or [] + ): + break + await asyncio.sleep(10) + + self.assertIsNotNone(health) + self.assertTrue( + any( + entry.vm_id == vm_id + for entry in health.backend_vm_health or [] + ) + ) + log_test_step( + "Backend VM {} is registered in {} (async)".format( + vm_id, load_balancer_name + ) + ) + finally: + if load_balancer_created: + log_test_step( + "Deleting load balancer {} (async)".format( + load_balancer_name + ) + ) + await client.osc.delete_load_balancer( + DeleteLoadBalancerRequest( + load_balancer_name=load_balancer_name + ) + ) + if vm_id: + await asyncio.sleep(1) + log_test_step("Deleting backend VM {} (async)".format(vm_id)) + await client.osc.delete_vms(DeleteVmsRequest(vm_ids=[vm_id])) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_log.py b/tests/integration/async_/osc/test_async_log.py new file mode 100644 index 0000000..6ad339a --- /dev/null +++ b/tests/integration/async_/osc/test_async_log.py @@ -0,0 +1,51 @@ +import asyncio +import logging +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ReadVmsRequest, ReadVmsResponse + + +class TestAsyncLog(unittest.TestCase): + def test_listing(self): + async def run(): + async with AsyncClient() as client: + with self.assertLogs("osc_sdk_python", level=logging.INFO) as logs: + vms = await client.osc.read_vms(ReadVmsRequest()) + self.assertIsInstance(vms, ReadVmsResponse) + self.assertEqual( + logs.records[-1].getMessage(), + """mode: async +service: api +method: POST +uri: /api/v1/ReadVms +payload: +{}""", + ) + + with self.assertLogs("osc_sdk_python", level=logging.INFO) as logs: + vms = await client.osc.read_vms( + ReadVmsRequest(filters={"TagKeys": ["test"]}) + ) + self.assertIsInstance(vms, ReadVmsResponse) + self.assertEqual( + logs.records[-1].getMessage(), + """mode: async +service: api +method: POST +uri: /api/v1/ReadVms +payload: +{ + "Filters": { + "TagKeys": [ + "test" + ] + } +}""", + ) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_manual_aksk.py b/tests/integration/async_/osc/test_async_manual_aksk.py new file mode 100644 index 0000000..95e06cd --- /dev/null +++ b/tests/integration/async_/osc/test_async_manual_aksk.py @@ -0,0 +1,38 @@ +import asyncio +import copy +import os +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ReadVolumesRequest, ReadVolumesResponse, Volume + + +class EnvironManager: + def __enter__(self): + self.env = copy.deepcopy(os.environ) + + def __exit__(self, *args): + os.environ = self.env + + +class TestAsyncLoginManualAkSk(unittest.TestCase): + def test_manual_ak_sk(self): + async def run(): + with EnvironManager(): + ak = os.environ.pop("OSC_ACCESS_KEY", None) + sk = os.environ.pop("OSC_SECRET_KEY", None) + self.assertIsNotNone(ak) + self.assertIsNotNone(sk) + async with AsyncClient(access_key=ak, secret_key=sk) as client: + volumes = await client.osc.read_volumes(ReadVolumesRequest()) + + self.assertIsInstance(volumes, ReadVolumesResponse) + self.assertIsInstance(volumes.volumes, list) + if volumes.volumes: + self.assertIsInstance(volumes.volumes[0], Volume) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_net.py b/tests/integration/async_/osc/test_async_net.py new file mode 100644 index 0000000..467b847 --- /dev/null +++ b/tests/integration/async_/osc/test_async_net.py @@ -0,0 +1,27 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient, SdkClientError +from osc_sdk_python.generated.osc import CreateNetRequest + + +class TestAsyncNet(unittest.TestCase): + def test_creation_error(self): + async def run(): + async with AsyncClient() as client: + with self.assertRaises(SdkClientError) as cm: + await client.osc.create_net( + CreateNetRequest(ip_range="142.42.42.42/32") + ) + + errors = cm.exception.response.json().get("Errors") + self.assertIsNotNone(errors) + self.assertIsInstance(errors, list) + for error in errors: + self.assertEqual(error.get("Code"), "9050") + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_net_subnet.py b/tests/integration/async_/osc/test_async_net_subnet.py new file mode 100644 index 0000000..228d8f4 --- /dev/null +++ b/tests/integration/async_/osc/test_async_net_subnet.py @@ -0,0 +1,99 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + CreateNetRequest, + CreateNetResponse, + CreateSubnetRequest, + CreateSubnetResponse, + DeleteNetRequest, + DeleteSubnetRequest, + Net, + ReadSubnetsRequest, + Subnet, + UpdateSubnetRequest, + UpdateSubnetResponse, +) +from tests.integration.async_.helpers.async_integration_utils import ( + build_name_tag_typed_request, + log_test_step, + read_single_resource, +) + + +class TestAsyncNetAndSubnet(unittest.TestCase): + def test_net_and_subnet_lifecycle(self): + async def run(): + async with AsyncClient() as client: + net_id = None + subnet_id = None + try: + log_test_step("Creating net 10.0.0.0/16 (async)") + net_response = await client.osc.create_net( + CreateNetRequest(ip_range="10.0.0.0/16") + ) + self.assertIsInstance(net_response, CreateNetResponse) + net = net_response.net + self.assertIsInstance(net, Net) + net_id = net.net_id + self.assertTrue(net_id) + log_test_step("Created net {} (async)".format(net_id)) + + await client.osc.create_tags(build_name_tag_typed_request(net_id)) + await asyncio.sleep(2) + + log_test_step("Creating subnet 10.0.1.0/24 in {} (async)".format(net_id)) + subnet_response = await client.osc.create_subnet( + CreateSubnetRequest(net_id=net_id, ip_range="10.0.1.0/24") + ) + self.assertIsInstance(subnet_response, CreateSubnetResponse) + subnet = subnet_response.subnet + self.assertIsInstance(subnet, Subnet) + subnet_id = subnet.subnet_id + self.assertTrue(subnet_id) + log_test_step("Created subnet {} (async)".format(subnet_id)) + + await client.osc.create_tags(build_name_tag_typed_request(subnet_id)) + await asyncio.sleep(2) + + log_test_step("Reading subnet {} (async)".format(subnet_id)) + subnet = await read_single_resource( + client, + "read_subnets", + ReadSubnetsRequest, + "subnets", + "SubnetIds", + subnet_id, + ) + self.assertIsInstance(subnet, Subnet) + self.assertEqual(subnet.subnet_id, subnet_id) + self.assertTrue( + any(tag.key == "Name" for tag in subnet.tags or []), + "expected a Name tag on the subnet", + ) + + log_test_step("Updating subnet {} (async)".format(subnet_id)) + updated = await client.osc.update_subnet( + UpdateSubnetRequest( + subnet_id=subnet_id, + map_public_ip_on_launch=False, + ) + ) + self.assertIsInstance(updated, UpdateSubnetResponse) + self.assertIsInstance(updated.subnet, Subnet) + finally: + if subnet_id: + log_test_step("Deleting subnet {} (async)".format(subnet_id)) + await client.osc.delete_subnet( + DeleteSubnetRequest(subnet_id=subnet_id) + ) + if net_id: + log_test_step("Deleting net {} (async)".format(net_id)) + await client.osc.delete_net(DeleteNetRequest(net_id=net_id)) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_password.py b/tests/integration/async_/osc/test_async_password.py new file mode 100644 index 0000000..e87f685 --- /dev/null +++ b/tests/integration/async_/osc/test_async_password.py @@ -0,0 +1,48 @@ +import asyncio +import copy +import os +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + AccessKey, + ReadAccessKeysRequest, + ReadAccessKeysResponse, +) + + +class EnvironManager: + def __enter__(self): + self.env = copy.deepcopy(os.environ) + + def __exit__(self, *args): + os.environ = self.env + + +class TestAsyncLoginPassword(unittest.TestCase): + @unittest.skipIf( + not (os.environ.get("OSC_TEST_LOGIN") and os.environ.get("OSC_TEST_PASSWORD")), + "login/password credentials are not available", + ) + def test_login(self): + async def run(): + with EnvironManager(): + os.environ.pop("OSC_ACCESS_KEY", None) + os.environ.pop("OSC_SECRET_KEY", None) + email = os.getenv("OSC_TEST_LOGIN") + password = os.getenv("OSC_TEST_PASSWORD") + self.assertIsNotNone(email) + self.assertIsNotNone(password) + async with AsyncClient(email=email, password=password) as client: + keys = await client.osc.read_access_keys(ReadAccessKeysRequest()) + + self.assertIsInstance(keys, ReadAccessKeysResponse) + self.assertIsInstance(keys.access_keys, list) + if keys.access_keys: + self.assertIsInstance(keys.access_keys[0], AccessKey) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_security_group.py b/tests/integration/async_/osc/test_async_security_group.py new file mode 100644 index 0000000..b6a952d --- /dev/null +++ b/tests/integration/async_/osc/test_async_security_group.py @@ -0,0 +1,121 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + CreateSecurityGroupRequest, + CreateSecurityGroupResponse, + CreateSecurityGroupRuleRequest, + CreateSecurityGroupRuleResponse, + DeleteSecurityGroupRequest, + DeleteSecurityGroupRuleRequest, + ReadSecurityGroupsRequest, + ReadSecurityGroupsResponse, + SecurityGroup, +) +from tests.integration.async_.helpers.async_integration_utils import get_tagged_name, log_test_step + + +class TestAsyncSecurityGroup(unittest.TestCase): + def test_security_group_lifecycle(self): + async def run(): + async with AsyncClient() as client: + security_group_id = None + tcp = "tcp" + ip_range = "0.0.0.0/0" + try: + log_test_step("Creating security group (async)") + response = await client.osc.create_security_group( + CreateSecurityGroupRequest( + security_group_name=get_tagged_name( + "osc-sdk-python-sg-async" + ), + description="Test security group lifecycle async", + ) + ) + self.assertIsInstance(response, CreateSecurityGroupResponse) + security_group = response.security_group + self.assertIsInstance(security_group, SecurityGroup) + security_group_id = security_group.security_group_id + self.assertTrue(security_group_id) + log_test_step( + "Created security group {} (async)".format(security_group_id) + ) + + log_test_step( + "Creating inbound SSH rule on {} (async)".format( + security_group_id + ) + ) + rule_response = await client.osc.create_security_group_rule( + CreateSecurityGroupRuleRequest( + security_group_id=security_group_id, + flow="Inbound", + ip_protocol=tcp, + from_port_range=22, + to_port_range=22, + ip_range=ip_range, + ) + ) + self.assertIsInstance(rule_response, CreateSecurityGroupRuleResponse) + self.assertIsInstance(rule_response.security_group, SecurityGroup) + + log_test_step( + "Reading security group {} (async)".format(security_group_id) + ) + read_response = await client.osc.read_security_groups( + ReadSecurityGroupsRequest( + filters={"SecurityGroupIds": [security_group_id]} + ) + ) + self.assertIsInstance(read_response, ReadSecurityGroupsResponse) + security_groups = read_response.security_groups + self.assertIsInstance(security_groups, list) + self.assertEqual(len(security_groups), 1) + self.assertIsInstance(security_groups[0], SecurityGroup) + + rules = security_groups[0].inbound_rules or [] + self.assertTrue( + any( + rule.from_port_range == 22 + and rule.to_port_range == 22 + and rule.ip_protocol == tcp + and ip_range in (rule.ip_ranges or []) + for rule in rules + ), + "expected SSH inbound rule on the security group", + ) + + log_test_step( + "Deleting inbound SSH rule on {} (async)".format( + security_group_id + ) + ) + await client.osc.delete_security_group_rule( + DeleteSecurityGroupRuleRequest( + security_group_id=security_group_id, + flow="Inbound", + ip_protocol=tcp, + from_port_range=22, + to_port_range=22, + ip_range=ip_range, + ) + ) + finally: + if security_group_id: + log_test_step( + "Deleting security group {} (async)".format( + security_group_id + ) + ) + await client.osc.delete_security_group( + DeleteSecurityGroupRequest( + security_group_id=security_group_id + ) + ) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_snapshot.py b/tests/integration/async_/osc/test_async_snapshot.py new file mode 100644 index 0000000..85f4b05 --- /dev/null +++ b/tests/integration/async_/osc/test_async_snapshot.py @@ -0,0 +1,149 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ( + CreateSnapshotRequest, + CreateSnapshotResponse, + CreateVolumeRequest, + CreateVolumeResponse, + DeleteSnapshotRequest, + DeleteVolumeRequest, + ReadSnapshotsRequest, + ReadVolumesRequest, + Snapshot, + Volume, +) +from tests.integration.async_.helpers.async_integration_utils import ( + build_name_tag_typed_request, + get_first_subregion_name, + get_tagged_name, + log_test_step, + read_single_resource, +) + + +class TestAsyncSnapshot(unittest.TestCase): + def test_snapshot_lifecycle(self): + async def run(): + async with AsyncClient() as client: + subregion_name = await get_first_subregion_name(client) + log_test_step("Using subregion {} (async)".format(subregion_name)) + volume_id = None + snapshot_id = None + description = get_tagged_name("osc-sdk-python-snapshot-async") + try: + log_test_step("Creating source volume (async)") + volume_response = await client.osc.create_volume( + CreateVolumeRequest(size=10, subregion_name=subregion_name) + ) + self.assertIsInstance(volume_response, CreateVolumeResponse) + volume = volume_response.volume + self.assertIsInstance(volume, Volume) + volume_id = volume.volume_id + self.assertTrue(volume_id) + log_test_step("Created volume {} (async)".format(volume_id)) + + await client.osc.create_tags(build_name_tag_typed_request(volume_id)) + log_test_step("Tagged volume {} (async)".format(volume_id)) + + for _ in range(30): + volume = await read_single_resource( + client, + "read_volumes", + ReadVolumesRequest, + "volumes", + "VolumeIds", + volume_id, + ) + self.assertIsInstance(volume, Volume) + log_test_step( + "Volume {} state={} (async)".format( + volume_id, volume.state + ) + ) + if volume.state == "available": + break + if volume.state == "error": + self.fail( + "Volume {} entered unexpected state {}".format( + volume_id, volume.state + ) + ) + await asyncio.sleep(10) + + log_test_step( + "Creating snapshot from volume {} (async)".format(volume_id) + ) + snapshot_response = await client.osc.create_snapshot( + CreateSnapshotRequest( + description=description, + volume_id=volume_id, + ) + ) + self.assertIsInstance(snapshot_response, CreateSnapshotResponse) + snapshot = snapshot_response.snapshot + self.assertIsInstance(snapshot, Snapshot) + snapshot_id = snapshot.snapshot_id + self.assertTrue(snapshot_id) + log_test_step("Created snapshot {} (async)".format(snapshot_id)) + + await client.osc.create_tags(build_name_tag_typed_request(snapshot_id)) + log_test_step("Tagged snapshot {} (async)".format(snapshot_id)) + + snapshot = None + for _ in range(60): + snapshot = await read_single_resource( + client, + "read_snapshots", + ReadSnapshotsRequest, + "snapshots", + "SnapshotIds", + snapshot_id, + ) + self.assertIsInstance(snapshot, Snapshot) + log_test_step( + "Snapshot {} state={} (async)".format( + snapshot_id, snapshot.state + ) + ) + if snapshot.state == "completed": + break + if snapshot.state == "error": + self.fail( + "Snapshot {} entered unexpected state {}".format( + snapshot_id, snapshot.state + ) + ) + await asyncio.sleep(10) + + self.assertIsNotNone(snapshot) + self.assertIsInstance(snapshot, Snapshot) + self.assertEqual(snapshot.snapshot_id, snapshot_id) + self.assertEqual(snapshot.volume_id, volume_id) + self.assertEqual(snapshot.description, description) + self.assertTrue( + any(tag.key == "Name" for tag in snapshot.tags or []), + "expected a Name tag on the snapshot", + ) + log_test_step( + "Snapshot {} is completed (async)".format(snapshot_id) + ) + finally: + if snapshot_id: + log_test_step("Deleting snapshot {} (async)".format(snapshot_id)) + await client.osc.delete_snapshot( + DeleteSnapshotRequest(snapshot_id=snapshot_id) + ) + if volume_id: + await asyncio.sleep(1) + log_test_step("Deleting volume {} (async)".format(volume_id)) + await client.osc.delete_volume( + DeleteVolumeRequest(volume_id=volume_id) + ) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_vm.py b/tests/integration/async_/osc/test_async_vm.py new file mode 100644 index 0000000..e548b89 --- /dev/null +++ b/tests/integration/async_/osc/test_async_vm.py @@ -0,0 +1,37 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ReadVmsRequest, ReadVmsResponse, Vm + + +class TestAsyncVm(unittest.TestCase): + def test_listing(self): + async def run(): + client = AsyncClient() + try: + vms = await client.osc.read_vms(ReadVmsRequest()) + finally: + await client.close() + + self.assertIsInstance(vms.vms, list) + self.assertIsInstance(vms, ReadVmsResponse) + if vms.vms: + self.assertIsInstance(vms.vms[0], Vm) + + asyncio.run(run()) + + def test_listing_with_context_manager(self): + async def run(): + async with AsyncClient() as client: + vms = await client.osc.read_vms(ReadVmsRequest()) + self.assertIsInstance(vms, ReadVmsResponse) + self.assertIsInstance(vms.vms, list) + if vms.vms: + self.assertIsInstance(vms.vms[0], Vm) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_async_volume.py b/tests/integration/async_/osc/test_async_volume.py new file mode 100644 index 0000000..7f8d8ec --- /dev/null +++ b/tests/integration/async_/osc/test_async_volume.py @@ -0,0 +1,23 @@ +import asyncio +import unittest + +from osc_sdk_python import AsyncClient +from osc_sdk_python.generated.osc import ReadVolumesRequest, ReadVolumesResponse, Volume + + +class TestAsyncVolume(unittest.TestCase): + def test_listing(self): + async def run(): + async with AsyncClient() as client: + volumes = await client.osc.read_volumes(ReadVolumesRequest()) + + self.assertIsInstance(volumes, ReadVolumesResponse) + self.assertIsInstance(volumes.volumes, list) + if volumes.volumes: + self.assertIsInstance(volumes.volumes[0], Volume) + + asyncio.run(run()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/async_/osc/test_authentication.py b/tests/integration/async_/osc/test_authentication.py new file mode 100644 index 0000000..392d556 --- /dev/null +++ b/tests/integration/async_/osc/test_authentication.py @@ -0,0 +1,25 @@ +import asyncio + +import pytest + +from osc_sdk_python import AsyncClient, SdkClientError +from osc_sdk_python.generated.osc import ReadVmsRequest + + +def test_invalid_credentials_raise_http_error(): + """Test invalid credentials are rejected by the API in the async client""" + + async def run(): + async with AsyncClient( + access_key="invalid-access-key", + secret_key="invalid-secret-key", + max_retries=0, + ) as client: + with pytest.raises(SdkClientError) as exc_info: + await client.osc.read_vms(ReadVmsRequest()) + + assert exc_info.value.response is not None + assert exc_info.value.response.status_code in {400, 401, 403} + assert "code = 4120" in str(exc_info.value) + + asyncio.run(run()) diff --git a/tests/integration/helpers/__init__.py b/tests/integration/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration_utils.py b/tests/integration/helpers/integration_utils.py similarity index 100% rename from tests/integration_utils.py rename to tests/integration/helpers/integration_utils.py diff --git a/tests/integration/sync/__init__.py b/tests/integration/sync/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/sync/oks/__init__.py b/tests/integration/sync/oks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/sync/oks/test_oks_project.py b/tests/integration/sync/oks/test_oks_project.py new file mode 100644 index 0000000..fecfa43 --- /dev/null +++ b/tests/integration/sync/oks/test_oks_project.py @@ -0,0 +1,103 @@ +import time +import unittest + +from osc_sdk_python import Client, SdkHttpError +from tests.integration.helpers.integration_utils import get_tagged_name, log_test_step + + +PROJECT_READY_STATUS = "ready" + + +def wait_project_ready(client, project_id): + for _ in range(36): + response = client.oks.GetProject(project_id=project_id) + project = response.get("Project") + status = project.get("status") + log_test_step("OKS project {} status={}".format(project_id, status)) + if status == PROJECT_READY_STATUS: + return project + time.sleep(10) + + raise AssertionError("OKS project {} did not become ready".format(project_id)) + + +def delete_project_when_ready(client, project_id): + for _ in range(36): + try: + delete_response = client.oks.DeleteProject(project_id=project_id) + log_test_step("Deleted OKS project {}".format(project_id)) + return delete_response + except SdkHttpError as err: + if err.response is None or err.response.status_code != 503: + raise + log_test_step( + "OKS project {} is not ready for deletion yet".format(project_id) + ) + time.sleep(10) + + raise AssertionError("OKS project {} could not be deleted".format(project_id)) + + +class TestOksProject(unittest.TestCase): + def test_project_lifecycle(self): + with Client() as client: + project_id = None + project_name = get_tagged_name("osc-sdk-python-oks-project") + updated_description = "Updated OKS project lifecycle test" + + try: + log_test_step("Reading OKS project template") + template_response = client.oks.GetProjectTemplate() + project_input = template_response.get("Template") + self.assertIsInstance(project_input, dict) + project_input.update( + { + "name": project_name, + "description": "OKS project lifecycle test", + "tags": {"Name": project_name}, + } + ) + + log_test_step("Creating OKS project {}".format(project_name)) + create_response = client.oks.CreateProject(body=project_input) + project = create_response.get("Project") + self.assertIsInstance(project, dict) + project_id = project.get("id") + self.assertTrue(project_id) + self.assertEqual(project.get("name"), project_name) + log_test_step("Created OKS project {}".format(project_id)) + + log_test_step("Reading OKS project {}".format(project_id)) + get_response = client.oks.GetProject(project_id=project_id) + read_project = get_response.get("Project") + self.assertIsInstance(read_project, dict) + self.assertEqual(read_project.get("id"), project_id) + self.assertEqual(read_project.get("name"), project_name) + + read_project = wait_project_ready(client, project_id) + self.assertEqual(read_project.get("id"), project_id) + self.assertEqual(read_project.get("name"), project_name) + + log_test_step("Updating OKS project {}".format(project_id)) + update_response = client.oks.UpdateProject( + project_id=project_id, + body={ + "description": updated_description, + "tags": {"Name": project_name, "Updated": "true"}, + }, + ) + updated_project = update_response.get("Project") + self.assertIsInstance(updated_project, dict) + self.assertEqual(updated_project.get("id"), project_id) + self.assertEqual(updated_project.get("name"), project_name) + self.assertEqual( + updated_project.get("description"), updated_description + ) + finally: + if project_id: + log_test_step("Deleting OKS project {}".format(project_id)) + delete_project_when_ready(client, project_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/sync/osc/__init__.py b/tests/integration/sync/osc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_eim_user.py b/tests/integration/sync/osc/test_eim_user.py similarity index 75% rename from tests/test_eim_user.py rename to tests/integration/sync/osc/test_eim_user.py index 10b8cb6..e15aaf9 100644 --- a/tests/test_eim_user.py +++ b/tests/integration/sync/osc/test_eim_user.py @@ -1,20 +1,19 @@ -import sys import unittest -sys.path.append("..") -from osc_sdk_python import Gateway -from tests.integration_utils import get_tagged_name, log_test_step +from osc_sdk_python import Client +from tests.integration.helpers.integration_utils import get_tagged_name, log_test_step class TestEimUser(unittest.TestCase): def test_eim_user_lifecycle(self): - gw = Gateway() + client = Client() + osc = client.osc user_name = get_tagged_name("osc-sdk-python-user") user_email = "{}@example.com".format(user_name) user_id = None try: log_test_step("Creating EIM user {}".format(user_name)) - response = gw.CreateUser(Path="/", UserEmail=user_email, UserName=user_name) + response = osc.CreateUser(Path="/", UserEmail=user_email, UserName=user_name) user = response.get("User") self.assertIsInstance(user, dict) user_id = user.get("UserId") @@ -25,7 +24,7 @@ def test_eim_user_lifecycle(self): self.assertEqual(user.get("Path"), "/") log_test_step("Reading EIM user {}".format(user_id)) - read_response = gw.ReadUsers(Filters={"UserIds": [user_id]}) + read_response = osc.ReadUsers(Filters={"UserIds": [user_id]}) users = read_response.get("Users") self.assertIsInstance(users, list) self.assertEqual(len(users), 1) @@ -35,7 +34,8 @@ def test_eim_user_lifecycle(self): finally: if user_id: log_test_step("Deleting EIM user {}".format(user_name)) - gw.DeleteUser(UserName=user_name) + osc.DeleteUser(UserName=user_name) + client.close() if __name__ == "__main__": diff --git a/tests/integration/sync/osc/test_exceptions.py b/tests/integration/sync/osc/test_exceptions.py new file mode 100644 index 0000000..10b00ad --- /dev/null +++ b/tests/integration/sync/osc/test_exceptions.py @@ -0,0 +1,15 @@ +import unittest + +from osc_sdk_python import Client, SdkClientError + + +class TestExcept(unittest.TestCase): + def test_listing(self): + with Client() as client: + # a is not a valide argument + with self.assertRaises(SdkClientError): + client.osc.ReadVms(Filters="a") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_exceptions_500.py b/tests/integration/sync/osc/test_exceptions_500.py similarity index 81% rename from tests/test_exceptions_500.py rename to tests/integration/sync/osc/test_exceptions_500.py index 15bf47b..3b9049c 100644 --- a/tests/test_exceptions_500.py +++ b/tests/integration/sync/osc/test_exceptions_500.py @@ -2,14 +2,10 @@ import threading import http.server import socketserver -import sys import os import time -sys.path.append("..") -from osc_sdk_python import Gateway -from requests.exceptions import RetryError -from requests import HTTPError +from osc_sdk_python import Client, SdkServerError, SdkTransportError import copy @@ -60,16 +56,20 @@ def tearDownClass(cls): def test_server_error(self): os.environ["OSC_ENDPOINT_API"] = "http://127.0.0.1:8000" - gw = Gateway() + client = Client() + osc = client.osc # a is not a valide argument - with self.assertRaises(RetryError): - gw.ReadVms() + with self.assertRaises(SdkTransportError): + osc.ReadVms() os.environ.pop("OSC_ENDPOINT_API", None) os.environ["OSC_ENDPOINT_API"] = "http://127.0.0.1:8000" - gw = Gateway() + client.close() + client = Client() + osc = client.osc # a is not a valide argument - with self.assertRaises(HTTPError): - gw.ReadVms() + with self.assertRaises(SdkServerError): + osc.ReadVms() + client.close() if __name__ == "__main__": diff --git a/tests/test_keypair.py b/tests/integration/sync/osc/test_keypair.py similarity index 68% rename from tests/test_keypair.py rename to tests/integration/sync/osc/test_keypair.py index d239add..006ec46 100644 --- a/tests/test_keypair.py +++ b/tests/integration/sync/osc/test_keypair.py @@ -1,19 +1,18 @@ -import sys import unittest -sys.path.append("..") -from osc_sdk_python import Gateway -from tests.integration_utils import get_tagged_name, log_test_step +from osc_sdk_python import Client +from tests.integration.helpers.integration_utils import get_tagged_name, log_test_step class TestKeypair(unittest.TestCase): def test_keypair_lifecycle(self): - gw = Gateway() + client = Client() + osc = client.osc keypair_name = get_tagged_name("osc-sdk-python-keypair") keypair_id = None try: log_test_step("Creating keypair {}".format(keypair_name)) - response = gw.CreateKeypair(KeypairName=keypair_name) + response = osc.CreateKeypair(KeypairName=keypair_name) keypair = response.get("Keypair") self.assertIsInstance(keypair, dict) keypair_id = keypair.get("KeypairId") @@ -22,7 +21,8 @@ def test_keypair_lifecycle(self): finally: if keypair_id: log_test_step("Deleting keypair {}".format(keypair_id)) - gw.DeleteKeypair(KeypairId=keypair_id) + osc.DeleteKeypair(KeypairId=keypair_id) + client.close() if __name__ == "__main__": diff --git a/tests/test_load_balancer_backend.py b/tests/integration/sync/osc/test_load_balancer_backend.py similarity index 84% rename from tests/test_load_balancer_backend.py rename to tests/integration/sync/osc/test_load_balancer_backend.py index 4f0bf6a..55a7cc2 100644 --- a/tests/test_load_balancer_backend.py +++ b/tests/integration/sync/osc/test_load_balancer_backend.py @@ -1,10 +1,8 @@ -import sys import time import unittest -sys.path.append("..") -from osc_sdk_python import Gateway -from tests.integration_utils import ( +from osc_sdk_python import Client +from tests.integration.helpers.integration_utils import ( build_name_tag_request, get_first_subregion_name, get_latest_public_ubuntu_image_id, @@ -17,16 +15,17 @@ class TestLoadBalancerBackend(unittest.TestCase): def test_load_balancer_backend_lifecycle(self): - gw = Gateway() - subregion_name = get_first_subregion_name(gw) - image_id = get_latest_public_ubuntu_image_id(gw) + client = Client() + osc = client.osc + subregion_name = get_first_subregion_name(osc) + image_id = get_latest_public_ubuntu_image_id(osc) log_test_step("Using subregion {} and image {}".format(subregion_name, image_id)) vm_id = None load_balancer_name = get_tagged_name("osc-sdk-python-lb") load_balancer_created = False try: log_test_step("Creating backend VM") - vm_response = gw.CreateVms( + vm_response = osc.CreateVms( ImageId=image_id, MinVmsCount=1, MaxVmsCount=1, @@ -41,11 +40,11 @@ def test_load_balancer_backend_lifecycle(self): self.assertTrue(vm_id) log_test_step("Created backend VM {}".format(vm_id)) - gw.CreateTags(**build_name_tag_request(vm_id)) + osc.CreateTags(**build_name_tag_request(vm_id)) log_test_step("Tagged backend VM {}".format(vm_id)) for _ in range(36): - vm = read_single_resource(gw, "ReadVms", "Vms", "VmIds", vm_id) + vm = read_single_resource(osc, "ReadVms", "Vms", "VmIds", vm_id) log_test_step("VM {} state={}".format(vm_id, vm.get("State"))) if vm.get("State") == "running": break @@ -54,7 +53,7 @@ def test_load_balancer_backend_lifecycle(self): time.sleep(10) log_test_step("Creating load balancer {}".format(load_balancer_name)) - load_balancer_response = gw.CreateLoadBalancer( + load_balancer_response = osc.CreateLoadBalancer( LoadBalancerName=load_balancer_name, Listeners=[ { @@ -71,12 +70,12 @@ def test_load_balancer_backend_lifecycle(self): load_balancer_created = True log_test_step("Linking backend VM {} to {}".format(vm_id, load_balancer_name)) - gw.LinkLoadBalancerBackendMachines( + osc.LinkLoadBalancerBackendMachines( LoadBalancerName=load_balancer_name, BackendVmIds=[vm_id] ) log_test_step("Reading load balancer {}".format(load_balancer_name)) - read_balancers = gw.ReadLoadBalancers( + read_balancers = osc.ReadLoadBalancers( Filters={"LoadBalancerNames": [load_balancer_name]} ) balancers = read_balancers.get("LoadBalancers") @@ -86,7 +85,7 @@ def test_load_balancer_backend_lifecycle(self): health = None for _ in range(18): - health = gw.ReadVmsHealth( + health = osc.ReadVmsHealth( LoadBalancerName=load_balancer_name, BackendVmIds=[vm_id] ) entry_count = len(health.get("BackendVmHealth", []) or []) @@ -113,11 +112,12 @@ def test_load_balancer_backend_lifecycle(self): finally: if load_balancer_created: log_test_step("Deleting load balancer {}".format(load_balancer_name)) - gw.DeleteLoadBalancer(LoadBalancerName=load_balancer_name) + osc.DeleteLoadBalancer(LoadBalancerName=load_balancer_name) if vm_id: time.sleep(1) log_test_step("Deleting backend VM {}".format(vm_id)) - gw.DeleteVms(VmIds=[vm_id]) + osc.DeleteVms(VmIds=[vm_id]) + client.close() if __name__ == "__main__": diff --git a/tests/integration/sync/osc/test_log.py b/tests/integration/sync/osc/test_log.py new file mode 100644 index 0000000..a9400bc --- /dev/null +++ b/tests/integration/sync/osc/test_log.py @@ -0,0 +1,42 @@ +import logging +import unittest + +from osc_sdk_python import Client + + +class TestLog(unittest.TestCase): + def test_listing(self): + with Client() as client: + with self.assertLogs("osc_sdk_python", level=logging.INFO) as logs: + client.osc.ReadVms() + self.assertEqual( + logs.records[-1].getMessage(), + """mode: sync +service: api +method: POST +uri: /api/v1/ReadVms +payload: +{}""", + ) + + with self.assertLogs("osc_sdk_python", level=logging.INFO) as logs: + client.osc.ReadVms(Filters={"TagKeys": ["test"]}) + self.assertEqual( + logs.records[-1].getMessage(), + """mode: sync +service: api +method: POST +uri: /api/v1/ReadVms +payload: +{ + "Filters": { + "TagKeys": [ + "test" + ] + } +}""", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_manual_aksk.py b/tests/integration/sync/osc/test_manual_aksk.py similarity index 66% rename from tests/test_manual_aksk.py rename to tests/integration/sync/osc/test_manual_aksk.py index d148a14..c50c408 100644 --- a/tests/test_manual_aksk.py +++ b/tests/integration/sync/osc/test_manual_aksk.py @@ -1,9 +1,7 @@ import unittest -import sys import os -sys.path.append("..") -from osc_sdk_python import Gateway +from osc_sdk_python import Client import copy @@ -22,10 +20,10 @@ def test_manual_ak_sk(self): sk = os.environ.pop("OSC_SECRET_KEY", None) self.assertIsNotNone(ak) self.assertIsNotNone(sk) - gw = Gateway(access_key=ak, secret_key=sk) - volumes = gw.ReadVolumes() - self.assertIsInstance(volumes, dict) - self.assertIsInstance(volumes.get("Volumes"), list) + with Client(access_key=ak, secret_key=sk) as client: + volumes = client.osc.ReadVolumes() + self.assertIsInstance(volumes, dict) + self.assertIsInstance(volumes.get("Volumes"), list) if __name__ == "__main__": diff --git a/tests/integration/sync/osc/test_net.py b/tests/integration/sync/osc/test_net.py new file mode 100644 index 0000000..6a7e7fb --- /dev/null +++ b/tests/integration/sync/osc/test_net.py @@ -0,0 +1,18 @@ +import unittest + +from osc_sdk_python import Client, SdkClientError + + +class TestNet(unittest.TestCase): + def test_creation_error(self): + with Client() as client: + with self.assertRaises(SdkClientError) as cm: + client.osc.CreateNet(IpRange="142.42.42.42/32") + + e = cm.exception + errors = e.response.json().get("Errors") + self.assertIsNotNone(errors) + self.assertIsInstance(errors, list) + for error in errors: + code = error.get("Code") + self.assertEqual(code, "9050") diff --git a/tests/test_net_subnet.py b/tests/integration/sync/osc/test_net_subnet.py similarity index 68% rename from tests/test_net_subnet.py rename to tests/integration/sync/osc/test_net_subnet.py index 69e2352..d256e9f 100644 --- a/tests/test_net_subnet.py +++ b/tests/integration/sync/osc/test_net_subnet.py @@ -1,10 +1,8 @@ -import sys import time import unittest -sys.path.append("..") -from osc_sdk_python import Gateway -from tests.integration_utils import ( +from osc_sdk_python import Client +from tests.integration.helpers.integration_utils import ( build_name_tag_request, log_test_step, read_single_resource, @@ -13,34 +11,35 @@ class TestNetAndSubnet(unittest.TestCase): def test_net_and_subnet_lifecycle(self): - gw = Gateway() + client = Client() + osc = client.osc net_id = None subnet_id = None try: log_test_step("Creating net 10.0.0.0/16") - net_response = gw.CreateNet(IpRange="10.0.0.0/16") + net_response = osc.CreateNet(IpRange="10.0.0.0/16") net = net_response.get("Net") self.assertIsInstance(net, dict) net_id = net.get("NetId") self.assertTrue(net_id) log_test_step("Created net {}".format(net_id)) - gw.CreateTags(**build_name_tag_request(net_id)) + osc.CreateTags(**build_name_tag_request(net_id)) time.sleep(2) log_test_step("Creating subnet 10.0.1.0/24 in {}".format(net_id)) - subnet_response = gw.CreateSubnet(NetId=net_id, IpRange="10.0.1.0/24") + subnet_response = osc.CreateSubnet(NetId=net_id, IpRange="10.0.1.0/24") subnet = subnet_response.get("Subnet") self.assertIsInstance(subnet, dict) subnet_id = subnet.get("SubnetId") self.assertTrue(subnet_id) log_test_step("Created subnet {}".format(subnet_id)) - gw.CreateTags(**build_name_tag_request(subnet_id)) + osc.CreateTags(**build_name_tag_request(subnet_id)) time.sleep(2) log_test_step("Reading subnet {}".format(subnet_id)) - subnet = read_single_resource(gw, "ReadSubnets", "Subnets", "SubnetIds", subnet_id) + subnet = read_single_resource(osc, "ReadSubnets", "Subnets", "SubnetIds", subnet_id) self.assertEqual(subnet.get("SubnetId"), subnet_id) self.assertTrue( any(tag.get("Key") == "Name" for tag in subnet.get("Tags", [])), @@ -48,15 +47,16 @@ def test_net_and_subnet_lifecycle(self): ) log_test_step("Updating subnet {}".format(subnet_id)) - updated = gw.UpdateSubnet(SubnetId=subnet_id, MapPublicIpOnLaunch=False) + updated = osc.UpdateSubnet(SubnetId=subnet_id, MapPublicIpOnLaunch=False) self.assertIsInstance(updated.get("Subnet"), dict) finally: if subnet_id: log_test_step("Deleting subnet {}".format(subnet_id)) - gw.DeleteSubnet(SubnetId=subnet_id) + osc.DeleteSubnet(SubnetId=subnet_id) if net_id: log_test_step("Deleting net {}".format(net_id)) - gw.DeleteNet(NetId=net_id) + osc.DeleteNet(NetId=net_id) + client.close() if __name__ == "__main__": diff --git a/tests/test_password.py b/tests/integration/sync/osc/test_password.py similarity index 74% rename from tests/test_password.py rename to tests/integration/sync/osc/test_password.py index e67457b..a35f200 100644 --- a/tests/test_password.py +++ b/tests/integration/sync/osc/test_password.py @@ -1,9 +1,7 @@ import unittest -import sys import os -sys.path.append("..") -from osc_sdk_python import Gateway +from osc_sdk_python import Client import copy @@ -28,10 +26,10 @@ def test_login(self): password = os.getenv("OSC_TEST_PASSWORD") self.assertIsNotNone(email, None) self.assertIsNotNone(password, None) - gw = Gateway(email=email, password=password) - keys = gw.ReadAccessKeys() - self.assertIsInstance(keys, dict) - self.assertIsInstance(keys.get("AccessKeys"), list) + with Client(email=email, password=password) as client: + keys = client.osc.ReadAccessKeys() + self.assertIsInstance(keys, dict) + self.assertIsInstance(keys.get("AccessKeys"), list) if __name__ == "__main__": diff --git a/tests/test_security_group.py b/tests/integration/sync/osc/test_security_group.py similarity index 83% rename from tests/test_security_group.py rename to tests/integration/sync/osc/test_security_group.py index ce4811c..70ea3bc 100644 --- a/tests/test_security_group.py +++ b/tests/integration/sync/osc/test_security_group.py @@ -1,20 +1,19 @@ -import sys import unittest -sys.path.append("..") -from osc_sdk_python import Gateway -from tests.integration_utils import get_tagged_name, log_test_step +from osc_sdk_python import Client +from tests.integration.helpers.integration_utils import get_tagged_name, log_test_step class TestSecurityGroup(unittest.TestCase): def test_security_group_lifecycle(self): - gw = Gateway() + client = Client() + osc = client.osc security_group_id = None tcp = "tcp" ip_range = "0.0.0.0/0" try: log_test_step("Creating security group") - response = gw.CreateSecurityGroup( + response = osc.CreateSecurityGroup( SecurityGroupName=get_tagged_name("osc-sdk-python-sg"), Description="Test security group lifecycle", ) @@ -25,7 +24,7 @@ def test_security_group_lifecycle(self): log_test_step("Created security group {}".format(security_group_id)) log_test_step("Creating inbound SSH rule on {}".format(security_group_id)) - rule_response = gw.CreateSecurityGroupRule( + rule_response = osc.CreateSecurityGroupRule( SecurityGroupId=security_group_id, Flow="Inbound", IpProtocol=tcp, @@ -36,7 +35,7 @@ def test_security_group_lifecycle(self): self.assertIsInstance(rule_response.get("SecurityGroup"), dict) log_test_step("Reading security group {}".format(security_group_id)) - read_response = gw.ReadSecurityGroups( + read_response = osc.ReadSecurityGroups( Filters={"SecurityGroupIds": [security_group_id]} ) security_groups = read_response.get("SecurityGroups") @@ -56,7 +55,7 @@ def test_security_group_lifecycle(self): ) log_test_step("Deleting inbound SSH rule on {}".format(security_group_id)) - gw.DeleteSecurityGroupRule( + osc.DeleteSecurityGroupRule( SecurityGroupId=security_group_id, Flow="Inbound", IpProtocol=tcp, @@ -67,7 +66,8 @@ def test_security_group_lifecycle(self): finally: if security_group_id: log_test_step("Deleting security group {}".format(security_group_id)) - gw.DeleteSecurityGroup(SecurityGroupId=security_group_id) + osc.DeleteSecurityGroup(SecurityGroupId=security_group_id) + client.close() if __name__ == "__main__": diff --git a/tests/test_snapshot.py b/tests/integration/sync/osc/test_snapshot.py similarity index 78% rename from tests/test_snapshot.py rename to tests/integration/sync/osc/test_snapshot.py index 151f692..9a9f01f 100644 --- a/tests/test_snapshot.py +++ b/tests/integration/sync/osc/test_snapshot.py @@ -1,10 +1,8 @@ -import sys import time import unittest -sys.path.append("..") -from osc_sdk_python import Gateway -from tests.integration_utils import ( +from osc_sdk_python import Client +from tests.integration.helpers.integration_utils import ( build_name_tag_request, get_first_subregion_name, get_tagged_name, @@ -15,26 +13,27 @@ class TestSnapshot(unittest.TestCase): def test_snapshot_lifecycle(self): - gw = Gateway() - subregion_name = get_first_subregion_name(gw) + client = Client() + osc = client.osc + subregion_name = get_first_subregion_name(osc) log_test_step("Using subregion {}".format(subregion_name)) volume_id = None snapshot_id = None description = get_tagged_name("osc-sdk-python-snapshot") try: log_test_step("Creating source volume") - volume_response = gw.CreateVolume(Size=10, SubregionName=subregion_name) + volume_response = osc.CreateVolume(Size=10, SubregionName=subregion_name) volume = volume_response.get("Volume") self.assertIsInstance(volume, dict) volume_id = volume.get("VolumeId") self.assertTrue(volume_id) log_test_step("Created volume {}".format(volume_id)) - gw.CreateTags(**build_name_tag_request(volume_id)) + osc.CreateTags(**build_name_tag_request(volume_id)) log_test_step("Tagged volume {}".format(volume_id)) for _ in range(30): - volume = read_single_resource(gw, "ReadVolumes", "Volumes", "VolumeIds", volume_id) + volume = read_single_resource(osc, "ReadVolumes", "Volumes", "VolumeIds", volume_id) log_test_step("Volume {} state={}".format(volume_id, volume.get("State"))) if volume.get("State") == "available": break @@ -47,20 +46,20 @@ def test_snapshot_lifecycle(self): time.sleep(10) log_test_step("Creating snapshot from volume {}".format(volume_id)) - snapshot_response = gw.CreateSnapshot(Description=description, VolumeId=volume_id) + snapshot_response = osc.CreateSnapshot(Description=description, VolumeId=volume_id) snapshot = snapshot_response.get("Snapshot") self.assertIsInstance(snapshot, dict) snapshot_id = snapshot.get("SnapshotId") self.assertTrue(snapshot_id) log_test_step("Created snapshot {}".format(snapshot_id)) - gw.CreateTags(**build_name_tag_request(snapshot_id)) + osc.CreateTags(**build_name_tag_request(snapshot_id)) log_test_step("Tagged snapshot {}".format(snapshot_id)) snapshot = None for _ in range(60): snapshot = read_single_resource( - gw, "ReadSnapshots", "Snapshots", "SnapshotIds", snapshot_id + osc, "ReadSnapshots", "Snapshots", "SnapshotIds", snapshot_id ) log_test_step("Snapshot {} state={}".format(snapshot_id, snapshot.get("State"))) if snapshot.get("State") == "completed": @@ -86,11 +85,12 @@ def test_snapshot_lifecycle(self): finally: if snapshot_id: log_test_step("Deleting snapshot {}".format(snapshot_id)) - gw.DeleteSnapshot(SnapshotId=snapshot_id) + osc.DeleteSnapshot(SnapshotId=snapshot_id) if volume_id: time.sleep(1) log_test_step("Deleting volume {}".format(volume_id)) - gw.DeleteVolume(VolumeId=volume_id) + osc.DeleteVolume(VolumeId=volume_id) + client.close() if __name__ == "__main__": diff --git a/tests/integration/sync/osc/test_vm.py b/tests/integration/sync/osc/test_vm.py new file mode 100644 index 0000000..d17d250 --- /dev/null +++ b/tests/integration/sync/osc/test_vm.py @@ -0,0 +1,19 @@ +import unittest + +from osc_sdk_python import Client + +class TestVm(unittest.TestCase): + def test_listing(self): + with Client() as client: + vms = client.osc.ReadVms() + self.assertEqual(type(vms), dict) + self.assertEqual(type(vms.get("Vms")), list) + + def test_listing_with_context_manager(self): + with Client() as client: + vms = client.osc.ReadVms() + self.assertEqual(type(vms), dict) + self.assertEqual(type(vms.get("Vms")), list) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/integration/sync/osc/test_volume.py b/tests/integration/sync/osc/test_volume.py new file mode 100644 index 0000000..aa7fb08 --- /dev/null +++ b/tests/integration/sync/osc/test_volume.py @@ -0,0 +1,13 @@ +import unittest + +from osc_sdk_python import Client + +class TestVolume(unittest.TestCase): + def test_listing(self): + with Client() as client: + volumes = client.osc.ReadVolumes() + self.assertEqual(type(volumes), dict) + self.assertEqual(type(volumes.get("Volumes")), list) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py deleted file mode 100644 index 3832812..0000000 --- a/tests/test_exceptions.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest -import sys - -sys.path.append("..") -from osc_sdk_python import Gateway -from requests.exceptions import HTTPError - - -class TestExcept(unittest.TestCase): - def test_listing(self): - gw = Gateway() - # a is not a valide argument - with self.assertRaises(HTTPError): - gw.ReadVms(Filters="a") - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_log.py b/tests/test_log.py deleted file mode 100644 index 901d012..0000000 --- a/tests/test_log.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest -import sys - -sys.path.append("..") -from osc_sdk_python import Gateway, LOG_MEMORY, LOG_KEEP_ONLY_LAST_REQ - - -class TestLog(unittest.TestCase): - def test_listing(self): - gw = Gateway() - gw.log.config(type=LOG_MEMORY, what=LOG_KEEP_ONLY_LAST_REQ) - gw.ReadVms() - self.assertEqual( - gw.log.str(), - """uri: /api/v1/ReadVms -payload: -{}""", - ) - - gw.ReadVms(Filters={"TagKeys": ["test"]}) - self.assertEqual( - gw.log.str(), - """uri: /api/v1/ReadVms -payload: -{ - "Filters": { - "TagKeys": [ - "test" - ] - } -}""", - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_net.py b/tests/test_net.py deleted file mode 100644 index 8055c6f..0000000 --- a/tests/test_net.py +++ /dev/null @@ -1,21 +0,0 @@ -import unittest -import sys -import requests - -sys.path.append("..") -from osc_sdk_python import Gateway - - -class TestNet(unittest.TestCase): - def test_creation_error(self): - gw = Gateway() - with self.assertRaises(requests.exceptions.HTTPError) as cm: - gw.CreateNet(IpRange="142.42.42.42/32") - - e = cm.exception - errors = e.response.json().get("Errors") - self.assertIsNotNone(errors) - self.assertIsInstance(errors, list) - for error in errors: - code = error.get("Code") - self.assertEqual(code, "9050") diff --git a/tests/test_retry.py b/tests/test_retry.py deleted file mode 100644 index 6709f02..0000000 --- a/tests/test_retry.py +++ /dev/null @@ -1,235 +0,0 @@ -import pytest -import requests -from unittest.mock import Mock, patch -from requests.exceptions import RequestException, HTTPError, ConnectionError - -from osc_sdk_python.retry import Retry - - -class TestRetry: - """Test cases for the Retry class""" - - def setup_method(self): - """Set up test fixtures""" - self.mock_session = Mock(spec=requests.Session) - self.method = "POST" - self.url = "https://api.test-region.outscalce.com/" - self.base_kwargs = {"timeout": 30} - - def build_response(self, status_code, reason): - mock_response = Mock(spec=requests.Response) - mock_response.status_code = status_code - mock_response.reason = reason - mock_response.headers = {"content-type": "application/json"} - mock_response.url = self.url - return mock_response - - def build_response_success(self): - return self.build_response(200, "OK") - - def test_execute_once_success(self): - """Test execute_once method with successful request""" - mock_response = Mock(spec=requests.Response) - self.mock_session.request.return_value = mock_response - - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - result = retry.execute_once() - - assert result == mock_response - self.mock_session.request.assert_called_once_with( - self.method, self.url, **self.base_kwargs - ) - - def test_should_retry_with_4xx_error(self): - """Test should_retry returns False for 4xx client errors""" - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - - mock_response = Mock() - mock_response.status_code = 400 - exception = RequestException() - exception.response = mock_response - - assert not retry.should_retry(exception) - - def test_should_retry_with_429_error(self): - """Test should_retry returns True for 429 client errors""" - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - - mock_response = Mock() - mock_response.status_code = 429 - exception = RequestException() - exception.response = mock_response - - assert retry.should_retry(exception) - - def test_should_retry_with_5xx_error_under_limit(self): - """Test should_retry returns True for 5xx server errors under retry limit""" - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - - mock_response = Mock() - mock_response.status_code = 500 - exception = RequestException() - exception.response = mock_response - - assert retry.should_retry(exception) - - def test_should_retry_with_no_response(self): - """Test should_retry returns True for exceptions without response""" - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - - exception = ConnectionError() - exception.response = None - - assert retry.should_retry(exception) - - def test_should_retry_at_max_retries(self): - """Test should_retry returns False when at max retries""" - retry = Retry( - self.mock_session, self.method, self.url, attempt=3, **self.base_kwargs - ) - - exception = ConnectionError() - exception.response = None - - assert not retry.should_retry(exception) - - @patch("random.uniform") - def test_get_backoff_time(self, mock_random): - """Test get_backoff_time calculation""" - mock_random.return_value = 1.5 - - retry = Retry( - self.mock_session, self.method, self.url, attempt=2, **self.base_kwargs - ) - - expected_backoff = 1.0 * (2**2) + 1.5 # backoff_factor * (2^attempt) + jitter - assert retry.get_backoff_time() == expected_backoff - - @patch("random.uniform") - def test_get_backoff_time_with_max(self, mock_random): - """Test get_backoff_time respects backoff_max""" - mock_random.return_value = 5.0 - - retry = Retry( - self.mock_session, - self.method, - self.url, - attempt=10, - backoff_factor=2.0, - backoff_max=10.0, - **self.base_kwargs, - ) - - assert retry.get_backoff_time() == 10.0 - - def test_execute_success_no_retry(self): - """Test execute method with successful request""" - mock_response = self.build_response_success() - self.mock_session.request.return_value = mock_response - - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - result = retry.execute() - - assert result == mock_response - self.mock_session.request.assert_called_once() - - @patch("time.sleep") - @patch("random.uniform") - def test_execute_with_retry_success(self, mock_random, mock_sleep): - """Test execute method with retry leading to success""" - mock_random.return_value = 1.0 - - # First call fails, second succeeds - mock_response_fail = self.build_response(500, "Internal Server Error") - - # Configure the successful response - mock_response_success = self.build_response_success() - - self.mock_session.request.side_effect = [ - mock_response_fail, - mock_response_success, - ] - - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - result = retry.execute() - - assert result == mock_response_success - assert self.mock_session.request.call_count == 2 - mock_sleep.assert_called_once() - - def test_execute_with_4xx_error_no_retry(self): - """Test execute method doesn't retry on 4xx errors""" - mock_response = self.build_response(400, "Bad Request") - - self.mock_session.request.return_value = mock_response - - retry = Retry(self.mock_session, self.method, self.url, **self.base_kwargs) - - with pytest.raises(HTTPError): - retry.execute() - - self.mock_session.request.assert_called_once() - - @patch("time.sleep") - @patch("random.uniform") - def test_execute_with_429_error_retry(self, mock_random, mock_sleep): - """Test execute method retry on 429 errors""" - mock_random.return_value = 1.0 - - mock_response = self.build_response(429, "Too Many Requests") - self.mock_session.request.return_value = mock_response - - retry = Retry( - self.mock_session, self.method, self.url, max_retries=2, **self.base_kwargs - ) - - with pytest.raises(HTTPError): - retry.execute() - - # Should try 2 times (initial + 2 retries) - assert self.mock_session.request.call_count == 3 - assert mock_sleep.call_count == 2 - - @patch("time.sleep") - @patch("random.uniform") - def test_execute_with_500_error_retry_wrong_content_type( - self, mock_random, mock_sleep - ): - """Test execute method retry on 500 errors""" - mock_random.return_value = 1.0 - - mock_response = self.build_response(500, "Internal Server Error") - mock_response.headers["content-type"] = "text/plain" - self.mock_session.request.return_value = mock_response - - retry = Retry( - self.mock_session, self.method, self.url, max_retries=2, **self.base_kwargs - ) - - with pytest.raises(HTTPError): - retry.execute() - - # Should try 2 times (initial + 2 retries) - assert self.mock_session.request.call_count == 3 - assert mock_sleep.call_count == 2 - - @patch("time.sleep") - @patch("random.uniform") - def test_execute_max_retries_exceeded(self, mock_random, mock_sleep): - """Test execute method when max retries are exceeded""" - mock_random.return_value = 1.0 - - exception = ConnectionError() - exception.response = None - self.mock_session.request.side_effect = exception - - retry = Retry( - self.mock_session, self.method, self.url, max_retries=2, **self.base_kwargs - ) - - with pytest.raises(ConnectionError): - retry.execute() - - # Should try 3 times (initial + 2 retries) - assert self.mock_session.request.call_count == 3 - assert mock_sleep.call_count == 2 diff --git a/tests/test_vm.py b/tests/test_vm.py deleted file mode 100644 index 98b3dd3..0000000 --- a/tests/test_vm.py +++ /dev/null @@ -1,21 +0,0 @@ -import unittest -import sys - -sys.path.append("..") -from osc_sdk_python import Gateway - -class TestVm(unittest.TestCase): - def test_listing(self): - gw = Gateway() - vms = gw.ReadVms() - self.assertEqual(type(vms), dict) - self.assertEqual(type(vms.get("Vms")), list) - - def test_listing_with_context_manager(self): - with Gateway() as gw: - vms = gw.ReadVms() - self.assertEqual(type(vms), dict) - self.assertEqual(type(vms.get("Vms")), list) - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_volume.py b/tests/test_volume.py deleted file mode 100644 index dbc3dd9..0000000 --- a/tests/test_volume.py +++ /dev/null @@ -1,15 +0,0 @@ -import unittest -import sys - -sys.path.append("..") -from osc_sdk_python import Gateway - -class TestVolume(unittest.TestCase): - def test_listing(self): - gw = Gateway() - volumes = gw.ReadVolumes() - self.assertEqual(type(volumes), dict) - self.assertEqual(type(volumes.get("Volumes")), list) - -if __name__ == "__main__": - unittest.main() diff --git a/tests/unit/codegen/__init__.py b/tests/unit/codegen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/codegen/test_codegen_openapi_adapter.py b/tests/unit/codegen/test_codegen_openapi_adapter.py new file mode 100644 index 0000000..b42cf97 --- /dev/null +++ b/tests/unit/codegen/test_codegen_openapi_adapter.py @@ -0,0 +1,588 @@ +from osc_sdk_python.codegen.adapters import PathOperationAdapter +from osc_sdk_python.codegen.generator import ( + render_async_client, + render_init, + render_models, +) + + +def test_action_body_schema_reuses_component_request_model(): + """Ensure action-style request bodies reuse existing request models.""" + spec = { + "paths": { + "/CreateVms": { + "post": { + "operationId": "CreateVms", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateVmsRequest" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateVmsResponse" + } + } + } + } + }, + } + } + }, + "components": { + "schemas": { + "CreateVmsRequest": { + "type": "object", + "properties": {"ImageId": {"type": "string"}}, + }, + "CreateVmsResponse": { + "type": "object", + "properties": { + "Vms": {"type": "array", "items": {"type": "object"}} + }, + }, + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + operations = adapter.operations() + models = adapter.schema_models() + + assert operations[0].request_model.name == "CreateVmsRequest" + assert operations[0].uses_request_as_body is True + + rendered_models = render_models(operations, models, "osc") + assert rendered_models.count("class CreateVmsRequest") == 1 + + rendered_client = render_async_client(operations, "api", "osc") + assert "class AsyncOscTypedMixin:" in rendered_client + assert 'service="api"' in rendered_client + assert "json_body=_dump_json_body(request)," in rendered_client + assert "from pydantic import TypeAdapter" in rendered_client + assert "return _validate_response(CreateVmsResponse, response)" in rendered_client + + rendered_init = render_init(operations, models, "osc") + assert "AsyncOscTypedMixin" in rendered_init + + +def test_path_query_service_generates_combined_request_model(): + """Ensure REST path and query parameters are exposed through one request model.""" + spec = { + "paths": { + "/projects/{project_id}": { + "get": { + "operationId": "GetProject", + "parameters": [ + { + "name": "project_id", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + { + "name": "deleted", + "in": "query", + "schema": {"type": "boolean"}, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectResponse" + } + } + } + } + }, + } + } + }, + "components": { + "schemas": { + "ProjectResponse": { + "type": "object", + "properties": {"Id": {"type": "string"}}, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="oks") + operations = adapter.operations() + + assert operations[0].request_model.name == "GetProjectRequest" + assert operations[0].uses_request_as_body is False + assert operations[0].path_fields[0].name == "project_id" + assert operations[0].query_fields[0].name == "deleted" + + rendered_client = render_async_client(operations, "oks", "oks") + assert "class AsyncOksTypedMixin:" in rendered_client + assert 'service="oks"' in rendered_client + assert "'project_id': request.project_id" in rendered_client + assert "'deleted': request.deleted" in rendered_client + + +def test_rest_body_schema_keeps_operation_request_wrapper(): + """Ensure REST request bodies are wrapped so body fields can coexist with params.""" + spec = { + "paths": { + "/projects": { + "post": { + "operationId": "CreateProject", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ProjectInput"} + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectResponse" + } + } + } + } + }, + } + } + }, + "components": { + "schemas": { + "ProjectInput": { + "type": "object", + "properties": {"name": {"type": "string"}}, + }, + "ProjectResponse": { + "type": "object", + "properties": {"id": {"type": "string"}}, + }, + } + }, + } + + adapter = PathOperationAdapter(spec, service="oks") + operations = adapter.operations() + + assert operations[0].request_model.name == "CreateProjectRequest" + assert operations[0].uses_request_as_body is False + assert operations[0].body_field.annotation == "ProjectInput" + + rendered_client = render_async_client(operations, "oks", "oks") + assert "request: CreateProjectRequest | None = None" in rendered_client + assert "json_body=_dump_json_body(request.body)" in rendered_client + + +def test_scalar_enums_render_as_literals(): + """Ensure scalar enum schemas render as Literal annotations in generated models.""" + spec = { + "paths": {}, + "components": { + "schemas": { + "BootMode": { + "type": "string", + "enum": ["uefi", "legacy"], + }, + "Vm": { + "type": "object", + "properties": { + "BootMode": {"$ref": "#/components/schemas/BootMode"}, + "State": { + "type": "string", + "enum": ["pending", "running"], + }, + }, + }, + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "from typing import Literal" in rendered_models + assert "BootMode = Literal['uefi', 'legacy']" in rendered_models + assert "boot_mode: BootMode | None" in rendered_models + assert "state: Literal['pending', 'running'] | None" in rendered_models + + +def test_required_schema_fields_render_without_default_none(): + """Ensure required generated model fields are not made optional with default None.""" + spec = { + "paths": {}, + "components": { + "schemas": { + "CreateVmRequest": { + "type": "object", + "required": ["ImageId"], + "properties": { + "ImageId": {"type": "string"}, + "DryRun": {"type": "boolean"}, + }, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "image_id: str = Field(alias='ImageId')" in rendered_models + assert ( + "dry_run: bool | None = Field(default=None, alias='DryRun')" in rendered_models + ) + + +def test_datetime_format_renders_datetime_annotation(): + spec = { + "paths": {}, + "components": { + "schemas": { + "Event": { + "type": "object", + "properties": { + "CreatedAt": {"type": "string", "format": "date-time"}, + }, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "import datetime" in rendered_models + assert "created_at: datetime.datetime | None" in rendered_models + + +def test_unknown_schema_type_warns_and_uses_any(caplog): + spec = { + "paths": {}, + "components": { + "schemas": { + "Thing": { + "type": "object", + "properties": { + "Value": {}, + }, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + with caplog.at_level("WARNING", logger="osc_sdk_python.codegen"): + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "value: Any | None" in rendered_models + assert "OpenAPI schema without a supported type" in caplog.text + + +def test_multi_schema_composition_warns_and_uses_any(caplog): + spec = { + "paths": {}, + "components": { + "schemas": { + "Thing": { + "type": "object", + "properties": { + "Value": { + "oneOf": [ + {}, + {"type": "integer"}, + ] + } + }, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + with caplog.at_level("WARNING", logger="osc_sdk_python.codegen"): + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "value: Any | None" in rendered_models + assert "OpenAPI oneOf with 2 schemas" in caplog.text + + +def test_oneof_composition_renders_union_with_exclusivity_warning(caplog): + spec = { + "paths": {}, + "components": { + "schemas": { + "Thing": { + "type": "object", + "properties": { + "Value": { + "oneOf": [ + {"type": "string"}, + {"type": "integer"}, + ] + } + }, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + with caplog.at_level("WARNING", logger="osc_sdk_python.codegen"): + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "value: str | int | None" in rendered_models + assert "OpenAPI oneOf with 2 schemas represented as a union" in caplog.text + assert "exclusivity is not enforced" in caplog.text + + +def test_anyof_composition_renders_union_for_supported_schemas(): + spec = { + "paths": {}, + "components": { + "schemas": { + "ValidationDetail": { + "type": "object", + "properties": {"Msg": {"type": "string"}}, + }, + "ErrorItem": { + "type": "object", + "properties": { + "Details": { + "anyOf": [ + {"type": "string"}, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationDetail" + }, + }, + ] + }, + "Loc": { + "type": "array", + "items": { + "anyOf": [ + {"type": "string"}, + {"type": "integer"}, + ] + }, + }, + }, + }, + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "details: str | list[ValidationDetail] | None" in rendered_models + assert "loc: list[str | int] | None" in rendered_models + + +def test_anyof_null_composition_renders_optional_concrete_types(): + spec = { + "paths": { + "/projects": { + "get": { + "operationId": "ListProjects", + "parameters": [ + { + "name": "name", + "in": "query", + "schema": { + "anyOf": [ + {"type": "string"}, + {"type": "null"}, + ] + }, + }, + { + "name": "limit", + "in": "query", + "schema": { + "anyOf": [ + {"type": "integer"}, + {"type": "null"}, + ] + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectResponse" + } + } + } + } + }, + } + } + }, + "components": { + "schemas": { + "Maintenance": { + "type": "object", + "properties": {"start_hour": {"type": "integer"}}, + }, + "Cluster": { + "type": "object", + "properties": { + "maintenance_window": { + "anyOf": [ + {"$ref": "#/components/schemas/Maintenance"}, + {"type": "null"}, + ] + }, + "tags": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {"type": "string"}, + }, + {"type": "null"}, + ] + }, + "quirks": { + "anyOf": [ + {"type": "array", "items": {"type": "string"}}, + {"type": "null"}, + ] + }, + }, + }, + "ProjectResponse": { + "type": "object", + "properties": {"id": {"type": "string"}}, + }, + } + }, + } + + adapter = PathOperationAdapter(spec, service="oks") + rendered_models = render_models(adapter.operations(), adapter.schema_models(), "oks") + + assert "maintenance_window: Maintenance | None" in rendered_models + assert "tags: dict[str, str] | None" in rendered_models + assert "quirks: list[str] | None" in rendered_models + assert "name: str | None" in rendered_models + assert "limit: int | None" in rendered_models + +def test_single_ref_allof_with_metadata_keeps_ref_type(): + spec = { + "paths": {}, + "components": { + "schemas": { + "BaseThing": { + "type": "object", + "properties": {"Id": {"type": "string"}}, + }, + "Thing": { + "type": "object", + "properties": { + "Value": { + "allOf": [ + {"$ref": "#/components/schemas/BaseThing"}, + {"description": "same schema with docs"}, + ] + } + }, + }, + } + }, + } + + adapter = PathOperationAdapter(spec, service="api") + rendered_models = render_models([], adapter.schema_models(), "osc") + + assert "value: BaseThing | None" in rendered_models + + +def test_non_model_response_uses_type_adapter(): + """Ensure primitive or collection responses are validated through TypeAdapter.""" + spec = { + "paths": { + "/names": { + "get": { + "operationId": "ListNames", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": {"type": "string"}, + } + } + } + } + }, + } + } + } + } + + adapter = PathOperationAdapter(spec, service="oks") + rendered_client = render_async_client(adapter.operations(), "oks", "oks") + + assert "async def list_names(" in rendered_client + assert " ) -> list[str]:" in rendered_client + assert "return _validate_response(list[str], response)" in rendered_client + + +def test_requestless_operation_does_not_create_unused_request_variable(): + """Ensure requestless methods keep API compatibility without unused variables.""" + spec = { + "paths": { + "/clusters/limits/kubernetes_versions": { + "get": { + "operationId": "GetKubernetesVersions", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KubernetesVersionsResponse" + } + } + } + } + }, + } + } + }, + "components": { + "schemas": { + "KubernetesVersionsResponse": { + "type": "object", + "properties": { + "versions": {"type": "array", "items": {"type": "string"}} + }, + } + } + }, + } + + adapter = PathOperationAdapter(spec, service="oks") + rendered_client = render_async_client(adapter.operations(), "oks", "oks") + + assert "request: GetKubernetesVersionsRequest | None = None" in rendered_client + assert "request = GetKubernetesVersionsRequest()" not in rendered_client + assert "_ = request" in rendered_client + assert 'path="/clusters/limits/kubernetes_versions"' in rendered_client diff --git a/tests/unit/codegen/test_codegen_overlay.py b/tests/unit/codegen/test_codegen_overlay.py new file mode 100644 index 0000000..f12684c --- /dev/null +++ b/tests/unit/codegen/test_codegen_overlay.py @@ -0,0 +1,188 @@ +from pathlib import Path +import shutil +from uuid import uuid4 + +from osc_sdk_python.codegen.overlay import apply_overlay, load_spec + + +def test_overlay_updates_wildcard_schema_property(): + spec = { + "components": { + "schemas": { + "Vm": { + "type": "object", + "properties": { + "State": {"type": "string"}, + }, + } + } + } + } + overlay = { + "actions": [ + { + "target": "$.components.schemas.Vm.*.State", + "update": {"enum": ["pending", "running"]}, + } + ] + } + + patched = apply_overlay(spec, overlay) + + assert patched["components"]["schemas"]["Vm"]["properties"]["State"]["enum"] == [ + "pending", + "running", + ] + + +def test_overlay_removes_targeted_key(): + spec = { + "components": { + "schemas": { + "ReadVmsRequest": { + "properties": { + "NextPageToken": {"type": "string", "format": "uuid"}, + } + } + } + } + } + overlay = { + "actions": [ + { + "target": "$.components.schemas.*.*.NextPageToken.format", + "remove": True, + } + ] + } + + patched = apply_overlay(spec, overlay) + + assert "format" not in patched["components"]["schemas"]["ReadVmsRequest"][ + "properties" + ]["NextPageToken"] + + +def test_overlay_removes_list_matches_in_reverse_index_order(): + spec = { + "parameters": [ + {"name": "keep", "deprecated": False}, + {"name": "first", "deprecated": True}, + {"name": "second", "deprecated": True}, + {"name": "keep2", "deprecated": False}, + ] + } + overlay = { + "actions": [ + { + "target": "$.parameters[?(@.deprecated == 'True')]", + "remove": True, + } + ] + } + + patched = apply_overlay(spec, overlay) + + assert [parameter["name"] for parameter in patched["parameters"]] == [ + "keep", + "keep2", + ] + + +def test_overlay_updates_quoted_path_key(): + spec = { + "paths": { + "/DeleteSecurityGroup": { + "post": { + "responses": {}, + } + } + } + } + overlay = { + "actions": [ + { + "target": '$.paths["/DeleteSecurityGroup"].post.responses', + "update": {"409": {"description": "Conflict"}}, + } + ] + } + + patched = apply_overlay(spec, overlay) + + assert patched["paths"]["/DeleteSecurityGroup"]["post"]["responses"]["409"] == { + "description": "Conflict" + } + + +def test_overlay_filters_matching_children(): + spec = { + "components": { + "schemas": { + "CreateVmsRequest": { + "properties": { + "Nics": {"type": "array"}, + "ImageId": {"type": "string"}, + } + } + } + } + } + overlay = { + "actions": [ + { + "target": "$.components.schemas.CreateVmsRequest.*.*[?(@.type == 'array')]", + "update": {"x-rs-type-skip-optional-pointer": True}, + } + ] + } + + patched = apply_overlay(spec, overlay) + + assert patched["components"]["schemas"]["CreateVmsRequest"]["properties"]["Nics"][ + "x-rs-type-skip-optional-pointer" + ] + assert "x-rs-type-skip-optional-pointer" not in patched["components"]["schemas"][ + "CreateVmsRequest" + ]["properties"]["ImageId"] + + +def test_cfg_loads_spec_and_applies_overlay(): + tmp_path = Path("tests/unit") / f".overlay-{uuid4().hex}" + tmp_path.mkdir() + try: + (tmp_path / "api.yaml").write_text( + """ +components: + schemas: + Vm: + type: object + properties: + State: + type: string +""" + ) + (tmp_path / "patch.yaml").write_text( + """ +actions: + - target: $.components.schemas.Vm.*.State + update: + enum: + - pending +""" + ) + cfg = tmp_path / "cfg.yaml" + cfg.write_text( + """ +spec: ./api.yaml +overlay: ./patch.yaml +""" + ) + + spec = load_spec(cfg) + + assert spec["components"]["schemas"]["Vm"]["properties"]["State"]["enum"] == [ + "pending" + ] + finally: + shutil.rmtree(tmp_path, ignore_errors=True) diff --git a/tests/unit/config/__init__.py b/tests/unit/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/config/test_profile_config.py b/tests/unit/config/test_profile_config.py new file mode 100644 index 0000000..5e9b3b2 --- /dev/null +++ b/tests/unit/config/test_profile_config.py @@ -0,0 +1,183 @@ +import json + +import pytest + +from osc_sdk_python import Client +from osc_sdk_python.credentials import Profile +from osc_sdk_python.exceptions import SdkConfigurationError + + +def test_default_region_and_protocol_are_used(monkeypatch): + """Test default region and protocol are set when no config is provided""" + monkeypatch.delenv("OSC_CONFIG_FILE", raising=False) + monkeypatch.delenv("OSC_PROFILE", raising=False) + monkeypatch.delenv("OSC_REGION", raising=False) + monkeypatch.delenv("OSC_PROTOCOL", raising=False) + + profile = Profile.from_standard_configuration(None, None) + + assert profile.region == "eu-west-2" + assert profile.protocol == "https" + + +def test_environment_values_override_defaults(monkeypatch): + """Test profile values can be loaded from environment variables""" + monkeypatch.setenv("OSC_ACCESS_KEY", "env-ak") + monkeypatch.setenv("OSC_SECRET_KEY", "env-sk") + monkeypatch.setenv("OSC_REGION", "cloudgouv-eu-west-1") + monkeypatch.setenv("OSC_PROTOCOL", "http") + monkeypatch.setenv("OSC_ENDPOINT_API", "https://osc.example.test") + monkeypatch.setenv("OSC_ENDPOINT_OKS", "https://oks.example.test") + monkeypatch.delenv("OSC_CONFIG_FILE", raising=False) + monkeypatch.delenv("OSC_PROFILE", raising=False) + + profile = Profile.from_standard_configuration(None, None) + + assert profile.access_key == "env-ak" + assert profile.secret_key == "env-sk" + assert profile.region == "cloudgouv-eu-west-1" + assert profile.protocol == "http" + assert profile.get_endpoint("api") == "https://osc.example.test" + assert profile.get_endpoint("oks") == "https://oks.example.test" + + +def test_profile_file_loading(tmp_path, monkeypatch): + """Test profile values can be loaded from a config file""" + monkeypatch.delenv("OSC_ACCESS_KEY", raising=False) + monkeypatch.delenv("OSC_SECRET_KEY", raising=False) + monkeypatch.delenv("OSC_REGION", raising=False) + monkeypatch.delenv("OSC_PROTOCOL", raising=False) + monkeypatch.delenv("OSC_ENDPOINT_API", raising=False) + + config = tmp_path / "config.json" + config.write_text( + json.dumps( + { + "default": { + "access_key": "file-ak", + "secret_key": "file-sk", + "region": "eu-west-2", + "protocol": "https", + } + } + ) + ) + + profile = Profile.from_standard_configuration(str(config), "default") + + assert profile.access_key == "file-ak" + assert profile.secret_key == "file-sk" + assert profile.region == "eu-west-2" + + +def test_environment_values_override_profile_file(tmp_path, monkeypatch): + """Test environment variables take priority over config file values""" + config = tmp_path / "config.json" + config.write_text( + json.dumps( + { + "default": { + "access_key": "file-ak", + "secret_key": "file-sk", + "region": "file-region", + "protocol": "https", + "endpoints": { + "api": "https://file-osc.example.test", + }, + } + } + ) + ) + + monkeypatch.setenv("OSC_ACCESS_KEY", "env-ak") + monkeypatch.setenv("OSC_SECRET_KEY", "env-sk") + monkeypatch.setenv("OSC_REGION", "env-region") + monkeypatch.setenv("OSC_PROTOCOL", "http") + monkeypatch.setenv("OSC_ENDPOINT_API", "https://env-osc.example.test") + + profile = Profile.from_standard_configuration(str(config), "default") + + assert profile.access_key == "env-ak" + assert profile.secret_key == "env-sk" + assert profile.region == "env-region" + assert profile.protocol == "http" + assert profile.get_endpoint("api") == "https://env-osc.example.test" + + +def test_constructor_values_override_environment_and_profile_file( + tmp_path, monkeypatch +): + """Test explicit constructor values have the highest priority""" + config = tmp_path / "config.json" + config.write_text( + json.dumps( + { + "default": { + "access_key": "file-ak", + "secret_key": "file-sk", + "region": "file-region", + "protocol": "https", + } + } + ) + ) + + monkeypatch.setenv("OSC_ACCESS_KEY", "env-ak") + monkeypatch.setenv("OSC_SECRET_KEY", "env-sk") + monkeypatch.setenv("OSC_REGION", "env-region") + + client = Client( + path=str(config), + profile="default", + access_key="arg-ak", + secret_key="arg-sk", + region="arg-region", + ) + try: + assert client.osc.profile.access_key == "arg-ak" + assert client.osc.profile.secret_key == "arg-sk" + assert client.osc.profile.region == "arg-region" + assert client.oks.profile.access_key == "arg-ak" + assert client.oks.profile.secret_key == "arg-sk" + assert client.oks.profile.region == "arg-region" + finally: + client.close() + + +def test_missing_default_config_is_ignored(monkeypatch): + """Test missing default config falls back to defaults""" + monkeypatch.delenv("OSC_CONFIG_FILE", raising=False) + monkeypatch.delenv("OSC_PROFILE", raising=False) + + profile = Profile.from_standard_configuration(None, None) + + assert profile.region == "eu-west-2" + assert profile.protocol == "https" + + +def test_missing_explicit_profile_raises(tmp_path): + """Test an explicitly requested missing profile raises an error""" + config = tmp_path / "config.json" + config.write_text(json.dumps({"default": {"access_key": "ak"}})) + + with pytest.raises(SdkConfigurationError): + Profile.from_standard_configuration(str(config), "missing") + + +def test_malformed_config_raises(tmp_path): + """Test malformed config files raise an error""" + config = tmp_path / "config.json" + config.write_text("{bad-json") + + with pytest.raises(SdkConfigurationError): + Profile.from_standard_configuration(str(config), "default") + + +def test_osc_and_oks_default_endpoints_are_separated(): + """Test OSC and OKS resolve to separate default endpoints""" + profile = Profile(region="eu-west-2", protocol="https") + + assert profile.get_endpoint("api") == "https://api.eu-west-2.outscale.com/api/v1" + assert ( + profile.get_endpoint("oks") == "https://api.eu-west-2.oks.outscale.com/api/v2" + ) diff --git a/tests/unit/runtime/__init__.py b/tests/unit/runtime/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/runtime/test_async_rate_limiter.py b/tests/unit/runtime/test_async_rate_limiter.py new file mode 100644 index 0000000..f4a3620 --- /dev/null +++ b/tests/unit/runtime/test_async_rate_limiter.py @@ -0,0 +1,138 @@ +import asyncio +import datetime + +from osc_sdk_python import RateLimiter + +i = 0 + + +def test_async_fast(monkeypatch): + async def run(): + with monkeypatch.context() as m: + was_called = [] + + async def mock_sleep(t): + was_called.append(t) + assert t > 0 + + class MockDateTimeFast(datetime.datetime): + @classmethod + def now(cls, tz=None): + global i + i += 1 + return cls(2022, 1, 1, microsecond=i, tzinfo=tz) + + m.setattr("asyncio.sleep", mock_sleep) + + rl = RateLimiter( + datetime.timedelta(seconds=1), 5, datetime_cls=MockDateTimeFast + ) + for _ in range(10): + await rl.async_acquire() + + assert len(rl.requests) > 5 + assert len(was_called) > 0 + + asyncio.run(run()) + + +def test_async_slow(monkeypatch): + async def run(): + with monkeypatch.context() as m: + was_called = [] + + async def mock_sleep(t): + was_called.append(t) + assert t > 0 + + class MockDateTimeSlow(datetime.datetime): + @classmethod + def now(cls, tz=None): + global i + i += 1 + return cls(2022 + i, 1, 1, tzinfo=tz) + + m.setattr("asyncio.sleep", mock_sleep) + + rl = RateLimiter( + datetime.timedelta(seconds=1), 5, datetime_cls=MockDateTimeSlow + ) + for _ in range(10): + await rl.async_acquire() + + assert len(rl.requests) <= 1 + assert len(was_called) == 0 + + asyncio.run(run()) + + +def test_async_refill_after_window(): + """Test old requests are removed once the async limiter window has passed""" + + async def run(): + class MockDateTime(datetime.datetime): + @classmethod + def now(cls, tz=None): + return cls(2022, 1, 1, 0, 0, 2, tzinfo=tz) + + rl = RateLimiter(datetime.timedelta(seconds=1), 5, datetime_cls=MockDateTime) + rl.requests = [ + MockDateTime(2022, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc), + MockDateTime(2022, 1, 1, 0, 0, 1, 500000, tzinfo=datetime.timezone.utc), + ] + + await rl.async_acquire() + + assert len(rl.requests) == 2 + assert rl.requests[0] == MockDateTime( + 2022, 1, 1, 0, 0, 1, 500000, tzinfo=datetime.timezone.utc + ) + assert rl.requests[1] == MockDateTime.now(datetime.timezone.utc) + + asyncio.run(run()) + + +def test_async_acquire_uses_shared_sync_lock(): + """Test async and sync limiter paths protect the same request history.""" + + async def run(): + rl = RateLimiter(datetime.timedelta(seconds=1), 5) + rl._lock.acquire() + try: + task = asyncio.create_task(rl.async_acquire()) + await asyncio.sleep(0.01) + + assert not task.done() + assert rl.requests == [] + finally: + rl._lock.release() + + await asyncio.wait_for(task, timeout=1) + assert len(rl.requests) == 1 + + asyncio.run(run()) + + +def test_async_concurrent_acquire_completes_without_deadlock(monkeypatch): + """Test concurrent async callers complete without deadlocking""" + + async def run(): + with monkeypatch.context() as m: + sleep_calls = [] + + async def mock_sleep(t): + sleep_calls.append(t) + + m.setattr("asyncio.sleep", mock_sleep) + + rl = RateLimiter(datetime.timedelta(seconds=1), 1) + + await asyncio.wait_for( + asyncio.gather(*(rl.async_acquire() for _ in range(3))), + timeout=1, + ) + + assert len(rl.requests) == 3 + assert len(sleep_calls) == 2 + + asyncio.run(run()) diff --git a/tests/unit/runtime/test_client_lifecycle.py b/tests/unit/runtime/test_client_lifecycle.py new file mode 100644 index 0000000..055cc7e --- /dev/null +++ b/tests/unit/runtime/test_client_lifecycle.py @@ -0,0 +1,149 @@ +import asyncio +from unittest.mock import Mock + +import pytest + +from osc_sdk_python import AsyncClient, Client, SdkConfigurationError, SdkUsageError +from osc_sdk_python.outscale_gateway import OpenAPIActionAPI +from osc_sdk_python.runtime.call import AsyncCall, Call + + +def test_client_close_closes_service_sessions(): + """Test Client.close closes OSC and OKS sync sessions""" + client = Client() + client.osc.call.session.close = Mock() + client.oks.call.session.close = Mock() + + client.close() + + client.osc.call.session.close.assert_called_once() + client.oks.call.session.close.assert_called_once() + + +def test_client_context_manager_closes_service_sessions(): + """Test Client context manager closes OSC and OKS sync sessions""" + with Client() as client: + client.osc.call.session.close = Mock() + client.oks.call.session.close = Mock() + osc_close = client.osc.call.session.close + oks_close = client.oks.call.session.close + + osc_close.assert_called_once() + oks_close.assert_called_once() + + +def test_async_client_close_closes_service_clients(): + """Test AsyncClient.close closes OSC and OKS async clients""" + + async def run(): + client = AsyncClient() + + await client.close() + + assert client.osc.call.client.is_closed + assert client.oks.call.client.is_closed + + asyncio.run(run()) + + +def test_async_client_context_manager_closes_service_clients(): + """Test AsyncClient context manager closes OSC and OKS async clients""" + + async def run(): + async with AsyncClient() as client: + osc_client = client.osc.call.client + oks_client = client.oks.call.client + + assert osc_client.is_closed + assert oks_client.is_closed + + asyncio.run(run()) + + +def test_async_client_rejects_sync_context_manager(): + """Test AsyncClient cannot be used with a sync context manager""" + with pytest.raises(SdkUsageError): + with AsyncClient(): + pass + + +class FakeSyncClient: + def __init__(self, tls_skip_verify): + self.tls_skip_verify = tls_skip_verify + self.closed = False + + def close(self): + self.closed = True + + +class FakeAsyncClient: + def __init__(self, tls_skip_verify): + self.tls_skip_verify = tls_skip_verify + + +class RecordingCall(Call): + def __init__(self, **kwargs): + self.created_clients = [] + super().__init__(**kwargs) + + def _make_client(self): + client = FakeSyncClient(self.profile.tls_skip_verify) + self.created_clients.append(client) + return client + + +class RecordingAsyncCall(AsyncCall): + def __init__(self, **kwargs): + self.created_clients = [] + super().__init__(**kwargs) + + def _make_client(self): + client = FakeAsyncClient(self.profile.tls_skip_verify) + self.created_clients.append(client) + return client + + +def test_update_profile_recreates_sync_client_for_tls_settings(): + call = RecordingCall(tls_skip_verify=False) + old_session = call.session + + call.update_profile(tls_skip_verify=True) + + assert old_session.closed is True + assert call.session.tls_skip_verify is True + assert call.session is not old_session + + +def test_update_profile_recreates_async_client_for_tls_settings(): + call = RecordingAsyncCall(tls_skip_verify=False) + old_client = call.client + + call.update_profile(tls_skip_verify=True) + + assert call.client.tls_skip_verify is True + assert call.client is not old_client + + +def test_openapi_action_api_raises_configuration_error_for_unreadable_spec(): + with pytest.raises(SdkConfigurationError, match="Problem reading OpenAPI spec"): + OpenAPIActionAPI("missing-spec.yaml") + + +def test_dynamic_service_hasattr_reflects_available_operations(): + with Client() as client: + assert hasattr(client.osc, "ReadVms") + assert not hasattr(client.osc, "TotallyWrongAction") + assert "ReadVms" in dir(client.osc) + + assert hasattr(client.oks, "ListProjects") + assert not hasattr(client.oks, "TotallyWrongOperation") + assert "ListProjects" in dir(client.oks) + + +def test_unknown_dynamic_service_attribute_raises_attribute_error(): + with Client() as client: + with pytest.raises(AttributeError): + _ = client.osc.TotallyWrongAction + + with pytest.raises(AttributeError): + _ = client.oks.TotallyWrongOperation diff --git a/tests/test_limiter.py b/tests/unit/runtime/test_rate_limiter.py similarity index 50% rename from tests/test_limiter.py rename to tests/unit/runtime/test_rate_limiter.py index 7eba1b0..8c62dfd 100644 --- a/tests/test_limiter.py +++ b/tests/unit/runtime/test_rate_limiter.py @@ -1,4 +1,5 @@ import datetime +from concurrent.futures import ThreadPoolExecutor from osc_sdk_python import RateLimiter @@ -63,3 +64,47 @@ def now(cls, tz=None): assert len(rl.requests) <= 1 assert len(was_called) == 0 + + +def test_refill_after_window(): + """Test old requests are removed once the limiter window has passed""" + class MockDateTime(datetime.datetime): + @classmethod + def now(cls, tz=None): + return cls(2022, 1, 1, 0, 0, 2, tzinfo=tz) + + rl = RateLimiter(datetime.timedelta(seconds=1), 5, datetime_cls=MockDateTime) + rl.requests = [ + MockDateTime(2022, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc), + MockDateTime(2022, 1, 1, 0, 0, 1, 500000, tzinfo=datetime.timezone.utc), + ] + + rl.acquire() + + assert len(rl.requests) == 2 + print(rl.requests) + assert rl.requests[0] == MockDateTime( + 2022, 1, 1, 0, 0, 1, 500000, tzinfo=datetime.timezone.utc + ) + assert rl.requests[1] == MockDateTime.now(datetime.timezone.utc) + + +def test_concurrent_acquire_completes_without_deadlock(monkeypatch): + """Test concurrent sync callers complete without deadlocking""" + with monkeypatch.context() as m: + sleep_calls = [] + + def mock_sleep(t): + sleep_calls.append(t) + + m.setattr("time.sleep", mock_sleep) + + rl = RateLimiter(datetime.timedelta(seconds=1), 1) + + with ThreadPoolExecutor(max_workers=3) as executor: + futures = [executor.submit(rl.acquire) for _ in range(3)] + for future in futures: + future.result(timeout=1) + + assert len(rl.requests) == 3 + assert len(sleep_calls) == 2 diff --git a/tests/unit/runtime/test_transport.py b/tests/unit/runtime/test_transport.py new file mode 100644 index 0000000..389bac0 --- /dev/null +++ b/tests/unit/runtime/test_transport.py @@ -0,0 +1,434 @@ +from unittest.mock import AsyncMock, Mock, patch + +import httpx +import pytest + +from osc_sdk_python.credentials import Profile +from osc_sdk_python.exceptions import ( + SdkClientError, + SdkConfigurationError, + SdkServerError, + SdkTransportError, +) +from osc_sdk_python.runtime.transport import ( + AsyncSdkTransport, + RetryPolicy, + SdkAuth, + SdkTransport, +) + + +class FixedDateSdkAuth(SdkAuth): + def build_dates(self): + return "20260102T030405Z", "20260102" + + +class SequenceTransport: + def __init__(self, responses): + self.responses = list(responses) + self.requests = [] + + def handle_request(self, request): + self.requests.append(request) + if isinstance(self.responses[0], Exception): + raise self.responses.pop(0) + return self.responses.pop(0) + + def close(self): + pass + + +class AsyncSequenceTransport: + def __init__(self, responses): + self.responses = list(responses) + self.requests = [] + + async def handle_async_request(self, request): + self.requests.append(request) + if isinstance(self.responses[0], Exception): + raise self.responses.pop(0) + return self.responses.pop(0) + + async def aclose(self): + pass + + +def response(status_code, request, headers=None): + return httpx.Response( + status_code, + json={}, + headers={"content-type": "application/json", **(headers or {})}, + request=request, + ) + + +def text_response(status_code, request=None, text="", headers=None): + return httpx.Response( + status_code, + text=text, + headers={"content-type": "text/plain", **(headers or {})}, + request=request, + ) + + +def test_sdk_auth_adds_signed_headers(): + auth = FixedDateSdkAuth( + Profile(access_key="ak", secret_key="sk", region="eu-west-2"), + service="api", + ) + request = httpx.Request( + "POST", + "https://api.eu-west-2.outscale.com/ReadVms", + content="{}", + ) + + signed = next(auth.auth_flow(request)) + + assert signed.headers["X-Osc-Date"] == "20260102T030405Z" + assert signed.headers["Authorization"].startswith("OSC4-HMAC-SHA256 ") + assert ( + "Credential=ak/20260102/eu-west-2/api/osc4_request" + in signed.headers["Authorization"] + ) + + +def test_sdk_auth_requires_signed_credentials(): + auth = FixedDateSdkAuth(Profile(region="eu-west-2"), service="api") + request = httpx.Request( + "POST", + "https://api.eu-west-2.outscale.com/ReadVms", + content="{}", + ) + + with pytest.raises(SdkConfigurationError): + next(auth.auth_flow(request)) + + +def test_sdk_auth_adds_basic_auth_headers(): + auth = FixedDateSdkAuth( + Profile(login="user@example.com", password="secret", region="eu-west-2"), + service="api", + ) + request = httpx.Request( + "POST", + "https://api.eu-west-2.outscale.com/ReadVms", + content="{}", + ) + + signed = next(auth.auth_flow(request)) + + assert signed.headers["X-Osc-Date"] == "20260102T030405Z" + assert signed.headers["Authorization"].startswith("Basic ") + + +def test_sdk_auth_adds_oks_headers(): + auth = FixedDateSdkAuth( + Profile(access_key="ak", secret_key="sk", region="eu-west-2"), + service="oks", + ) + request = httpx.Request( + "GET", + "https://api.eu-west-2.oks.outscale.com/projects", + ) + + signed = next(auth.auth_flow(request)) + + assert signed.headers["AccessKey"] == "ak" + assert signed.headers["SecretKey"] == "sk" + + +def test_sdk_auth_requires_oks_credentials(): + auth = FixedDateSdkAuth(Profile(region="eu-west-2"), service="oks") + request = httpx.Request( + "GET", + "https://api.eu-west-2.oks.outscale.com/projects", + ) + + with pytest.raises(SdkConfigurationError): + next(auth.auth_flow(request)) + + +def test_transport_retries_429_with_retry_after(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=1)) + transport._transport = SequenceTransport( + [ + response(429, request, {"Retry-After": "0"}), + response(200, request), + ] + ) + + with patch("time.sleep") as sleep: + result = transport.handle_request(request) + + assert result.status_code == 200 + assert len(transport._transport.requests) == 2 + sleep.assert_called_once_with(0.0) + + +def test_transport_retries_non_json_500(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=1)) + transport._transport = SequenceTransport( + [ + text_response(500, request, "upstream failure"), + response(200, request), + ] + ) + + with patch("time.sleep"): + result = transport.handle_request(request) + + assert result.status_code == 200 + assert len(transport._transport.requests) == 2 + + +def test_transport_retries_connection_error_until_max_retries(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=2)) + transport._transport = SequenceTransport( + [ + httpx.ConnectError("connection failed", request=request), + httpx.ConnectError("connection failed", request=request), + httpx.ConnectError("connection failed", request=request), + ] + ) + + with patch("time.sleep") as sleep: + with pytest.raises(SdkTransportError) as exc_info: + transport.handle_request(request) + + assert len(transport._transport.requests) == 3 + assert sleep.call_count == 2 + assert isinstance(exc_info.value.__cause__, httpx.ConnectError) + + +def test_transport_retries_timeout_until_max_retries(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=2)) + transport._transport = SequenceTransport( + [ + httpx.TimeoutException("timed out", request=request), + httpx.TimeoutException("timed out", request=request), + httpx.TimeoutException("timed out", request=request), + ] + ) + + with patch("time.sleep") as sleep: + with pytest.raises(SdkTransportError) as exc_info: + transport.handle_request(request) + + assert len(transport._transport.requests) == 3 + assert sleep.call_count == 2 + assert isinstance(exc_info.value.__cause__, httpx.TimeoutException) + + +def test_transport_wraps_httpx_error_without_request(): + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=0)) + request = httpx.Request("POST", "https://example.test/ReadVms") + transport._transport = SequenceTransport([httpx.ReadTimeout("timed out")]) + + with pytest.raises(SdkTransportError) as exc_info: + transport.handle_request(request) + + assert exc_info.value.request is None + assert exc_info.value.response is None + assert isinstance(exc_info.value.__cause__, httpx.ReadTimeout) + + +def test_transport_uses_backoff_when_retry_after_missing(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport( + retry_policy=RetryPolicy( + max_retries=1, + backoff_factor=2.0, + backoff_jitter=3.0, + ) + ) + transport._transport = SequenceTransport( + [ + text_response(500, request, "upstream failure"), + response(200, request), + ] + ) + + with patch("random.uniform", return_value=1.5) as random_uniform: + with patch("time.sleep") as sleep: + result = transport.handle_request(request) + + assert result.status_code == 200 + random_uniform.assert_called_once_with(0, 3.0) + sleep.assert_called_once_with(3.5) + + +def test_retry_after_http_date_overrides_backoff(): + request = httpx.Request("POST", "https://example.test/ReadVms") + retry_after = "Fri, 02 Jan 2026 03:04:06 GMT" + policy = RetryPolicy(max_retries=1) + + error = httpx.HTTPStatusError( + "too many requests", + request=request, + response=response(429, request, {"Retry-After": retry_after}), + ) + + class FixedDateTime: + @classmethod + def now(cls, tz=None): + import datetime + + return datetime.datetime(2026, 1, 2, 3, 4, 5, tzinfo=tz) + + with patch("osc_sdk_python.runtime.transport.datetime", FixedDateTime): + assert policy.retry_after_time(error) == 1.0 + + +def test_retry_policy_does_not_retry_redirect_or_invalid_request_errors(): + policy = RetryPolicy(max_retries=3) + request = httpx.Request("POST", "https://example.test/ReadVms") + + assert not policy.should_retry( + httpx.TooManyRedirects("too many redirects", request=request), + attempt=0, + ) + assert not policy.should_retry(httpx.InvalidURL("bad url"), attempt=0) + assert not policy.should_retry( + httpx.UnsupportedProtocol("bad protocol", request=request), + attempt=0, + ) + + +def test_transport_error_uses_original_request_when_response_has_none(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=0)) + transport._transport = SequenceTransport( + [text_response(500, text="upstream failure")] + ) + + with pytest.raises(SdkServerError) as exc_info: + transport.handle_request(request) + + assert exc_info.value.request is request + assert "https://example.test/ReadVms" in str(exc_info.value) + + +def test_transport_does_not_retry_400(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = SdkTransport(retry_policy=RetryPolicy(max_retries=3)) + transport._transport = SequenceTransport([response(400, request)]) + + with pytest.raises(SdkClientError): + transport.handle_request(request) + + assert len(transport._transport.requests) == 1 + + +def test_async_transport_uses_async_limiter(): + async def run(): + request = httpx.Request("POST", "https://example.test/ReadVms") + limiter = Mock() + limiter.async_acquire = AsyncMock() + transport = AsyncSdkTransport( + limiter=limiter, + retry_policy=RetryPolicy(max_retries=0), + ) + transport._transport = AsyncSequenceTransport([response(200, request)]) + + result = await transport.handle_async_request(request) + + assert result.status_code == 200 + limiter.async_acquire.assert_called_once() + + import asyncio + + asyncio.run(run()) + + +def test_async_transport_retries_non_json_500(): + async def run(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = AsyncSdkTransport(retry_policy=RetryPolicy(max_retries=1)) + transport._transport = AsyncSequenceTransport( + [ + text_response(500, request, "upstream failure"), + response(200, request), + ] + ) + + with patch("asyncio.sleep", new_callable=AsyncMock): + result = await transport.handle_async_request(request) + + assert result.status_code == 200 + assert len(transport._transport.requests) == 2 + + import asyncio + + asyncio.run(run()) + + +def test_async_transport_retries_connection_error_until_max_retries(): + async def run(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = AsyncSdkTransport(retry_policy=RetryPolicy(max_retries=2)) + transport._transport = AsyncSequenceTransport( + [ + httpx.ConnectError("connection failed", request=request), + httpx.ConnectError("connection failed", request=request), + httpx.ConnectError("connection failed", request=request), + ] + ) + + with patch("asyncio.sleep", new_callable=AsyncMock) as sleep: + with pytest.raises(SdkTransportError) as exc_info: + await transport.handle_async_request(request) + + assert len(transport._transport.requests) == 3 + assert sleep.call_count == 2 + assert isinstance(exc_info.value.__cause__, httpx.ConnectError) + + import asyncio + + asyncio.run(run()) + + +def test_async_transport_wraps_httpx_error_without_request(): + async def run(): + transport = AsyncSdkTransport(retry_policy=RetryPolicy(max_retries=0)) + request = httpx.Request("POST", "https://example.test/ReadVms") + transport._transport = AsyncSequenceTransport([httpx.ReadTimeout("timed out")]) + + with pytest.raises(SdkTransportError) as exc_info: + await transport.handle_async_request(request) + + assert exc_info.value.request is None + assert exc_info.value.response is None + assert isinstance(exc_info.value.__cause__, httpx.ReadTimeout) + + import asyncio + + asyncio.run(run()) + + +def test_async_transport_retries_timeout_until_max_retries(): + async def run(): + request = httpx.Request("POST", "https://example.test/ReadVms") + transport = AsyncSdkTransport(retry_policy=RetryPolicy(max_retries=2)) + transport._transport = AsyncSequenceTransport( + [ + httpx.TimeoutException("timed out", request=request), + httpx.TimeoutException("timed out", request=request), + httpx.TimeoutException("timed out", request=request), + ] + ) + + with patch("asyncio.sleep", new_callable=AsyncMock) as sleep: + with pytest.raises(SdkTransportError) as exc_info: + await transport.handle_async_request(request) + + assert len(transport._transport.requests) == 3 + assert sleep.call_count == 2 + assert isinstance(exc_info.value.__cause__, httpx.TimeoutException) + + import asyncio + + asyncio.run(run()) diff --git a/tests/unit/spec/__init__.py b/tests/unit/spec/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_problems.py b/tests/unit/spec/test_problems.py similarity index 97% rename from tests/test_problems.py rename to tests/unit/spec/test_problems.py index adcf094..1a3c1ca 100644 --- a/tests/test_problems.py +++ b/tests/unit/spec/test_problems.py @@ -1,6 +1,4 @@ -import sys -sys.path.append("..") from osc_sdk_python import Problem, ProblemDecoder import json diff --git a/tests/unit/spec/test_request_spec.py b/tests/unit/spec/test_request_spec.py new file mode 100644 index 0000000..e584662 --- /dev/null +++ b/tests/unit/spec/test_request_spec.py @@ -0,0 +1,27 @@ +import pytest + +from osc_sdk_python.exceptions import SdkValidationError + +from osc_sdk_python.runtime.request import RequestSpec + + +def test_resolved_path_replaces_and_quotes_path_parameters(): + spec = RequestSpec(service="oks", method="GET", path="/projects/{project_id}") + + assert ( + spec.resolved_path({"project_id": "project/one"}) == "/projects/project%2Fone" + ) + + +def test_resolved_path_raises_when_path_parameter_is_missing(): + spec = RequestSpec( + service="oks", + method="GET", + path="/projects/{project_id}/clusters/{cluster_id}", + ) + + with pytest.raises( + SdkValidationError, + match="Missing path parameter\\(s\\): cluster_id, project_id", + ): + spec.resolved_path() diff --git a/tox.ini b/tox.ini index 6a8e6fa..59fee4f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,16 @@ [tox] skipsdist = true -envlist = py39, py310, py311, py312, py313, py314 +envlist = py310, py311, py312, py313, py314 [gh-actions] python = - 3.9: py39 3.10: py310 3.11: py311 3.12: py312 3.14: py314 [testenv] -passenv = PYTHON_VERSION, OSC_ACCESS_KEY, OSC_SECRET_KEY, OSC_REGION, OSC_TEST_LOGIN, OCS_TEST_PASSWORD, OSC_ENDPOINT_API, OSC_IS_RICOCHET +passenv = PYTHON_VERSION, OSC_ACCESS_KEY, OSC_SECRET_KEY, OSC_REGION, OSC_TEST_LOGIN, OSC_TEST_PASSWORD, OSC_ENDPOINT_API, OSC_IS_RICOCHET allowlist_externals = uv commands = uv sync --python {envpython} diff --git a/uv.lock b/uv.lock index f265f10..eb5830b 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,29 @@ version = 1 revision = 3 requires-python = ">=3.10" +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + [[package]] name = "cachetools" version = "7.0.0" @@ -29,67 +52,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, ] -[[package]] -name = "charset-normalizer" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -113,7 +75,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ @@ -129,6 +91,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + [[package]] name = "idna" version = "3.10" @@ -149,12 +148,12 @@ wheels = [ [[package]] name = "osc-sdk-python" -version = "0.41.0" +version = "0.42.0" source = { editable = "." } dependencies = [ - { name = "requests" }, + { name = "httpx" }, + { name = "pydantic" }, { name = "ruamel-yaml" }, - { name = "urllib3" }, ] [package.dev-dependencies] @@ -166,9 +165,9 @@ dev = [ [package.metadata] requires-dist = [ - { name = "requests", specifier = ">=2.20.0" }, + { name = "httpx", specifier = ">=0.28.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, { name = "ruamel-yaml", specifier = "==0.19.1" }, - { name = "urllib3", specifier = ">=2.6.3" }, ] [package.metadata.requires-dev] @@ -205,6 +204,137 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -245,21 +375,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] -[[package]] -name = "requests" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, -] - [[package]] name = "ruamel-yaml" version = "0.19.1" @@ -396,12 +511,15 @@ wheels = [ ] [[package]] -name = "urllib3" -version = "2.7.0" +name = "typing-inspection" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]]