Initial commit: VMware vSphere API simulator scaffold.

Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API
contracts, docs, client examples, and the unit/integration/compatibility
test suite for local client and tooling labs without a real vCenter.
This commit is contained in:
2026-07-18 04:42:11 +03:00
commit f8d3cbdd59
422 changed files with 361335 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
**Language / Язык:** [English](README.md) | [Русский](ru/README.md)
# Documentation
Guides for the VMware vSphere API simulator. Switch language with the header on each
page. Russian mirrors live under [`ru/`](ru/README.md).
| Guide | Description |
|---|---|
| [Getting started](getting-started.md) | First successful lab session |
| [Configuration](configuration.md) | Environment variables and Compose |
| [Authentication](authentication.md) | Sessions, `vmware-api-session-id`, privileges |
| [API versions](api-versions.md) | Catalog majors 69 and hot-swap |
| [API surface](api-surface.md) | REST/SOAP routing, coverage registry, stubs |
| [API coverage](api-coverage.md) | Broadcom universe vs implemented surface |
| [Clients & examples](clients.md) | Python, Go, Java, Perl, Ansible, Terraform, Pulumi |
| [Seed profiles](seed-profiles.md) | Deterministic inventory fixtures |
| [Domains](domains/README.md) | Session, inventory, VM, storage, networking, tagging, SOAP, tasks, … |
| [Web UI](web-ui.md) | Interactive console and catalogs |
| [Operations](operations.md) | Reseed, migrate, release, upgrade |
| [Kubernetes / Helm](kubernetes.md) | Hub image + Ingress + Let's Encrypt |
| [Security](security.md) | Lab threat model and credentials |
| [Observability](observability.md) | Health endpoints and logging |
| [Ports](ports.md) | Published host ports and internal services |
| [Troubleshooting](troubleshooting.md) | Common failure modes |
| [FAQ](faq.md) | Short answers |
| [Architecture](architecture.md) | Component boundaries |
| [Compatibility](compatibility.md) | Evidence model and release matrix |
Runnable cookbooks: [`examples/`](../examples/README.md).
Integration suites: [`pulumi-tests/`](../pulumi-tests/README.md) (`make pulumi-tests`).
+135
View File
@@ -0,0 +1,135 @@
**Language / Язык:** [English](api-coverage.md) | [Русский](ru/api-coverage.md)
# vSphere API coverage matrix
Auto-oriented registry: [`app/vsphere/rest/coverage.py`](../app/vsphere/rest/coverage.py).
Broadcom universe stubs: [`app/vsphere/rest/universe.json`](../app/vsphere/rest/universe.json) (from the public operations index).
Per-major floors + stub OpenAPI bundles: [`app/vsphere/contracts/matrix.py`](../app/vsphere/contracts/matrix.py) → `contracts/vsphere/<version>/manifest.json`.
## Broadcom vs this simulator
Public source (scraped): [vSphere Automation API Operations Index (9.1 Latest)](https://developer.broadcom.com/xapis/vsphere-automation-api/latest/operation-index/)
| Surface | Count | Notes |
|---|---:|---|
| Broadcom Operations Index | **1348** | GET 628 / POST 422 / DELETE 114 / PUT 93 / PATCH 91 |
| Generated unique `verb + path` routes | **~1037** | Same HTTP path can back several named ops (`?action=…`, `$Task`) |
| Simulator registry (core + stubs + `/rest`) | **1077** | Core deep handlers overwrite stub entries on the same path |
| Core deep handlers | **104** | Seeded inventory / lifecycle / authz behaviour |
| DB-backed surface rows (`vsphere_api_state`) | **~540+** | Seeded for every GET `/api` route + lab extras |
Regenerate universe after refreshing the index dump:
```bash
python scripts/generate_vsphere_universe.py
make vsphere-bundles
```
Refresh live stats / regenerate artifacts:
```bash
curl -sk https://localhost/ui/api/compatibility?major=9
make vsphere-surface
python scripts/write_vsphere_bundles.py
python scripts/write_vsphere_evidence.py
```
| Major | Label | Implemented / universe | Coverage | Notes |
|---|---|---:|---:|---|
| 6 | vSphere 7.0 | 31 / 1077 | 2.9% | Catalog/evidence floor only |
| 7 | vSphere 7.0 U3 | 77 / 1077 | 7.2% | Catalog/evidence floor only |
| 8 | vSphere 8.0 | 103 / 1077 | 9.6% | Catalog/evidence floor only |
| 9 | vSphere 8.0 U2 / Automation 9.1 surface | **1077 / 1077** | **100%** | Deep handlers + DB-backed Broadcom surface |
Numbers come from `GET /ui/api/compatibility?major=N` and `evidence/vsphere-*.json` (`make vsphere-bundles`).
Hot-swap (`POST /ui/api/contract/apply?major=N`) changes the **catalog** major used by the Web UI / evidence reports. **Runtime always serves the full registered surface** — known paths are never HTTP 501d by version floor.
## Planes
| Plane | Default | Notes |
|---|---|---|
| Native REST `/api`, `/rest` | on | Primary lab surface |
| Native SOAP `/sdk` | on | PropertyCollector subset + VM tasks |
| Proxmox `/api2/*` stub | **off** (`ENABLE_PVE_STUB=false`) | Optional legacy |
## Auth & synthetic data
| Item | Detail |
|---|---|
| Users | `administrator`, `readonly`, `operator`, `vmadmin` `@vsphere.local` / `VMware1!` |
| AuthZ | Role → privilege gate on mutate endpoints (403 `unauthorized`) |
| Seed `large` | 10 hosts, **1000 VMs**, 4 datastores, DVS, folders, permissions |
| Seed `demo-cluster` | 20 hosts, 1000 VMs (UI demo load) |
| Seed `small` | 3 hosts, 5 named VMs (tests) |
## REST domains
### Deep (core) at major 9
- Session / CIS tasks / AuthZ roles+permissions / identity providers / TLS cert stub
- VM list/get/create/delete/power, hardware, snapshots, clone, relocate, tools, guest identity/networking/power/customization, console tickets, template/unregister
- Host list/get + maintenance + storage-device + networking
- Datastore list/get + file metadata
- Network list + DVS/DVPG create
- Datacenter / cluster / folder (+children) / resource-pool CRUD
- Tagging, content library + OVF, storage policies (+ VM associations), privileges
- Appliance version/health/networking/timesync
- `vapi` metamodel service list stub
### DB-backed Automation surface (Broadcom universe catch-all)
Remaining Automation API routes from the 9.1 operations index are registered and answered by [`app/vsphere/rest/stub_surface.py`](../app/vsphere/rest/stub_surface.py) against PostgreSQL:
- table `vsphere_api_state` (migration `011_vsphere_api_state.sql`)
- seeded by `seed_api_surface()` on every profile including **`demo-cluster`** / UI `POST /ui/api/demo/load`
- inventory overlay for VM hardware (cdrom/scsi/boot/…), host networking/storage, tagging, content libraries
- PUT/PATCH persist into `vsphere_api_state`; POST appends collection rows; DELETE removes them
No `"stub": true` markers — probes require real seeded payloads on major 9.
## SOAP domains (govmomi / Terraform / Pulumi / pyvmomi)
- RetrieveServiceContent (+ TaskManager / SearchIndex / GuestOperationsManager / FileManager / OvfManager)
- RetrieveProperties / RetrievePropertiesEx / **ContinueRetrievePropertiesEx** (pagination tokens; `<objects>` plural)
- PropertyCollector: parent-chain Ancestors, one-hop `childEntity` ListFolder, ContainerView `view` traversal
- Folder.childType as `ArrayOfString`; string props carry `xsi:type="xsd:string"` (govmomi decode)
- Datastore.host as `ArrayOfDatastoreHostMount`; Cluster/Host **environmentBrowser**
- **QueryConfigOption** / QueryConfigOptionEx / QueryConfigOptionDescriptor / QueryConfigTarget
- CreateFilter / WaitForUpdatesEx (version tokens; empty polls)
- FindByInventoryPath (govmomi paths omit root `Datacenters`), FindByUuid/Dns/Ip, FindChild
- **CreateVM_Task** / CreateChildVM_Task, CreateFolder, Power/Clone/Snapshot/Rename/Reconfig/Relocate/Destroy/Unregister/MarkAsTemplate/CustomizeVM_Task + CancelTask
- Guest file ops: ListFilesInGuest, InitiateFileTransferTo/FromGuest, DeleteFileInGuest, MakeDirectoryInGuest
- Real task IDs from `vsphere_tasks` (including `info.result` MoRef on create/clone)
- `/sdk/vimService.wsdl`, `/sdk/about.do`, `/pbm` stub
- Type-strict MOR lookup: `VirtualApp:resgroup-*` does not resolve a plain ResourcePool (Terraform CreateVM path)
## REST extras for Ansible / Python apps
- VM power returns `{ "task": "task-…" }` for CIS task polling
- Guest virtual filesystem: `/api/vcenter/vm/{vm}/guest/filesystem` (+ local-filesystem listing)
- Content library update/download sessions for OVF push/pull lab flows
## Legacy `/rest`
`{ "value": … }` wrappers for vm/host/datastore/network/datacenter/cluster/power/appliance.
## Contract majors (browse vs runtime)
Hot-swap (`POST /ui/api/contract/apply?major=N`) still switches the **catalog** major for UI browse/evidence. **Runtime always serves the full registered surface** with deep handlers or DB-backed stubs — known paths are never HTTP 501d by version floor. Catalog floors remain historical for documentation only.
## Platform surfaces (lab-available)
These were historically “deferred”; they now return **non-empty seeded lab data** and accept basic mutate:
| Area | REST | SOAP |
|---|---|---|
| NSX (tier0 / projects / edges / VPC / subnets) | Seeded Automation paths under `namespace-management` / `namespaces` | — |
| Supervisor / WCP | namespaces, VM classes, supervisor summary/identity, infra policies | — |
| vSAN | Storage policies with `policy_type: VSAN` (+ RAID1 lab policy) | — |
| SAML / OIDC | `GET/POST/PATCH/DELETE /api/vcenter/identity/providers` (LocalOS + OIDC + SAML) | — |
| VECS / certs | TLS, TLS CSR, trusted-root-chains, supervisor certs/signing-requests | — |
| HttpNfcLease | `PUT/GET /nfc/{lease}/files/...` | `ImportVApp_Task`, `CreateImportSpec`, lease progress/complete |
| Guest customization | GET+POST `/api/vcenter/vm/{vm}/guest/customization` | `CustomizeVM_Task` |
This is still a **lab-grade** stand-in (not a binary-compatible NSX Manager / real VECS store / full Broadcom device XML matrix). Perf/Event/Alarm remain answered but not deeply simulated.
+87
View File
@@ -0,0 +1,87 @@
**Language / Язык:** [English](api-surface.md) | [Русский](ru/api-surface.md)
# API surface
## Request path
1. Middleware assigns or forwards a request ID (`REQUEST_ID_HEADER`).
2. FastAPI routes the request to the vSphere REST router (`/api`, `/rest`),
the SOAP router (`/sdk`), or (if `ENABLE_PVE_STUB=true`) the optional
legacy stub.
3. `/api/session` (or `/rest/com/vmware/cis/session`, or SOAP `Login`)
resolves a principal and issues a `vmware-api-session-id`.
4. `require_read` / `require_privilege(...)` dependencies check the session's
roles before revealing or mutating resources.
5. A deep handler (core inventory/lifecycle/tagging/content/appliance logic)
or the DB-backed stub surface executes against PostgreSQL-backed state.
6. Long operations (power, clone, relocate, snapshot, OVF deploy) create a
durable CIS task and return `{ "task": "task-…" }`.
## Two REST surfaces on one registry
- **Core (deep) handlers** — ~104 verb+path combinations across
[`app/vsphere/rest/router.py`](../app/vsphere/rest/router.py),
`vm_ext.py`, `inventory_ext.py`, `platform_rest.py`, `tagging_rest.py`,
`content_rest.py`, `appliance_ext.py`, `nfc_rest.py`, `tasks.py`. These read
and mutate the seeded inventory/tagging/content/appliance tables directly.
- **DB-backed stub surface** —
[`app/vsphere/rest/stub_surface.py`](../app/vsphere/rest/stub_surface.py)
answers the remaining Broadcom Automation API operations index routes
(registered from `universe.json`) against `vsphere_api_state`. GET returns
live inventory-derived payloads when possible, otherwise seeded rows;
PUT/PATCH persist into `vsphere_api_state`; POST appends collection rows;
DELETE removes them. No `"stub": true` marker is returned — probes see real
seeded payloads.
Both surfaces share one route table; core handlers take priority over stub
entries registered for the same verb+path.
## Legacy `/rest`
[`app/vsphere/rest/legacy.py`](../app/vsphere/rest/legacy.py) wraps
vm/host/datastore/network/datacenter/cluster/power/appliance reads (and VM
power) in `{ "value": … }` envelopes for older `com.vmware.vcenter.*` clients.
## Errors ([`app/vsphere/errors.py`](../app/vsphere/errors.py))
| Status | `error_type` | Typical cause |
|---|---|---|
| 400 | `invalid_argument` / `already_exists` | Malformed body, duplicate name |
| 401 | `unauthenticated` | Missing/invalid/expired session |
| 403 | `unauthorized` | Session lacks the required privilege |
| 404 | `not_found` | Unknown MOID/path parameter |
| 409 | (handler-specific) | Illegal power-state transition, lock conflict |
| 501 | `error` | Only reachable via the optional legacy stub's undeclared-method fallback |
All error bodies follow the vSphere Automation shape:
`{ "error_type": "...", "messages": [{ "default_message": "...", "id": "...", "args": [] }] }`.
## Tasks
Async work (power, clone, snapshot, relocate, OVF deploy, guest customize)
returns a task id. Poll:
```text
GET /api/cis/tasks/{task}
```
Task rows commit in `vsphere_tasks`; `progress` is `100` once `status` is
`SUCCEEDED`/`FAILED`. HTTP 200/201 on the mutation request means "accepted",
not "VM already in final state". See [Tasks](domains/tasks.md).
## Exploration
- Interactive FastAPI docs: `/docs`
- Web UI method inspector: `/` → catalog → method
- UI helper APIs: `/ui/api/catalog`, `/ui/api/method`, `/ui/api/compatibility`
- Coverage registry: [`app/vsphere/rest/coverage.py`](../app/vsphere/rest/coverage.py)
- Path-floor / catalog matrix: [`app/vsphere/contracts/matrix.py`](../app/vsphere/contracts/matrix.py)
## Compatibility endpoints
| Path | Format |
|---|---|
| `/ui/api/compatibility?major=N` | JSON |
See [Compatibility](compatibility.md) and [API coverage](api-coverage.md) for
the full Broadcom-universe-vs-implemented breakdown.
+77
View File
@@ -0,0 +1,77 @@
**Language / Язык:** [English](api-versions.md) | [Русский](ru/api-versions.md)
# API versions (vSphere catalog majors 69)
The Web UI and evidence/compatibility reports browse four integer **catalog
majors** that map onto vSphere Automation API label floors:
| Major | vSphere label | Contract version string |
|---|---|---|
| 6 | 7.0 | `7.0.0` |
| 7 | 7.0 U3 | `7.0.3` |
| 8 | 8.0 | `8.0.0` |
| 9 | 8.0 U2 (Automation 9.1 surface) | `8.0.2` |
Definitions live in [`app/vsphere/contracts/matrix.py`](../app/vsphere/contracts/matrix.py)
(`VERSIONS`, `PATH_FLOOR`). Each registered REST path has a **floor** — the
lowest major at which it appears in the catalog — sourced from the same
module. Undated paths default to the highest major (9) until catalogued.
## Runtime vs catalog
This is the most important distinction in the project:
- **Catalog major** — controls what the Web UI endpoint tree, `/ui/api/catalog`,
and compatibility/evidence reports show for a given major.
- **Runtime surface** — the simulator always serves the **full registered
route table** with deep handlers or DB-backed stubs, independent of the
active catalog major. A known path is never returned as HTTP 501 because of
a version floor.
Hot-swapping the catalog major is therefore a **documentation/browse**
switch, not a compatibility gate on live traffic. See
[`available_for_request()`](../app/vsphere/contracts/matrix.py) for the exact
policy.
## Cold start
`GET /api/appliance/system/version` reports the version string of the
currently selected runtime source (defaults to `8.0.2` / major 9 unless the
process overrides `app.state.runtime_source_version`).
## Hot-swap (catalog browse)
Browse any major in the Web UI catalog, or call:
```http
POST /ui/api/contract/apply?major=7
```
Effects:
- The Web UI catalog, `/ui/api/compatibility`, and evidence reports switch to
major 7's floor and ledger (`evidence/vsphere-7.0.3.json`).
- The change is **process-local** and **not persisted**; a restart returns to
the default (major 9).
- REST/SOAP routes already registered continue to answer with their real
handlers regardless of the applied major.
### Client guidance
- Most clients (pyvmomi, govmomi, Terraform, Pulumi, Ansible `uri`) do not
need to pin a catalog major — the runtime surface does not change shape
based on it.
- Use catalog majors when you specifically want the Web UI / evidence view to
reflect an older vSphere label for documentation or screenshots.
- After apply, re-check `/ui/api/compatibility?major=N` for the active
catalog state.
## Regenerating catalog artifacts
```bash
make vsphere-bundles # stub OpenAPI matrices + evidence ledgers
make vsphere-universe # regenerate universe.json from the Broadcom operations index
make evidence # regenerate per-major verified surface evidence ledgers
```
See [API surface](api-surface.md) and [Compatibility](compatibility.md).
+76
View File
@@ -0,0 +1,76 @@
**Language / Язык:** [English](architecture.md) | [Русский](ru/architecture.md)
# Architecture
## Goals
`vmware-api-simulator` is a stateful vSphere lab emulator (Automation REST + VIM SOAP).
The primary design goal is **practical client compatibility**: sessions, inventory,
VM lifecycle, PropertyCollector walks, tasks, tagging/content library stubs, and
AuthZ roles are implemented against a large synthetic datastore so tools like curl,
govc-style flows, pyvmomi, and Terraform can exercise common paths without a real
vCenter.
Catalog majors **69** map to vSphere 7.0 / 7.0U3 / 8.0 / 8.0U2 floors. Hot-swap
changes the catalog used for Web UI browse/evidence only — it does **not** gate
live routes. Optional Proxmox `/api2/*` stub remains behind `ENABLE_PVE_STUB`
(off by default).
## System context
```mermaid
flowchart LR
Client["API clients<br/>pyvmomi / Terraform / govc / REST SDKs"]
Admin["Lab operator"]
UI["Web lab UI"]
API["FastAPI application"]
Gateway["HTTPS gateway :443"]
Contract["vSphere contract matrix"]
Domain["vsphere domain + inventory"]
DB[(PostgreSQL)]
Obs["Logs / Prometheus / OpenTelemetry"]
Client -->|"/api /rest /sdk"| Gateway
Gateway --> API
UI --> Gateway
Admin -->|"seed / migrate"| API
API --> Contract
API --> Domain
Domain --> DB
API --> Obs
```
## Planes
| Plane | Path | Notes |
|---|---|---|
| Automation REST | `/api`, `/rest` | Session header `vmware-api-session-id` |
| VIM SOAP | `/sdk` | PropertyCollector subset + VM tasks |
| Lab UI helpers | `/ui/api/*` | Catalog, demo seed, compatibility |
| Optional PVE stub | `/api2/*` | Off unless `ENABLE_PVE_STUB=true` |
## Data model
Inventory lives in `vsphere_objects` (MOIDs, types, props JSON, parent links).
Sessions, credentials, tasks, tags, libraries, snapshots, and permissions are
sibling tables (migrations `009_vsphere.sql`, `010_vsphere_platform.sql`).
DB-backed Automation stubs use `vsphere_api_state` (`011`); content-library
transfer sessions and HttpNfcLease rows live in `vsphere_transfer_sessions` /
`vsphere_nfc_leases` (`012`); PropertyCollector views/tokens and console
tickets persist in `vsphere_pc_state` / `vsphere_console_tickets` (`013`).
Seed profiles (`small` / `large` / `demo-cluster`) build a deterministic cluster —
default **large** is ~10 hosts / **1000 VMs**.
## AuthZ
Credentials map to roles → privilege sets. Mutate handlers use
`require_privilege(...)`; read paths use `require_read`. SOAP Login issues a cookie
compatible with VIM sessions.
## Related docs
- [API coverage](api-coverage.md)
- [Authentication](authentication.md)
- [Web UI](web-ui.md)
- [Clients](clients.md)
+102
View File
@@ -0,0 +1,102 @@
**Language / Язык:** [English](authentication.md) | [Русский](ru/authentication.md)
# Authentication
Primary plane: **vSphere Automation REST** sessions (`vmware-api-session-id`).
SOAP `/sdk` uses its own `Login`/`Logout` on the VIM `SessionManager`. An
optional legacy Proxmox stub plane (`ENABLE_PVE_STUB=true`) keeps historic
`/api2/json/access/ticket` behavior from a shared platform lineage — it is not
the default lab path and is not covered further here.
## Session login (REST)
```http
POST /api/session
Authorization: Basic base64(user:password)
```
Successful response:
- Body: JSON string session id (e.g. `"a1b2c3…"`)
- Header: `vmware-api-session-id: <id>`
- Cookie: `vmware-api-session-id=<id>` (`SameSite=Strict`, 2 hour TTL)
Legacy wrapper (same credentials, `{ "value": "<session-id>" }` shape):
```http
POST /rest/com/vmware/cis/session
Authorization: Basic base64(user:password)
```
### Calling APIs
```bash
SID=$(curl -sk -u 'administrator@vsphere.local:VMware1!' \
-X POST 'https://localhost/api/session' | tr -d '"')
curl -sk -H "vmware-api-session-id: $SID" \
'https://localhost/api/vcenter/vm'
```
Cookie-only clients also work after login (`credentials: include` in the
browser Web UI).
### Session inspect / logout
| Method | Path | Notes |
|---|---|---|
| GET | `/api/session` | HTTP 200 with `x-vmware-session-user` / `x-vmware-session-roles` headers |
| DELETE | `/api/session` | Invalidates the session and clears the cookie |
| GET / DELETE | `/rest/com/vmware/cis/session` | Legacy `{ "value": … }` equivalents |
Sessions live in PostgreSQL (`vsphere_sessions`) with a 2-hour sliding
expiry — every authenticated request extends `expires_at`. Expired sessions
return HTTP 401 on the next lookup and are lazily deleted.
## Seeded lab principals
Password for all: `VMware1!`
| Principal | Role |
|---|---|
| `administrator@vsphere.local` | Administrator |
| `readonly@vsphere.local` | ReadOnly |
| `operator@vsphere.local` | VirtualMachinePowerUser |
| `vmadmin@vsphere.local` | VirtualMachineAdministrator |
Credentials are stored in `vsphere_credentials` (scrypt-hashed passwords,
`roles` array) and are re-inserted idempotently on first `/api/session` call
and by every seed profile. See [Authorization](domains/authz.md) for the
privilege model and [Seed profiles](seed-profiles.md) for how the four
principals map to inventory-scoped permissions.
Mutating endpoints check privileges via `require_privilege(...)`; calling a
mutate path as `readonly@vsphere.local` returns **403**.
## SOAP `/sdk`
```xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:vim25">
<soapenv:Body>
<urn:Login>
<urn:_this type="SessionManager">SessionManager</urn:_this>
<urn:userName>administrator@vsphere.local</urn:userName>
<urn:password>VMware1!</urn:password>
</urn:Login>
</soapenv:Body>
</soapenv:Envelope>
```
`Login` issues the same underlying session id, returned as
`vmware-api-session-id` and as a `vmware_soap_session` cookie; subsequent SOAP
calls (pyvmomi, govmomi, the `hashicorp/vsphere` Terraform provider, Pulumi)
carry that cookie automatically. `Logout` deletes the session. See
[SOAP / VIM](domains/soap.md).
## Optional legacy Proxmox stub
Only when `ENABLE_PVE_STUB=true`: ticket login at `/api2/json/access/ticket`
with `PVEAuthCookie` + CSRF, inherited from the shared simulator platform this
project forked from. It is disabled by default (`ENABLE_PVE_STUB=false`) and
is not exercised by the vSphere docs, examples, or test suites in this
repository.
+83
View File
@@ -0,0 +1,83 @@
**Language / Язык:** [English](clients.md) | [Русский](ru/clients.md)
# Clients
Use the simulator from common VMware automation stacks: Python, Ansible, Terraform, Pulumi.
## Connection matrix
| Stack | Transport | Notes | Code |
|---|---|---|---|
| REST (curl / SDK) | HTTPS `:443` | `vmware-api-session-id` after Basic session | `examples/python/vsphere_rest_smoke.py`, `vsphere_lifecycle.py` |
| SOAP / VIM | HTTPS `:443/sdk` | pyvmomi / govmomi / Terraform / Pulumi providers | `examples/python/vsphere_soap_smoke.py` |
| Legacy `/rest` | HTTPS `:443` | `{ "value": … }` wrappers | `/rest/vcenter/vm` |
| Terraform | HTTPS `:443` | `hashicorp/vsphere` data sources + optional VM resource | `examples/terraform/vsphere/` |
| Ansible | HTTPS `:443` | REST lifecycle playbook (`uri` module) | `examples/ansible/vsphere_playbook.yml` |
| Pulumi | HTTPS `:443` | REST ComponentResource cookbook | `examples/pulumi/` |
| govc | HTTPS `:443` | `GOVC_URL=https://…` insecure | see below |
| Go / Java / Perl | HTTPS `:443` | Minimal REST cookbooks (Basic-auth session) | `examples/go/`, `examples/java/`, `examples/perl/` |
## Credentials (seed)
| User | Password | Role |
|---|---|---|
| `administrator@vsphere.local` | `VMware1!` | Administrator |
| `readonly@vsphere.local` | `VMware1!` | ReadOnly |
| `operator@vsphere.local` | `VMware1!` | VirtualMachinePowerUser |
| `vmadmin@vsphere.local` | `VMware1!` | VirtualMachineAdministrator |
## Inventory seed
```bash
make seed # large: 10 hosts / 1000 VMs
VSPHERE_PROFILE=demo-cluster make seed
VSPHERE_PROFILE=small make seed
```
## Quick cookbooks
```bash
# All four stacks (Python/Ansible/Terraform/Pulumi-style) inside Compose
make client-cookbooks
# Python REST + SOAP CreateVM / NFC
VSPHERE_BASE=https://localhost python examples/python/vsphere_lifecycle.py
# Ansible
ansible-playbook -i examples/ansible/inventory.ini examples/ansible/vsphere_playbook.yml
# Terraform — hashicorp/vsphere data sources (plan) + optional CreateVM resource
cd examples/terraform/vsphere
terraform init
TF_VAR_vsphere_server=localhost TF_VAR_create_lab_vm=false terraform plan
TF_VAR_vsphere_server=localhost TF_VAR_create_lab_vm=true terraform apply
# Pulumi REST
cd examples/pulumi && pulumi up
```
Verified against the gateway (`:443`): Python lifecycle, Ansible playbook, Pulumi-style REST, and `terraform plan` (datacenter/cluster/datastore/network/VM data sources) are green. SOAP `CreateVM_Task` is available for the resource path; use a fresh seed if folder names were renamed by probes (`make seed`).
## govc (optional host tool)
```bash
export GOVC_URL=https://localhost
export GOVC_USERNAME=administrator@vsphere.local
export GOVC_PASSWORD='VMware1!'
export GOVC_INSECURE=1
govc about
govc ls /
govc find / -type m | head
govc vm.info web-01
```
## pyvmomi smoke
```bash
docker compose run --rm --no-deps -e VSPHERE_BASE=http://simulator:8080 \
-e TEST_DATABASE_URL=postgresql://vmware:vmware@postgres:5432/vmware_simulator \
dev pytest tests/compatibility/test_vsphere_pyvmomi.py -q
```
Per-language guides: [examples/overview.md](examples/overview.md). Coverage:
[api-coverage.md](api-coverage.md).
+86
View File
@@ -0,0 +1,86 @@
**Language / Язык:** [English](compatibility-0.1.0.md) | [Русский](ru/compatibility-0.1.0.md)
# Compatibility report — 0.1.0
This report records evidence for simulator release 0.1.0 against the vSphere
Automation API route registry (catalog majors 69, primary contract major 9 /
8.0 U2). It is a limitation matrix for *quality / external integration*
dimensions, not a claim of general vCenter/ESXi hardware compatibility.
For the user-facing overview see [compatibility.md](compatibility.md). Live
machine-readable counts are always available from
`/ui/api/compatibility?major=N` when the simulator is running.
## Summary (major 9 / vSphere 8.0 U2 primary contract)
| Level | Methods | Universe share | Evidence |
|---|---:|---:|---|
| Declared in universe (Broadcom operations index → route table) | 1077 | 100% | `app/vsphere/rest/universe.json` |
| Implemented at major 9 (catalog floor) | **1077** | **100%** | `app/vsphere/contracts/matrix.py` |
| Core deep handlers (inventory/lifecycle/tagging/content/appliance) | 104 | 9.7% | `app/vsphere/rest/coverage.py` (`CORE_IMPLEMENTED`) |
| DB-backed stub surface (remaining registry) | ~973 | 90.3% | `app/vsphere/rest/stub_surface.py` against `vsphere_api_state` |
| Verified / observed surface ledger | **1077** | **100%** | `evidence/vsphere-8.0.2.json` |
## Coverage by catalog major
| Major | vSphere label | Implemented | Universe | Coverage |
|---|---|---:|---:|---:|
| 6 | 7.0 | 31 | 1077 | 2.88% |
| 7 | 7.0 U3 | 77 | 1077 | 7.15% |
| 8 | 8.0 | 103 | 1077 | 9.56% |
| 9 | 8.0 U2 | 1077 | 1077 | 100.00% |
**Implemented** here is a catalog-floor score for Web UI browse and evidence
reports, regenerated with `make evidence` / `make vsphere-bundles` and
guarded by `tests/compatibility/test_verified_surface.py`. It does **not**
gate live traffic — see [API surface](api-surface.md) for why runtime always
serves the registered route regardless of the applied major.
## Implemented surface (high level)
- **Session**: `/api/session`, `/rest/com/vmware/cis/session`, SOAP
`Login`/`Logout` — all durable in PostgreSQL (`vsphere_sessions`,
`vsphere_credentials`).
- **Inventory**: VM/host/datastore/network/datacenter/cluster/folder/resource-pool
list+get, plus create/delete for datacenter/cluster/folder/resource-pool.
- **VM lifecycle**: create, delete, power, hardware (CPU/memory/disk/NIC/boot),
snapshots, clone, relocate, guest identity/networking/power/customization,
console tickets, tools.
- **Tasks**: `/api/cis/tasks`, real ids from `vsphere_tasks`, SOAP task MoRefs.
- **Tagging / content library**: categories, tags, associations, libraries,
library items, update/download sessions, OVF deploy.
- **Authorization**: privileges, roles, permissions CRUD, identity providers.
- **Appliance**: version, health, networking (hostname/DNS), timesync.
- **SOAP / VIM**: RetrieveServiceContent, PropertyCollector
(RetrieveProperties/Ex, ContinueRetrievePropertiesEx, CreateFilter,
WaitForUpdatesEx), FindBy* / FindChild, CreateVM_Task and friends, guest
file operations, HttpNfcLease import flow, WSDL stub.
- **Platform lab surfaces**: seeded (non-binary-compatible) NSX/Supervisor/vSAN/
SAML-OIDC/VECS-cert stand-ins — see [API coverage](api-coverage.md) for the
exact list and caveats.
## Persistence principle
Every create/update/delete path writes to PostgreSQL (tables and/or the
`vsphere_api_state` catch-all). Secrets may be stored but must not be echoed
on GET. User-facing "not supported in the emulator" errors are forbidden for
registered paths — see `.cursor/rules/durable-simulator.mdc`.
## Known limitations
| Area | Current behavior |
|---|---|
| External systems | NSX/LDAP/SAML/OIDC/ACME do not contact real remotes; state is simulated locally |
| TLS | Local nginx gateway with a checked-in self-signed development key only |
| Client certification | pyvmomi/govmomi-style SOAP smoke + Ansible/Terraform/Pulumi cookbooks; not a formal certification suite for every provider version |
| Provider smoke | The `pulumi-vsphere` suite under `pulumi-tests/` (`make pulumi-tests`) exercises SOAP-backed inventory/VM/tag resources with nonempty export checks; semantic depth still varies (deep handlers vs DB-backed stubs) |
Full registry coverage at major 9 means HTTP 501 "handler pending" should not
appear for any route in the simulator's registry. Compatibility *quality*
(exact vSphere edge-case parity) still deepens with tests and observation.
When importing a refreshed Broadcom operations index dump: regenerate
`universe.json` (`make vsphere-universe`), regenerate bundles/evidence
(`make vsphere-bundles`, `make evidence`), run
`pytest tests/compatibility/test_verified_surface.py`, and commit the updated
`evidence/vsphere-*.json` ledgers.
+74
View File
@@ -0,0 +1,74 @@
**Language / Язык:** [English](compatibility.md) | [Русский](ru/compatibility.md)
# Compatibility
This document explains how the simulator claims compatibility with the
vSphere Automation API across catalog majors **69**. Prefer live reports
when the process is running.
## Live reports
| URL | Format |
|---|---|
| `/ui/api/compatibility?major=N` | JSON |
The Web UI also exposes a compatibility panel driven by this endpoint.
## Registry vs verified surface coverage
| Major | vSphere label | Implemented / universe | Coverage |
|---|---|---:|---:|
| 6 | 7.0 | 31 / 1077 | 2.9% |
| 7 | 7.0 U3 | 77 / 1077 | 7.2% |
| 8 | 8.0 | 103 / 1077 | 9.6% |
| 9 | 8.0 U2 (Automation 9.1 surface) | **1077 / 1077** | **100%** |
- **Universe** — unique verb+path routes derived from the public
[vSphere Automation API operations index](https://developer.broadcom.com/xapis/vsphere-automation-api/latest/operation-index/)
(1348 documented operations → ~1037 unique routes → 1077 registered in this
simulator's route table, since some paths back multiple named operations).
- **Implemented (per major)** — routes whose catalog floor
(`app/vsphere/contracts/matrix.py`) is at or below that major. This is a
**catalog/documentation** score, not a live-traffic gate.
- **Runtime** — regardless of the applied catalog major, every registered
route is always served by its real handler (104 deep handlers) or the
DB-backed stub surface. See [API surface](api-surface.md).
After **Apply as runtime** (`POST /ui/api/contract/apply?major=N`), the live
report loads that major's ledger (`evidence/vsphere-{version}.json`) so the
Web UI compatibility panel reflects the selected major.
## Evidence dimensions
Per-major ledgers in `evidence/vsphere-{version}.json` record `declared`,
`implemented`, `observed`, and `verified` counts plus per-HTTP-verb and
per-domain (`auth_session`, `inventory`, …) breakdowns. Regenerate with:
```bash
make evidence # app/evidence_gen.py
make vsphere-bundles # stub OpenAPI matrices + evidence ledgers together
```
Executable backing for those claims:
| Suite | Role |
|---|---|
| `tests/compatibility/test_verified_surface.py` | hot-swap + ledger drift + score gates |
| `tests/compatibility/test_group_smoke.py` | representative REST group mutations with PostgreSQL |
| `tests/compatibility/test_vsphere_pyvmomi.py` | external pyvmomi SOAP smoke |
| `tests/integration/test_vsphere_full_api.py` | broad REST/SOAP integration coverage |
Additional cookbooks under [`examples/`](../examples/README.md) and the
`pulumi-vsphere` lab suite under [`pulumi-tests/`](../pulumi-tests/README.md)
(`make pulumi-tests`) are manual or CI-optional depending on the stack.
## Known behavioural limits
| Area | Behaviour |
|---|---|
| External systems | NSX Manager, live LDAP/SAML/OIDC IdPs, and ACME directories do not contact real remotes; seeded/local state only |
| TLS | Local self-signed development gateway only (Compose); use your own certs / cert-manager for real deployments |
| Hypervisor | No real ESXi/KVM execution; no binary NFC uploads |
| Observation corpus | Sanitized real-vCenter observation data remains limited; deep semantic parity is verified path-by-path via the suites above, not by exhaustive production diffing |
Historical release notes: [compatibility-0.1.0.md](compatibility-0.1.0.md).
+96
View File
@@ -0,0 +1,96 @@
**Language / Язык:** [English](configuration.md) | [Русский](ru/configuration.md)
# Configuration
Application settings are loaded from the environment (see `.env.example`).
Docker Compose injects many of these for the `simulator` service; values
declared under `environment:` in `docker-compose.yml` override `.env` for that
service. The typed settings model lives in [`app/config.py`](../app/config.py).
## Core
| Variable | Default / example | Meaning |
|---|---|---|
| `APP_HOST` | `0.0.0.0` | Bind address |
| `APP_PORT` | `8080` | Internal uvicorn listen port (not published; the gateway publishes vCenter HTTPS) |
| `DATABASE_URL` | `postgresql://vmware:vmware@postgres:5432/vmware_simulator` | asyncpg DSN |
| `DB_POOL_MIN_SIZE` | `1` | Pool minimum |
| `DB_POOL_MAX_SIZE` | `10` | Pool maximum |
| `DB_CONNECT_TIMEOUT_SECONDS` | `10` | Connect timeout |
| `DB_COMMAND_TIMEOUT_SECONDS` | `30` | Command timeout |
| `LOG_LEVEL` | `INFO` | Logging level |
| `REQUEST_ID_HEADER` | `X-Request-ID` | Request correlation header |
## vSphere seed inventory
| Variable | Default | Meaning |
|---|---|---|
| `SEED_VSPHERE_PROFILE` | `large` | `small` \| `large` \| `demo-cluster` — see [Seed profiles](seed-profiles.md) |
| `SEED_VSPHERE_LARGE_HOSTS` | `10` | Host count for the `large` profile |
| `SEED_VSPHERE_LARGE_VMS` | `1000` | VM count for the `large` profile |
## Optional legacy plane
| Variable | Default | Meaning |
|---|---|---|
| `ENABLE_PVE_STUB` | `false` | Enables the legacy Proxmox VE `/api2/*` stub plane inherited from a shared platform lineage. Native vSphere `/api` + `/rest` + `/sdk` is the default and primary plane regardless of this flag. |
## Contract and catalog
| Variable | Meaning |
|---|---|
| `CONTRACT_SNAPSHOT` | Optional path to a normalized PVE-style snapshot (only relevant with `ENABLE_PVE_STUB=true`) |
| `CONTRACT_FALLBACK` | `error` (default), `schema-default`, or `fixture` — fallback behaviour for the optional stub plane |
| `COMPATIBILITY_EVIDENCE` | Optional evidence JSON path used by compatibility reports |
| `CATALOG_ARTIFACT_URL_6``_9` | Labels backing the vSphere catalog majors (6→7.0, 7→7.0 U3, 8→8.0, 9→8.0 U2); stub URLs, not live downloads |
Runtime hot-swap (Web UI / `POST /ui/api/contract/apply?major=N`) switches the
active **catalog** major used by the Web UI and compatibility/evidence
reports. It does not gate the registered REST/SOAP surface — every known
route is always served with its real handler or DB-backed stub. See
[API versions](api-versions.md).
## Security and tasks
| Variable | Meaning |
|---|---|
| `TICKET_SIGNING_KEY` | HMAC signing key for sessions (**change outside toy labs**) |
| `TASK_WORKER_CONCURRENCY` | Number of leased asyncio workers (132) |
| `TASK_LEASE_SECONDS` | PostgreSQL task lease duration |
| `SIMULATION_TIME_SCALE` | Accelerates simulated task durations (higher = faster) |
## Client test hooks
| Variable | Meaning |
|---|---|
| `TEST_DATABASE_URL` | Integration-test DSN |
| `VSPHERE_BASE` | Target base URL used by cookbooks/probes (`https://localhost` from the host, `http://simulator:8080` from inside Compose) |
## Ports and TLS
| Endpoint | Use |
|---|---|
| `https://localhost` | Primary vCenter HTTPS entry (curl, browsers, pyvmomi, govmomi, Terraform, most examples) |
| `http://localhost` | HTTP lab face |
| `localhost:5434` | PostgreSQL (localhost only) |
| Internal `simulator:8080` | Direct FastAPI process; only reachable inside the Compose network |
The checked-in certificate under `docker/tls/` is disposable development
material. Never reuse it outside local labs. See [Security](security.md) and
[Ports](ports.md).
## Compose notes
- `migrate` runs once; `simulator` waits for a successful migrate.
- Development Compose bind-mounts the repository and enables Uvicorn reload.
- The `api-gateway` (nginx) service publishes `443`/`80` and proxies to the
internal `simulator:8080` process; it sets `X-VMware-Service` /
`X-Forwarded-Port` so future routers can tell which listener was used.
## Open and unused example keys
`.env.example` still lists a few keys from the shared platform lineage that
are **not** consumed by the current vSphere-first settings model, notably
`SIMULATION_SEED`, `SIMULATOR_ADMIN_ENABLED`, and `SIMULATOR_ADMIN_TOKEN`. Do
not assume an authenticated `/_simulator` admin API exists today — see
[Security](security.md).
+42
View File
@@ -0,0 +1,42 @@
**Language / Язык:** [English](README.md) | [Русский](../ru/domains/README.md)
# Domain guides
These pages summarize durable semantics by area. For exhaustive method lists,
use the Web UI catalog or OpenAPI (`/docs`), or browse
[`app/vsphere/rest/coverage.py`](../../app/vsphere/rest/coverage.py) directly
— the runtime always serves the full registered surface regardless of the
active catalog major.
| Guide | Topics |
|---|---|
| [Session](session.md) | `/api/session`, legacy `/rest` session, SOAP `Login`/`Logout` |
| [Inventory](inventory.md) | Datacenter, cluster, folder, resource pool, host, datastore, network CRUD |
| [Virtual machines](vm.md) | Create/delete, power, hardware, snapshots, clone, relocate, guest ops |
| [Storage](storage.md) | Datastores, files, host storage devices, storage policies |
| [Networking](networking.md) | Standard/distributed portgroups, DVS, host networking |
| [Tagging](tagging.md) | Categories, tags, associations |
| [Content library](content-library.md) | Libraries, items, update/download sessions, OVF deploy |
| [SOAP / VIM](soap.md) | RetrieveServiceContent, PropertyCollector, task-returning operations |
| [Tasks](tasks.md) | CIS task ids, polling, workers |
| [Appliance](appliance.md) | Version, health, networking, timesync |
| [Authorization](authz.md) | Roles, privileges, permissions |
## Persistence map
- Inventory objects (hosts, VMs, datastores, networks, folders, …) →
`vsphere_objects` (MOID, type, name, parent, `props` JSONB).
- Sessions / credentials → `vsphere_sessions`, `vsphere_credentials`.
- Tasks → `vsphere_tasks`.
- Tags / categories / associations → `vsphere_tag_categories`,
`vsphere_tags`, `vsphere_tag_associations`.
- Content libraries / items → `vsphere_libraries`, `vsphere_library_items`.
- Datastore file metadata → `vsphere_datastore_files`.
- Remaining Broadcom Automation API routes (the DB-backed stub surface) →
`vsphere_api_state` (migration `011`).
- Content-library update/download sessions → `vsphere_transfer_sessions`
(migration `012`).
- HttpNfcLease transfer state → `vsphere_nfc_leases` (migration `012`).
- PropertyCollector views / WaitForUpdates tokens → `vsphere_pc_state`
(migration `013`).
- Console tickets → `vsphere_console_tickets` (migration `013`).
+35
View File
@@ -0,0 +1,35 @@
**Language / Язык:** [English](appliance.md) | [Русский](../ru/domains/appliance.md)
# Appliance
vCenter Server Appliance (VCSA) surfaces — version, health, networking,
timesync:
[`app/vsphere/rest/appliance_ext.py`](../../app/vsphere/rest/appliance_ext.py),
[`app/vsphere/domain/appliance.py`](../../app/vsphere/domain/appliance.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/appliance/system/version` | Readable without a session; reflects the active catalog major's label |
| GET | `/api/appliance/health/system` | Overall health summary |
| GET/PUT/POST | `/api/appliance/networking` | Hostname, DNS, default gateway, interfaces, proxy |
| GET/PUT/POST | `/api/appliance/networking/dns/hostname` \| `/dns/servers` \| `/dns/domains` | Focused mirrors kept in sync with `/networking` |
| GET | `/api/appliance/timesync` | NTP mode + servers |
| GET | `/api/vcenter/certificate-management/vcenter/tls[-csr]` \| `/trusted-root-chains` | Machine-cert / CSR / trust-chain stand-ins |
## Highlights
- Defaults model a realistic single-nic VCSA (`vcenter.lab.local`,
`192.168.1.50/24`, gateway `192.168.1.1`, `8.8.8.8`/`1.1.1.1` DNS).
- `save_networking` keeps the focused DNS mirrors
(`/dns/hostname`, `/dns/servers`, `/dns/domains`) consistent with the full
`/networking` document so both shapes of Automation API client work.
- State is idempotently seeded once per fresh database
(`seed_appliance_state`) and persists in `vsphere_api_state`.
- The TLS/certificate-management endpoints are seeded stand-ins, not a real
VECS certificate store — see [API coverage](../api-coverage.md).
`/api/appliance/system/version` intentionally does not require a session in
this lab build (real vCenter behavior varies by version) so smoke scripts can
check availability before authenticating.
+52
View File
@@ -0,0 +1,52 @@
**Language / Язык:** [English](authz.md) | [Русский](../ru/domains/authz.md)
# Authorization
Role → privilege gate for REST mutate endpoints (and a decorator-style hook
for SOAP): [`app/vsphere/security/authz.py`](../../app/vsphere/security/authz.py),
[`platform_rest.py`](../../app/vsphere/rest/platform_rest.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/vcenter/privilege` | Privilege catalog |
| GET | `/api/vcenter/authorization/roles` | Role → privilege set |
| GET/POST/DELETE | `/api/vcenter/authorization/permissions[/{permission_id}]` | Principal ↔ role ↔ entity bindings |
| GET/POST/PATCH/DELETE | `/api/vcenter/identity/providers[/{provider}]` | LocalOS + OIDC + SAML identity-provider stand-ins |
## Roles (seed)
| Role | Scope |
|---|---|
| `Administrator` | Every privilege in the catalog |
| `ReadOnly` | `System.Anonymous`, `System.Read`, `System.View`, `Datastore.Browse` |
| `VirtualMachinePowerUser` | Read + power/snapshot/clone interactions |
| `VirtualMachineAdministrator` | Power-user set + create/delete/reconfigure/tag/content-library privileges |
`ROLE_PRIVILEGES` in `authz.py` defines the exact privilege sets; a
non-exhaustive sample of gated privileges: `VirtualMachine.Inventory.Create`,
`VirtualMachine.Inventory.Delete`, `VirtualMachine.Interact.PowerOn`,
`VirtualMachine.Config.CPUCount`, `VirtualMachine.Provisioning.Clone`,
`Datastore.FileManagement`, `Network.Assign`,
`InventoryService.Tagging.CreateTag`, `ContentLibrary.AddLibraryItem`,
`Authorization.ModifyPermissions`.
## How gating works
- `require_privilege(*needed)` is a FastAPI dependency factory: it resolves
the session, loads roles (from the session or `vsphere_credentials` if
absent), and raises HTTP 403 (`unauthorized`) if any listed privilege is
missing.
- `require_read` is shorthand for `require_privilege("System.Read")`.
- Permissions can also scope a role to a specific entity MOID
(`PermissionSpec(principal, role, entity_moid, propagate)`); the seed
scopes `readonly@vsphere.local` to the datacenter and the two VM-admin
principals to the VM folder.
## Seeded principals
See [Authentication](../authentication.md) for the four
`@vsphere.local` principals and their roles, and
[Seed profiles](../seed-profiles.md) for how permissions are scoped per
profile.
+38
View File
@@ -0,0 +1,38 @@
**Language / Язык:** [English](content-library.md) | [Русский](../ru/domains/content-library.md)
# Content library
Local content libraries, library items, upload/download sessions, and OVF
deploy:
[`app/vsphere/rest/content_rest.py`](../../app/vsphere/rest/content_rest.py),
[`nfc_rest.py`](../../app/vsphere/rest/nfc_rest.py),
[`app/vsphere/domain/content.py`](../../app/vsphere/domain/content.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/content/library` | List library ids |
| POST | `/api/content/local-library` | Create a local library |
| GET/POST | `/api/content/library/item` | List / create items (`?library_id=`) |
| POST | `/api/vcenter/ovf/library-item/{item_id}` | Deploy OVF item → new `VirtualMachine` + task |
| POST | `/api/content/library/item/update-session[/{session_id}[/file]]` | Push-upload flow (Ansible/Terraform-style) |
| GET/POST | `/api/content/library/item/download-session[/{session_id}[/file]]` | Pull-download flow |
| GET/PUT/POST | `/nfc/{lease}` \| `/nfc/{lease}/files/{filename}` \| `/nfc/{lease}/complete` | HttpNfcLease-style transfer endpoints for the SOAP import path |
## Highlights
- Libraries/items persist in `vsphere_libraries` / `vsphere_library_items`;
the seed creates two libraries ("Local Content", "Published Templates")
with OVF-typed items (`ubuntu-22.04`, `centos-stream-9`, `golden-image`).
- Update/download sessions persist in PostgreSQL (`vsphere_transfer_sessions`,
migration `012`) and model the file-transfer handshake — not a real
byte-for-byte OVF/VMDK store. HttpNfcLease rows live in `vsphere_nfc_leases`.
- `deploy_ovf_from_library` creates a real `VirtualMachine` row and returns a
task id, mirroring the SOAP `ImportVApp_Task` / `CreateImportSpec` +
`HttpNfcLease*` flow used by govc-style `ovf.import`.
- Requires `ContentLibrary.CreateLocalLibrary` / `.AddLibraryItem` to create,
and `VirtualMachine.Provisioning.DeployTemplate` to deploy.
See [SOAP / VIM](soap.md) for the HttpNfcLease progress/complete/abort
operations used by upload-heavy clients.
+46
View File
@@ -0,0 +1,46 @@
**Language / Язык:** [English](inventory.md) | [Русский](../ru/domains/inventory.md)
# Inventory
Datacenter, cluster, folder, resource pool, host, and datastore/network
listing + CRUD:
[`app/vsphere/rest/router.py`](../../app/vsphere/rest/router.py),
[`app/vsphere/domain/inventory_ops.py`](../../app/vsphere/domain/inventory_ops.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/vcenter/datacenter` | List |
| POST/DELETE | `/api/vcenter/datacenter[/{datacenter}]` | Create seeds host/vm/datastore/network sub-folders |
| GET | `/api/vcenter/cluster` | List |
| POST/DELETE | `/api/vcenter/cluster[/{cluster}]` | Create seeds a `ResourcePool` |
| GET | `/api/vcenter/folder` | List; `GET /api/vcenter/folder/{folder}/children` |
| POST/DELETE | `/api/vcenter/folder[/{folder}]` | |
| GET | `/api/vcenter/resource-pool` | List |
| POST/DELETE | `/api/vcenter/resource-pool[/{resource_pool}]` | |
| GET | `/api/vcenter/host[/{host}]` | Connection state, CPU/memory, IP, storage devices, networking |
| POST | `/api/vcenter/host/{host}/maintenance` | Toggle maintenance mode |
| GET | `/api/vcenter/datastore[/{datastore}]` | Type, capacity, free space, accessibility |
| GET | `/api/vcenter/network` | Standard networks + distributed portgroups |
Legacy `/rest/vcenter/*` mirrors most GET paths with a `{ "value": … }`
envelope — see [API surface](../api-surface.md).
## Highlights
- Every inventory object is a row in `vsphere_objects` (MOID, type, name,
`parent_moid`, `props` JSONB) — see
[`app/vsphere/inventory.py`](../../app/vsphere/inventory.py).
- MOID conventions follow real vCenter shapes: `datacenter-NN`,
`domain-cNN` (cluster), `resgroup-NN` (resource pool), `group-vNN`/`group-hNN`/
`group-sNN`/`group-nNN` (VM/host/datastore/network folders), `host-NN`,
`datastore-NN`, `network-NN` / `dvportgroup-NN`.
- `list_hosts`/`list_clusters`/etc. filter live PostgreSQL state; there is no
separate cache to invalidate after a mutation.
- VM listing (`GET /api/vcenter/vm`) supports filters: `names`,
`power_states`, `hosts`, `folders`, `datacenters`, `clusters`,
`resource_pools`, plus `limit`/`cursor` pagination.
See [Seed profiles](../seed-profiles.md) for the default topology shape and
[Virtual machines](vm.md) for VM-specific operations.
+35
View File
@@ -0,0 +1,35 @@
**Language / Язык:** [English](networking.md) | [Русский](../ru/domains/networking.md)
# Networking
Standard networks, distributed portgroups/switches, and host networking:
[`app/vsphere/rest/router.py`](../../app/vsphere/rest/router.py),
[`platform_rest.py`](../../app/vsphere/rest/platform_rest.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/vcenter/network` | Standard `Network` objects + `DistributedVirtualPortgroup` |
| GET/POST | `/api/vcenter/network/dvs` | Distributed virtual switches |
| POST | `/api/vcenter/network/dvpg` | Create a distributed portgroup |
| GET | `/api/vcenter/host/{host}/networking` | DNS, default gateway, `vmk0` interface, routing |
| GET/PUT/POST | `/api/appliance/networking` \| `/networking/dns/{hostname,servers,domains}` | vCenter appliance-level networking (see [Appliance](appliance.md)) |
Legacy `GET /rest/vcenter/network` mirrors the list.
## Highlights
- Every VM's `nics[].value.backing` points at either a `STANDARD_PORTGROUP`
(`network-41`, "VM Network") or a `DISTRIBUTED_PORTGROUP`
(`dvportgroup-4N`, tagged with a `vlan_id`).
- The default topology seeds one `VmwareDistributedVirtualSwitch`
(`dvs-51`, `mtu: 9000`) and 13 extra distributed portgroups depending on
profile size.
- Host networking (`GET /api/vcenter/host/{host}/networking`) returns DNS
servers/domains, a default gateway, and a single `vmk0` management
interface with a deterministic IPv4 address per host index.
- NSX-labelled Automation API paths (tier-0 gateway, projects, edges,
VPC/subnets) are seeded lab stand-ins under `namespace-management` — see
the "Platform surfaces" table in [API coverage](../api-coverage.md); they
are not a real NSX Manager.
+31
View File
@@ -0,0 +1,31 @@
**Language / Язык:** [English](session.md) | [Русский](../ru/domains/session.md)
# Session
Durable session identity shared by REST and SOAP:
[`app/vsphere/security/session.py`](../../app/vsphere/security/session.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| POST | `/api/session` | Basic auth → JSON string session id + `vmware-api-session-id` header/cookie |
| GET | `/api/session` | HTTP 200; `x-vmware-session-user` / `x-vmware-session-roles` headers |
| DELETE | `/api/session` | Invalidates the session, clears cookie |
| POST/GET/DELETE | `/rest/com/vmware/cis/session` | Legacy `{ "value": … }` equivalents |
| POST | SOAP `SessionManager.Login` | Returns the same session id; sets `vmware_soap_session` cookie |
| POST | SOAP `SessionManager.Logout` | Deletes the session |
## Highlights
- Sessions are opaque 32-char hex tokens stored in `vsphere_sessions` with a
**2-hour sliding TTL** — every authenticated call extends `expires_at`.
- The four lab credentials (`vsphere_credentials`, scrypt-hashed) are
idempotently ensured on first login and by every seed profile
(`ensure_default_credentials`).
- `require_session` resolves the session from either the
`vmware-api-session-id` header or cookie; missing/expired → HTTP 401.
- Roles are attached to the session at lookup time
(`vsphere_credentials.roles`) and drive [Authorization](authz.md).
See [Authentication](../authentication.md) for full request examples.
+68
View File
@@ -0,0 +1,68 @@
**Language / Язык:** [English](soap.md) | [Русский](../ru/domains/soap.md)
# SOAP / VIM
Minimal VIM SDK for pyvmomi / govmomi-style clients (Terraform's
`hashicorp/vsphere` provider, Pulumi, govc):
[`app/vsphere/soap/router.py`](../../app/vsphere/soap/router.py),
[`property_collector.py`](../../app/vsphere/soap/property_collector.py),
[`pbm.py`](../../app/vsphere/soap/pbm.py).
## Endpoint
All operations POST a SOAP envelope to `/sdk` (also `/sdk/`). Auxiliary
routes:
| Method | Path | Notes |
|---|---|---|
| GET | `/sdk/vimService.wsdl` (alias `/sdk/vim.wsdl`) | WSDL stub advertising the implemented operation list |
| GET | `/sdk/about.do` (alias `/about.do`) | Human-readable "VMware vCenter Server" page |
| POST | `/sdk/vim25/{version}/SessionManager/SessionManager/Login` | JSON-body login variant used by some SDKs |
## Implemented operations
- `RetrieveServiceContent`, `Login`, `Logout`
- `RetrieveProperties`, `RetrievePropertiesEx`, **ContinueRetrievePropertiesEx**
(pagination tokens; `<objects>` plural), `CreateFilter`,
`WaitForUpdatesEx` (version tokens; empty polls), `CreateContainerView`,
`DestroyPropertyFilter`
- `FindByInventoryPath` (paths omit the root `Datacenters` folder, matching
govmomi conventions), `FindByUuid`, `FindByDnsName`, `FindByIp`, `FindChild`
- `CreateVM_Task`, `CreateChildVM_Task`, `CreateFolder`, `PowerOnVM_Task`,
`PowerOffVM_Task`, `CloneVM_Task`, `CreateSnapshot_Task`, `Rename_Task`,
`ReconfigVM_Task`, `RelocateVM_Task`, `Destroy_Task`, `CustomizeVM_Task`,
`CancelTask`, `CurrentTime`
- Guest file ops: `InitiateFileTransferToGuest`,
`InitiateFileTransferFromGuest`, `ListFilesInGuest`, `DeleteFileInGuest`,
`MakeDirectoryInGuest`
- Import/upload: `ImportVApp_Task`, `CreateImportSpec`,
`HttpNfcLeaseComplete`, `HttpNfcLeaseProgress`, `HttpNfcLeaseAbort`,
`HttpNfcLeaseGetManifest` (paired with the REST `/nfc/{lease}` endpoints —
see [Content library](content-library.md))
- `QueryConfigOption`, `QueryConfigOptionEx`, `QueryConfigOptionDescriptor`,
`QueryConfigTarget`
- PBM (`/pbm`) stub for storage-policy-aware clients
## Highlights
- `Login` issues the same underlying session as REST (`vmware-api-session-id`
cookie/header, plus a `vmware_soap_session` cookie) — see
[Session](session.md).
- `VIM_VERSION` is pinned to `8.0.2` with ≤3 dotted components, since
`hashicorp/vsphere` parses `AboutInfo.version` strictly.
- Type-strict MOR lookup rejects a `VirtualApp:resgroup-*` reference from
resolving as a plain `ResourcePool` — matters for the Terraform
`CreateVM_Task` resource path.
- Task-returning operations create a real row in `vsphere_tasks` (shared with
REST — see [Tasks](tasks.md)), including `info.result` MoRefs on
create/clone.
- PropertyCollector filters, ContainerViews, and WaitForUpdatesEx version
tokens persist in `vsphere_pc_state` (migration `013`) across process
restarts within a lab.
- `Folder.childType` is emitted as `ArrayOfString`; string properties carry
`xsi:type="xsd:string"` so govmomi's decoder accepts them; `Datastore.host`
is `ArrayOfDatastoreHostMount`; `Cluster`/`Host` expose `environmentBrowser`.
See [Clients](../clients.md) for pyvmomi/govmomi/Terraform/Pulumi connection
examples and [examples/python/vsphere_soap_smoke.py](../../examples/python/vsphere_soap_smoke.py)
for a minimal raw-XML smoke.
+37
View File
@@ -0,0 +1,37 @@
**Language / Язык:** [English](storage.md) | [Русский](../ru/domains/storage.md)
# Storage
Datastores, datastore file metadata, host storage devices, and storage
policies:
[`app/vsphere/rest/router.py`](../../app/vsphere/rest/router.py),
[`content_rest.py`](../../app/vsphere/rest/content_rest.py),
[`platform_rest.py`](../../app/vsphere/rest/platform_rest.py),
[`app/vsphere/domain/content.py`](../../app/vsphere/domain/content.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/vcenter/datastore[/{datastore}]` | Type (`VMFS`/`NFS`), capacity, free space, `multiple_host_access` |
| GET/POST | `/api/vcenter/datastore/{datastore}/files` | List / register file metadata (ISOs, VMX, VMDK paths) |
| GET | `/api/vcenter/host/{host}/storage/storage-device` | Seeded local disk devices (`naa.*`, capacity, SSD flag) |
| GET | `/api/vcenter/storage/policies[/{policy}/vm]` | Storage-based policy management, incl. `policy_type: VSAN` lab policies |
Legacy `GET /rest/vcenter/datastore` mirrors the list in a
`{ "value": … }` envelope.
## Highlights
- Datastore rows are seeded with realistic capacity/free-space pairs
(`type`, `capacity`, `free_space`, `accessible`,
`multiple_host_access`) — see
[`app/vsphere/profiles.py`](../../app/vsphere/profiles.py).
- File metadata lives in `vsphere_datastore_files` (`path`, `size`, `type`);
the seed pre-populates ISOs and a VM's `.vmx`/`.vmdk` entries
(`seed_platform_extras`).
- Storage policies include a lab `RAID1` vSAN-labelled policy — see the
"Platform surfaces" table in [API coverage](../api-coverage.md) for the
vSAN caveat (seeded lab data, not a real vSAN cluster).
- Host storage devices are per-host synthetic disks, not real ESXi VMFS
extents — capacity/SSD flags vary deterministically by host index.
+31
View File
@@ -0,0 +1,31 @@
**Language / Язык:** [English](tagging.md) | [Русский](../ru/domains/tagging.md)
# Tagging
CIS tagging service (categories, tags, object associations):
[`app/vsphere/rest/tagging_rest.py`](../../app/vsphere/rest/tagging_rest.py),
[`app/vsphere/domain/tagging.py`](../../app/vsphere/domain/tagging.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET/POST | `/api/cis/tagging/category` | List / create (`cardinality`, `associable_types`) |
| GET/DELETE | `/api/cis/tagging/category/{category_id}` | |
| GET/POST | `/api/cis/tagging/tag` | List / create under a category |
| GET/DELETE | `/api/cis/tagging/tag/{tag_id}` | |
| POST | `/api/cis/tagging/tag-association` | Attach/detach a tag to/from an object |
## Highlights
- Category and tag ids follow the real `urn:vmomi:InventoryServiceCategory:…`
/ `urn:vmomi:InventoryServiceTag:…:GLOBAL` shape.
- Rows persist in `vsphere_tag_categories`, `vsphere_tags`,
`vsphere_tag_associations` — durable across restarts, replaced on reseed.
- The seed creates two categories (`Environment`, `Owner`) with `prod`/
`staging`/`platform` tags and attaches `prod` to two seeded VMs
(`seed_platform_extras` in
[`app/vsphere/domain/content.py`](../../app/vsphere/domain/content.py)).
- Attaching/creating a tag requires
`InventoryService.Tagging.CreateCategory` / `.CreateTag` / `.AttachTag`
privileges — see [Authorization](authz.md).
+37
View File
@@ -0,0 +1,37 @@
**Language / Язык:** [English](tasks.md) | [Русский](../ru/domains/tasks.md)
# Tasks
Long-running operations (power, clone, relocate, snapshot, OVF deploy, guest
customize) return a CIS-style task id:
[`app/vsphere/domain/tasks.py`](../../app/vsphere/domain/tasks.py),
[`app/vsphere/rest/tasks.py`](../../app/vsphere/rest/tasks.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/cis/tasks` | List recent tasks (most recent 200) |
| GET | `/api/cis/tasks/{task}` | Status, progress, `service`/`operation`, `result`/`error` |
## Client pattern
1. `POST`/`DELETE` mutation → read the task id from `{ "task": "task-…" }`
(REST) or the SOAP `*_Task` MoRef.
2. Poll `GET /api/cis/tasks/{task}` until `status` is `SUCCEEDED` or `FAILED`.
3. `result` holds operation-specific output (for example `{"vm": "vm-104"}`
on create/clone/deploy).
## Highlights
- Task rows commit to `vsphere_tasks` (`id`, `description`, `status`,
`service`, `operation`, `result`, `error`, `completed_at`).
- `progress` is synthesized as `50` while running and `100` once terminal —
this simulator does not model fractional progress.
- The same task store backs both REST `/api/cis/tasks` and SOAP task MoRefs,
so a Terraform apply (SOAP `CreateVM_Task`) and a REST poll of the same id
see consistent state.
- Simulation durations honour `SIMULATION_TIME_SCALE`
(higher = faster simulated completion).
See [API surface](../api-surface.md) and [Operations](../operations.md).
+50
View File
@@ -0,0 +1,50 @@
**Language / Язык:** [English](vm.md) | [Русский](../ru/domains/vm.md)
# Virtual machines
Full REST lifecycle for `VirtualMachine` objects:
[`app/vsphere/rest/router.py`](../../app/vsphere/rest/router.py),
[`vm_ext.py`](../../app/vsphere/rest/vm_ext.py),
[`app/vsphere/domain/vm_ops.py`](../../app/vsphere/domain/vm_ops.py).
## Endpoints
| Method | Path | Notes |
|---|---|---|
| GET | `/api/vcenter/vm` | List with `names`/`power_states`/`hosts`/`folders`/`datacenters`/`clusters`/`resource_pools`/`limit`/`cursor` filters |
| GET/DELETE | `/api/vcenter/vm/{vm}` | Get / delete (must be powered off) |
| POST | `/api/vcenter/vm` | Create — `placement.{folder,host,datastore,resource_pool}`, `cpu.count`, `memory.size_MiB`, `disks`, `nics` |
| GET/POST | `/api/vcenter/vm/{vm}/power` | Get power state / `?action=start\|stop\|suspend\|reset` — returns `{ "task": "task-…" }` |
| GET | `/api/vcenter/vm/{vm}/hardware` | Summary |
| GET/PATCH | `/api/vcenter/vm/{vm}/hardware/cpu` \| `/memory` | Change CPU count / memory (privilege-gated) |
| GET/POST | `/api/vcenter/vm/{vm}/hardware/disk` \| `/ethernet` | Add disk / NIC |
| GET | `/api/vcenter/vm/{vm}/hardware/boot` | Boot type/order |
| GET/POST/DELETE | `/api/vcenter/vm/{vm}/snapshots[/{snapshot}]` | Create, revert (`?action=revert`), delete |
| POST | `/api/vcenter/vm/{vm}/clone` \| `/relocate` | Task-returning |
| GET/POST | `/api/vcenter/vm/{vm}/tools` | Guest tools status / upgrade |
| GET | `/api/vcenter/vm/{vm}/guest/identity` \| `/networking` | Guest OS name, synthetic IP |
| GET/POST | `/api/vcenter/vm/{vm}/guest/power` | Guest-level power ops |
| POST | `/api/vcenter/vm/{vm}/guest/customization` | Sysprep/cloud-init-style customization spec |
| POST | `/api/vcenter/vm/{vm}/console/tickets` | Console (VNC/WebMKS-style) ticket |
| GET/PUT/DELETE | `/api/vcenter/vm/{vm}/guest/filesystem` | Lab virtual guest filesystem (Ansible/Terraform write-a-file flows) |
| GET | `/api/vcenter/vm/{vm}/guest/filesystem/files` \| `/guest/local-filesystem` | Listing |
## Highlights
- Every VM row carries a realistic device shape: `nics`, `disks`, `cdroms`,
`floppies`, `serials`, `scsi_adapters`, `boot`/`boot_devices`, `identity`
(`instance_uuid`, `bios_uuid`), and a synthetic `guest_ip` /
`guest_filesystems` map — the same fields power both the REST hardware
endpoints and SOAP `VirtualMachineConfigInfo`.
- Create requires `VirtualMachine.Inventory.Create`; delete requires
`VirtualMachine.Inventory.Delete` **and** the VM must be `POWERED_OFF`.
- Power/clone/snapshot/relocate/customize all create a durable CIS task (see
[Tasks](tasks.md)) rather than mutating synchronously in the response body.
- Console tickets from `/api/vcenter/vm/{vm}/console/tickets` persist in
`vsphere_console_tickets` (migration `013`).
- MOIDs follow the `vm-{100+n}` convention seeded by
[`app/vsphere/profiles.py`](../../app/vsphere/profiles.py).
See [Storage](storage.md) for datastore/disk-file semantics and
[SOAP / VIM](soap.md) for the equivalent `CreateVM_Task`/`PowerOnVM_Task`/…
operations used by pyvmomi, govmomi, Terraform, and Pulumi.
+23
View File
@@ -0,0 +1,23 @@
**Language / Язык:** [English](ansible.md) | [Русский](../ru/examples/ansible.md)
# Ansible
The playbook uses the `uri` module against the HTTPS gateway
(`https://localhost`), with Basic-auth session login followed by
`vmware-api-session-id`-header calls for the rest of the lifecycle.
```bash
cd examples/ansible
ansible-playbook -i inventory.ini vsphere_playbook.yml
```
[`vsphere_playbook.yml`](../../examples/ansible/vsphere_playbook.yml) covers:
session login, list VMs, create, power on, poll the CIS task
(`/api/cis/tasks/{task}`), write a file to the lab guest virtual filesystem,
power off, delete, and session logout.
Reseed the simulator (`make seed`) before relying on fixed VM names/MOIDs
from a previous run.
For the official `pulumi-vsphere` lab suite (nonempty exports, HTML report),
see [`pulumi-tests/`](../../pulumi-tests/README.md) or `make pulumi-tests`.
+21
View File
@@ -0,0 +1,21 @@
**Language / Язык:** [English](go.md) | [Русский](../ru/examples/go.md)
# Go
Uses the Go standard library (`net/http`) against
`https://localhost` with a Basic-auth session
(`vmware-api-session-id`).
```bash
cd examples/go
go run .
```
Override defaults with `VSPHERE_BASE`, `VSPHERE_USER`, `VSPHERE_PASSWORD`,
`VSPHERE_VM_NAME`. See [`main.go`](../../examples/go/main.go) for the
session → list → create → power → wait-task → delete flow and the
`waitTask` helper that polls `GET /api/cis/tasks/{task}`.
TLS verification is disabled in the HTTP client for the local self-signed
development gateway certificate only — do not reuse that transport against a
real vCenter.
+22
View File
@@ -0,0 +1,22 @@
**Language / Язык:** [English](java.md) | [Русский](../ru/examples/java.md)
# Java
Java 11+ `HttpClient` cookbook using a Basic-auth session
(`vmware-api-session-id`) against `https://localhost`. No third-party
JSON library — responses are inspected with a small string-based field
extractor suitable for a lab smoke.
```bash
cd examples/java
javac Cookbook.java && java Cookbook
```
Override defaults with `VSPHERE_BASE`, `VSPHERE_USER`, `VSPHERE_PASSWORD`,
`VSPHERE_VM_NAME` environment variables. See
[`Cookbook.java`](../../examples/java/Cookbook.java) for the session →
create → power → wait-task → delete flow.
The client installs a trust-all `SSLContext` for the local self-signed
development gateway certificate only — do not reuse it against a real
vCenter.
+53
View File
@@ -0,0 +1,53 @@
**Language / Язык:** [English](overview.md) | [Русский](../ru/examples/overview.md)
# Client examples overview
## Bring-up checklist
```bash
make up
curl -skf https://localhost/health/ready
make seed
curl -sk https://localhost/api/appliance/system/version
```
## Endpoints
| URL | When |
|---|---|
| `https://localhost` | curl, pyvmomi, govmomi, Terraform, Pulumi, Ansible, Go, Java, Perl — everything in `examples/` |
| `http://localhost` | Plain-HTTP lab face (no TLS handshake needed) |
## Auth quick reference
**Session (REST)**
```bash
SID=$(curl -sk -u 'administrator@vsphere.local:VMware1!' \
-X POST https://localhost/api/session | tr -d '"')
curl -sk -H "vmware-api-session-id: $SID" https://localhost/api/vcenter/vm
```
**SOAP Login**
```bash
python examples/python/vsphere_soap_smoke.py https://localhost
```
## Task waiting
Never treat the mutation HTTP response alone as "VM running". Power, clone,
relocate, snapshot, and OVF-deploy calls return `{ "task": "task-…" }`; poll
`GET /api/cis/tasks/{task}` until `status` is `SUCCEEDED` or `FAILED`. See
[Tasks](../domains/tasks.md).
## Reseed warning
`make seed` replaces the PostgreSQL inventory. Refresh Terraform/Pulumi/Ansible
state afterwards — see [Seed profiles](../seed-profiles.md).
## Runnable tree
See [`examples/README.md`](../../examples/README.md). The official
`pulumi-vsphere` lab suite lives under
[`pulumi-tests/`](../../pulumi-tests/README.md) (`make pulumi-tests`).
+20
View File
@@ -0,0 +1,20 @@
**Language / Язык:** [English](perl.md) | [Русский](../ru/examples/perl.md)
# Perl
`HTTP::Tiny` + `JSON` cookbook using a Basic-auth session
(`vmware-api-session-id`) against `https://localhost`.
```bash
cd examples/perl
cpanm --installdeps . # or install HTTP::Tiny, JSON, IO::Socket::SSL manually
perl cookbook.pl
```
Override defaults with `VSPHERE_BASE`, `VSPHERE_USER`, `VSPHERE_PASSWORD`,
`VSPHERE_VM_NAME` environment variables. See
[`cookbook.pl`](../../examples/perl/cookbook.pl) for the session → list →
create → power → wait-task → delete flow.
`HTTP::Tiny` is constructed with `verify_SSL => 0` for the local self-signed
development gateway certificate only.
+31
View File
@@ -0,0 +1,31 @@
**Language / Язык:** [English](pulumi.md) | [Русский](../ru/examples/pulumi.md)
# Pulumi
[`examples/pulumi/`](../../examples/pulumi/) is a Python Pulumi program that uses
the official [`pulumi-vsphere`](https://www.pulumi.com/registry/packages/vsphere/)
provider (SOAP/VIM) against the simulator — the same provider path as Terraform
`hashicorp/vsphere`.
```bash
cd examples/pulumi
pip install -r requirements.txt
pulumi plugin install resource vsphere 4.17.0
pulumi stack init dev # once
pulumi config set server localhost # or your gateway host
pulumi config set --secret password 'VMware1!'
pulumi up
```
Configuration (`pulumi config set`): `server` (default `localhost`), `user`
(default `administrator@vsphere.local`), `password` (secret), `datacenter`,
`datastore`, `cluster`, `network`, `vm_name` (default `pulumi-lab-01`).
Same reseed caution as Terraform: simulator PostgreSQL state and Pulumi state
are independent. Pin the catalog major for reproducible CI if your workflow
depends on Web UI/evidence output (see [API versions](../api-versions.md)) —
runtime routes themselves are always available regardless of the major.
For the lab suite (inventory + folder + VM + tags, nonempty output checks, HTML
report), see [`pulumi-tests/`](../../pulumi-tests/README.md) or run
`make pulumi-tests` from the repo root.
+33
View File
@@ -0,0 +1,33 @@
**Language / Язык:** [English](python-requests.md) | [Русский](../ru/examples/python-requests.md)
# Python — REST (requests / stdlib)
Raw HTTP against the vSphere REST gateway, no vendor SDK required.
```bash
pip install -r examples/python/requirements.txt
python examples/python/requests_cookbook.py
```
[`requests_cookbook.py`](../../examples/python/requests_cookbook.py)
demonstrates the shared session → create → wait-for-task → power on → wait →
power off → delete flow using `requests`, with the session id carried as the
`vmware-api-session-id` header.
For a dependency-free variant using only the standard library (`urllib`),
see [`vsphere_rest_smoke.py`](../../examples/python/vsphere_rest_smoke.py):
```bash
python examples/python/vsphere_rest_smoke.py https://localhost
```
For a combined REST-create + SOAP-`CreateVM_Task` + guest-filesystem smoke,
see [`vsphere_lifecycle.py`](../../examples/python/vsphere_lifecycle.py):
```bash
VSPHERE_BASE=https://localhost python examples/python/vsphere_lifecycle.py
```
All three scripts default to `administrator@vsphere.local` / `VMware1!` and
disable TLS verification for the local self-signed development gateway
certificate only.
+32
View File
@@ -0,0 +1,32 @@
**Language / Язык:** [English](terraform.md) | [Русский](../ru/examples/terraform.md)
# Terraform
[`examples/terraform/vsphere/`](../../examples/terraform/vsphere/) uses the
official `hashicorp/vsphere` provider (SOAP `/sdk` under the hood) pointed at
the local HTTPS gateway (`https://localhost`) with
`allow_unverified_ssl = true` for the development certificate.
```bash
cd examples/terraform/vsphere
terraform init
TF_VAR_create_lab_vm=false terraform plan # data sources only (datacenter/cluster/datastore/network/VM)
TF_VAR_create_lab_vm=true terraform apply # also creates a lab VM (SOAP CreateVM_Task)
```
Defaults (`variables.tf`): `vsphere_server = "localhost"`,
`vsphere_user = "administrator@vsphere.local"`,
`vsphere_password = "VMware1!"`, `datacenter = "Datacenter"`,
`cluster = "Cluster"`, `datastore = "datastore1"`,
`network = "VM Network"`, `vm_name = "web-01"` (a `small`/`large` seeded VM).
Provider plugin versions move quickly — pin versions in a `required_providers`
block to what you have tested. After `make seed`, refresh or recreate state
so VM name/MOID assumptions stay aligned.
This cookbook is a starting point for lab CI, not a certification of every
`hashicorp/vsphere` resource/data source against the full route registry. See
[SOAP / VIM](../domains/soap.md) for the exact operations backing the
provider's create/read paths, and
[`pulumi-tests/`](../../pulumi-tests/README.md) for the `pulumi-vsphere` lab
suite (`make pulumi-tests`).
+15
View File
@@ -0,0 +1,15 @@
**Language / Язык:** [English](troubleshooting-clients.md) | [Русский](../ru/examples/troubleshooting-clients.md)
# Troubleshooting clients
| Symptom | Fix |
|---|---|
| TLS certificate errors | Use `:443` with `verify=False` / `insecure`/`allow_unverified_ssl=true` **only** locally, or use plain HTTP `:80` |
| 401 on first call | Send `Authorization: Basic …` only to `/api/session` (or SOAP `Login`); every other call needs `vmware-api-session-id` |
| 403 on power/create | You may be using `readonly@vsphere.local` — switch to `administrator@vsphere.local` or `operator@vsphere.local` |
| VM not found | `small` seed VM names are `web-01`, `web-02`, `db-01`, `app-01`, `jumpbox` — not Proxmox-style numeric VMIDs |
| Create returns a MOID, not a task | REST `POST /api/vcenter/vm` returns the new VM's MOID synchronously; only **power/clone/relocate/snapshot/OVF-deploy** return `{ "task": "…" }` |
| Provider create vs task | Poll `/api/cis/tasks/{task}`; many providers (Terraform, Pulumi) already wait internally — raw HTTP/Go/Java/Perl clients often forget to |
| Drift after reseed | Refresh/recreate Terraform/Pulumi/Ansible state after `make seed` |
| Session expired mid-run | Sessions have a 2-hour sliding TTL; re-login if a long-running script idles past that |
| SOAP `Login` fails | Confirm the envelope targets `/sdk` with `SOAPAction` set (empty string is fine) and `Content-Type: text/xml` |
+57
View File
@@ -0,0 +1,57 @@
**Language / Язык:** [English](faq.md) | [Русский](ru/faq.md)
# FAQ
## Is this a real vCenter / ESXi?
No. It is an API and state simulator. Hosts, VMs, datastores, and networks
are durable PostgreSQL models, not ESXi hosts or KVM/vmkernel processes.
## Do you really cover the vSphere Automation API?
The **runtime** always serves the full registered route table (1077 routes:
104 deep handlers + a DB-backed stub surface for the rest) — see
[API surface](api-surface.md). The **catalog** majors 68 are intentionally
low-coverage historical floors (2.9%9.6%); only major 9 (8.0 U2 / Automation
9.1 surface) is declared 100% in the catalog. See
[API versions](api-versions.md) and [Compatibility](compatibility.md).
## Can I use this in CI for Terraform / Ansible / pyvmomi / custom clients?
Yes. That is a primary use case. Seed a profile and point clients at the
HTTPS gateway `:443` (REST `/api`/`/rest` or SOAP `/sdk`). See
[Clients](clients.md).
## Why do some NSX / Supervisor / vSAN / SAML calls "succeed" without remotes?
Those areas persist **local, seeded** simulator state (see the "Platform
surfaces" table in [API coverage](api-coverage.md)). They intentionally do
not call a real NSX Manager, Tanzu Supervisor, or IdP.
## Does registry coverage mean perfect vSphere parity?
It means every registered route has a durable handler or DB-backed stub and
is subject to the project's verification suites. Exact edge-case parity with
a physical ESXi cluster can still differ; use `/ui/api/compatibility` and
your own client tests for certification claims.
## Where is the Web UI?
[https://localhost/](https://localhost/) after `make up` (gateway).
## Can I deploy on Kubernetes?
Yes. Use the Helm chart under `helm/vmware-api-simulator` with the published
Hub image. Ingress + cert-manager Let's Encrypt is supported — see
[Kubernetes / Helm](kubernetes.md).
## What is `ENABLE_PVE_STUB`?
An optional, off-by-default legacy Proxmox VE `/api2/*` stub plane inherited
from a shared platform lineage. Native vSphere REST/SOAP is always on and is
the primary surface of this project regardless of this flag.
## Which VMs does the `small` seed use?
`web-01`, `web-02`, `db-01`, `app-01`, `jumpbox` — see
[Seed profiles](seed-profiles.md).
+175
View File
@@ -0,0 +1,175 @@
**Language / Язык:** [English](getting-started.md) | [Русский](ru/getting-started.md)
# Getting started
Bring up a local vSphere laboratory, authenticate, and exercise a first
read/mutation cycle against the simulator.
## Prerequisites
- Docker and Docker Compose
- `make` (optional but used by the documented commands)
Python, linters, and tests run **inside** containers. You do not need a local
Python toolchain for day-to-day use.
## Choose a path
| Path | When to use |
|---|---|
| [Published image](#1a-published-image-docker-hub) | Fastest lab using `inecs/vmware-api-simulator` |
| [Helm / Kubernetes](kubernetes.md) | Cluster install with Ingress + Let's Encrypt |
| [Development checkout](#1b-development-checkout) | Contribute / bind-mount source |
## 1a. Published image (Docker Hub)
Uses [`docker-compose.release.yml`](../docker-compose.release.yml) — PostgreSQL +
runtime simulator + HTTPS gateway from Hub. No source build required, but you
**must** run Compose from a checkout of this repository so `docker/gateway/` and
`docker/tls/` bind-mounts resolve. Seed runs automatically after the simulator
is healthy.
```bash
# from a git checkout of this repository (needs docker/gateway + docker/tls)
docker compose -f docker-compose.release.yml pull
docker compose -f docker-compose.release.yml up -d --wait
```
Pin a version:
```bash
IMAGE_TAG=0.1.0 docker compose -f docker-compose.release.yml up -d --wait
```
Make helpers (git checkout):
```bash
make release-up
# optional re-seed: make release-seed PROFILE=small
```
| Host port | Service |
|---|---|
| `443` | HTTPS gateway (primary vCenter entry) |
| `80` | HTTP lab face |
| `5434` | PostgreSQL (localhost only) |
Migrations run automatically via the `migrate` one-shot service.
Then continue from [Wait until ready](#2-wait-until-ready).
## 1b. Development checkout
```bash
make install
make up
```
Services (see [Ports](ports.md) for the full picture):
| Host port | Service |
|---|---|
| `443` | HTTPS gateway (nginx) → simulator |
| `80` | HTTP lab face |
| `5434` | PostgreSQL (localhost only) |
Migrations apply automatically before the simulator becomes ready. The
internal FastAPI process listens on `8080` and is not published to the host.
## 2. Wait until ready
```bash
curl -sk https://localhost/health/live
curl -sk https://localhost/health/ready
```
`/health/ready` returns HTTP 503 until PostgreSQL is reachable **and** the
latest packaged migration is applied.
## 3. Seed a profile
```bash
make seed # default: large — 10 hosts / 1000 VMs
VSPHERE_PROFILE=small make seed
```
`small` creates a 3-host cluster with five named VMs (`web-01`, `web-02`,
`db-01`, `app-01`, `jumpbox`), datastores, a standard portgroup, and the four
lab principals. See [Seed profiles](seed-profiles.md) for other sizes.
## 4. Check the API version
```bash
curl -sk https://localhost/api/appliance/system/version | jq .
```
The cold-start catalog major defaults to **9** (vSphere 8.0 U2 / Automation
9.1 surface) in Docker Compose. Browse or hot-swap majors 69 from the Web UI
or [API versions](api-versions.md).
## 5. Authenticate
```bash
SID=$(curl -sk -u 'administrator@vsphere.local:VMware1!' \
-X POST https://localhost/api/session | tr -d '"')
echo "$SID"
```
`SID` is the `vmware-api-session-id`. Send it on every subsequent call as a
header (or rely on the cookie the login response also sets):
```bash
curl -sk -H "vmware-api-session-id: $SID" https://localhost/api/vcenter/vm
```
Details: [Authentication](authentication.md).
## 6. List VMs and power one on
```bash
curl -sk -H "vmware-api-session-id: $SID" \
https://localhost/api/vcenter/vm | jq .
curl -sk -X POST -H "vmware-api-session-id: $SID" \
"https://localhost/api/vcenter/vm/vm-104/power?action=start" | jq .
```
Power actions and other long-running operations return a CIS task id.
Poll until the task finishes:
```bash
curl -sk -H "vmware-api-session-id: $SID" \
"https://localhost/api/cis/tasks/${TASK_ID}" | jq .
```
## 7. Open the Web UI
Visit [https://localhost/](https://localhost/) for the interactive
console, endpoint catalog (vSphere majors 69), compatibility view, runtime
contract apply, and demo-cluster controls. See [Web UI](web-ui.md) for
light/dark theme screenshots and the full feature list.
## 8. Try a client library
```bash
# from the repository root after make up + seed
python examples/python/vsphere_rest_smoke.py https://localhost
python examples/python/vsphere_soap_smoke.py https://localhost
```
More stacks: [Clients](clients.md) and [`examples/`](../examples/README.md).
## You're done when…
- `/health/ready` returns `{"status": "ok"}` (or equivalent OK body)
- `/api/appliance/system/version` reports the active catalog major's version
- Session login succeeds for `administrator@vsphere.local`
- `/api/vcenter/vm` lists the seeded VMs
- A power action returns a task id that reaches `SUCCEEDED`
## Next steps
- [Configuration](configuration.md) — env vars, workers, seed sizing
- [API versions](api-versions.md) — hot-swap catalog majors 69
- [Clients](clients.md) — Python, Ansible, Terraform, Pulumi
- [Operations](operations.md) — reseed, migrate, upgrades
Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

+162
View File
@@ -0,0 +1,162 @@
**Language / Язык:** [English](kubernetes.md) | [Русский](ru/kubernetes.md)
# Kubernetes / Helm
Deploy the published Docker Hub runtime image with the chart in
[`helm/vmware-api-simulator`](../helm/vmware-api-simulator).
Image: [`inecs/vmware-api-simulator`](https://hub.docker.com/r/inecs/vmware-api-simulator)
## Prerequisites
- Kubernetes 1.27+ (or comparable)
- Helm 3.14+
- [Ingress NGINX](https://kubernetes.github.io/ingress-nginx/) (or another
IngressClass that supports HTTP-01)
- [cert-manager](https://cert-manager.io/) installed cluster-wide
Example cert-manager install:
```bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml
```
## Quick install (Hub release + Ingress + Let's Encrypt)
From a git checkout of this repository:
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
-f ./helm/vmware-api-simulator/values-ingress-example.yaml \
--set certManager.email=you@example.com \
--set ingress.hosts[0].host=vmware-sim.example.com \
--set ingress.tls[0].hosts[0]=vmware-sim.example.com \
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
--set postgresql.auth.password="$(openssl rand -hex 16)"
```
What this does:
1. Pulls `inecs/vmware-api-simulator:0.1.0` (see `image.tag` in the example file).
2. Installs bundled PostgreSQL 17 (`postgres:17.5-bookworm`, same as Compose).
3. Runs schema migrations in an init container (idempotent).
4. Seeds the `small` lab profile (`seed.enabled=true`).
5. Creates `ClusterIssuer` resources:
- `letsencrypt-prod`
- `letsencrypt-staging`
6. Creates an Ingress with
`cert-manager.io/cluster-issuer: letsencrypt-prod` and a TLS secret
`vmware-api-simulator-tls`.
DNS for `vmware-sim.example.com` must point at your Ingress controller. Then:
```bash
kubectl -n vmware-sim get certificate,ingress,pods
# wait until Certificate READY=True
curl -sS https://vmware-sim.example.com/health/ready
open https://vmware-sim.example.com/
```
Default seeded login: `administrator@vsphere.local` / `VMware1!`.
### Staging first (recommended)
Validate HTTP-01 without hitting production rate limits:
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
-f ./helm/vmware-api-simulator/values-ingress-example.yaml \
--set certManager.email=you@example.com \
--set certManager.useStaging=true \
--set ingress.hosts[0].host=vmware-sim.example.com \
--set ingress.tls[0].hosts[0]=vmware-sim.example.com \
--set secret.ticketSigningKey="$(openssl rand -hex 32)"
```
Browsers will not trust the staging CA — use `curl -k` while testing. Flip
`certManager.useStaging=false` and recreate the Certificate/TLS secret for
production.
## Minimal install (ClusterIP + port-forward)
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
--set seed.enabled=true
kubectl -n vmware-sim port-forward svc/vmware-sim-vmware-api-simulator 8080:8080
```
Open http://127.0.0.1:8080/. The Service exposes the internal application
port (`8080`, see [Ports](ports.md)) — the chart does not run the nginx
TLS gateway used by Compose; put TLS in front of it via Ingress in
production, or talk to the plain-HTTP Service for local testing.
## External PostgreSQL
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
--set postgresql.enabled=false \
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
--set secret.databaseUrl='postgresql://user:pass@pg.example.com:5432/vmware_simulator'
```
Or use `secret.existingSecret` with keys `DATABASE_URL` and `TICKET_SIGNING_KEY`.
## How TLS issuance works
When `certManager.enabled=true` and `certManager.createClusterIssuer=true`, the
chart creates ACME `ClusterIssuer` objects that solve HTTP-01 through your
Ingress class. The Ingress template adds:
```yaml
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- secretName: vmware-api-simulator-tls
hosts: [vmware-sim.example.com]
```
cert-manager then creates a `Certificate`, completes HTTP-01, and stores the
Let's Encrypt key pair in that TLS secret. The chart does **not** install
cert-manager or the Ingress controller — only the issuers + Ingress wiring.
If ClusterIssuers already exist cluster-wide, set:
```yaml
certManager:
enabled: true
createClusterIssuer: false
issuerName: your-existing-issuer
```
## Operations
```bash
# logs
kubectl -n vmware-sim logs -l app.kubernetes.io/instance=vmware-sim -c simulator -f
# reseed
kubectl -n vmware-sim exec deploy/vmware-sim-vmware-api-simulator -- \
python -m app.simulation.seed_cli
# SEED_VSPHERE_PROFILE via: kubectl set env ... or --set seed.profile=demo-cluster and upgrade
# uninstall
helm -n vmware-sim uninstall vmware-sim
```
## Values reference
See [`helm/vmware-api-simulator/values.yaml`](../helm/vmware-api-simulator/values.yaml)
and the [chart README](../helm/vmware-api-simulator/README.md). Related docs:
- [Getting started](getting-started.md) — Compose paths
- [Operations](operations.md) — Docker Hub publish / release compose
- [Security](security.md) — lab credentials and trust boundary
- [Ports](ports.md) — internal `8080` vs published gateway ports
+45
View File
@@ -0,0 +1,45 @@
**Language / Язык:** [English](observability.md) | [Русский](ru/observability.md)
# Observability
## Health
| Path | Meaning |
|---|---|
| `GET /health/live` | Process liveness — no dependency checks |
| `GET /health/ready` | Database reachable via `database.is_ready()`; HTTP 503 when not |
Example:
```bash
curl -sk https://localhost/health/live
curl -sk https://localhost/health/ready
```
Implementation: [`app/observability/health.py`](../app/observability/health.py).
## Request correlation
Incoming requests accept or generate an ID via `REQUEST_ID_HEADER`
(default `X-Request-ID`). Structured logs include correlation fields and
redact known secret patterns (session ids, passwords, ticket-like tokens).
## Metrics / tracing
There is **no** Prometheus `/metrics` scrape endpoint and **no** bundled
OpenTelemetry exporter in the current application. Architecture notes that
mention them describe target design, not shipping telemetry.
Do not confuse vSphere REST paths under `/api/vcenter/activity-history` or
the seeded appliance health/timesync endpoints with simulator process
telemetry — those handlers simulate vCenter appliance state inside
PostgreSQL, not this process's own metrics.
## Compatibility evidence
Operational compatibility reports:
- `/ui/api/compatibility?major=N`
Also available through the Web UI compatibility panel. See
[Compatibility](compatibility.md).
+148
View File
@@ -0,0 +1,148 @@
**Language / Язык:** [English](operations.md) | [Русский](ru/operations.md)
# Operations
## Day-2 commands
```bash
make up # start stack
make down # stop stack
make restart
make logs
make dev # foreground reload-oriented workflow
make db-migrate # idempotent migrations
make seed # atomic reseed (SEED_VSPHERE_PROFILE=large by default)
make shell # interactive tools container
```
## Migrations
Ordered SQL files apply transactionally and record SHA-256 checksums.
Re-running `make db-migrate` is safe. Altering an already-applied migration
is rejected. `/health/ready` stays unavailable until the latest packaged
migration is present.
## Reseed
```bash
make seed # large (default)
VSPHERE_PROFILE=small make seed
VSPHERE_PROFILE=demo-cluster make seed
```
Reseed replaces the PostgreSQL inventory atomically. External automation
state (Terraform state files, Pulumi stacks, Ansible inventories that encode
VM MOIDs/names) may then drift — refresh or recreate those side channels.
See [Seed profiles](seed-profiles.md).
## Worker recovery
CIS task workers use PostgreSQL leases (`FOR UPDATE SKIP LOCKED`). After a
crash or restart, expired leases are reclaimed and incomplete work can resume
safely. Tunables: `TASK_WORKER_CONCURRENCY`, `TASK_LEASE_SECONDS`,
`SIMULATION_TIME_SCALE`.
## Changing the default catalog major
1. The default catalog major is **9** (8.0 U2 / Automation 9.1 surface) at
cold start; this does not gate the runtime route table (see
[API versions](api-versions.md)).
2. Use the Web UI "Apply as runtime" or
`POST /ui/api/contract/apply?major=N` to switch the catalog major
process-locally for browse/evidence purposes.
## Backing up lab state
PostgreSQL is the system of record. Use normal Postgres backup/restore
(`pg_dump` / volume snapshots) if you need to preserve a seeded laboratory.
Application containers are disposable when the database volume remains.
## Publishing to Docker Hub
`make release` builds the **runtime** image (the `runtime` build target — not
the local bind-mounted `dev` image) and pushes it to Docker Hub:
```bash
docker login # once; account must own or can push to DOCKERHUB_USER
make release
```
Defaults:
| Variable | Default | Meaning |
|---|---|---|
| `DOCKERHUB_USER` | `inecs` | Docker Hub namespace/org |
| `IMAGE_NAME` | `vmware-api-simulator` | Repository name |
| `VERSION` | from `pyproject.toml` | Image tag |
| `PUSH_LATEST` | `1` | Also tag/push `:latest` |
Examples:
```bash
make release
make release VERSION=0.2.0
make release DOCKERHUB_USER=myorg PUSH_LATEST=0
make release-build # build/tag locally without pushing
```
Published tags:
- `inecs/vmware-api-simulator:<version>`
- `inecs/vmware-api-simulator:latest` (unless `PUSH_LATEST=0`)
## Quick start with the published compose file
[`docker-compose.release.yml`](../docker-compose.release.yml) pulls the Hub
runtime image and starts PostgreSQL + migrate + simulator + the HTTPS
gateway:
```bash
docker compose -f docker-compose.release.yml up -d
docker compose -f docker-compose.release.yml run --rm --entrypoint python \
simulator -m app.simulation.seed_cli
curl -sk https://localhost/health/ready
open https://localhost/
```
Helpers from a git checkout:
```bash
make release-up
make release-seed PROFILE=small
make release-down
```
Useful overrides:
| Variable | Default | Meaning |
|---|---|---|
| `DOCKER_IMAGE` | `inecs/vmware-api-simulator` | Image repository |
| `IMAGE_TAG` | `latest` | Tag to pull |
| `HTTP_PORT` | `80` | Host HTTP port |
| `HTTPS_PORT` | `443` | Host HTTPS port |
| `POSTGRES_PORT` | `127.0.0.1:5434` | Host Postgres bind |
| `TICKET_SIGNING_KEY` | lab default | Change outside toy labs |
| `POSTGRES_PASSWORD` | `vmware` | DB password |
For Kubernetes with public TLS (cert-manager / Let's Encrypt), use the Helm
chart — see [Kubernetes / Helm](kubernetes.md).
## Upgrades
1. Pull / rebuild images (`make install` / `make docker-build` as appropriate).
2. Run migrations (`make db-migrate`).
3. Confirm `/health/ready`.
4. Re-check `/ui/api/compatibility?major=9` and `/api/appliance/system/version`.
5. Re-run `make test-vsphere` / `make vsphere-matrix` if you validate the
surface after upgrading.
## Resetting a lab
```bash
make seed PROFILE=small
# or via UI: unload demo → small, then seed again
```
For a hard database reset use `make db-reset` (destructive — see Makefile
help).
+49
View File
@@ -0,0 +1,49 @@
**Language / Язык:** [English](ports.md) | [Русский](ru/ports.md)
# vCenter ports in this simulator
Reference: [vSphere Networking Ports](https://ports.esp.vmware.com/) (vCenter Server).
The `api-gateway` (nginx) publishes the **primary vCenter HTTPS listener**
plus an HTTP lab face. Every published port proxies to the same FastAPI
process, which already path-routes REST (`/api`, `/rest`) and SOAP (`/sdk`)
internally — there is no separate port per protocol. The gateway also sets
`X-VMware-Service` / `X-Forwarded-Port` so clients and future routers can tell
which port was used.
## Published by Compose (`api-gateway`)
| Service | Container port | Host port (dev compose) |
|---|---:|---:|
| HTTP lab face | 80 | 80 |
| vCenter HTTPS (primary UI/API entry) | 443 | 443 |
Host ports match real vCenter defaults so remote clients can use
`https://<host>/` and `http://<host>/` without a non-standard port.
Override on release compose with `HTTP_PORT` / `HTTPS_PORT` if needed.
Also published by Compose (not via the gateway):
| Service | Host port (dev compose) |
|---|---:|
| PostgreSQL | `5434` (localhost only) |
Internal simulator process (not published to the host): `8080`.
## Path layout on HTTPS
| Surface | Path prefix | Status |
|---|---|---|
| vSphere REST | `/api/…`, `/rest/…` | implemented (core inventory + session) |
| SOAP / VIM SDK | `/sdk` | implemented (RetrieveServiceContent / Login / RetrieveProperties subset) |
| HttpNfcLease / NFC | `/nfc/…` | lab transfer handshake on the same HTTPS listener |
| Lab console | `/` | yes |
| Health | `/health/live`, `/health/ready` | yes |
## Documented but not published yet
| Service | Ports |
|---|---|
| VAMI / appliance management | 5480 |
| ESXi host management (if simulated later) | 443 (separate host) |
| Syslog / etc. | various |
+32
View File
@@ -0,0 +1,32 @@
**Language / Язык:** [English](../README.md) | [Русский](README.md)
# Документация
Руководства по симулятору VMware vSphere API. Переключайте язык с помощью заголовка на
каждой странице. Русские версии находятся в каталоге [`ru/`](README.md).
| Руководство | Описание |
|---|---|
| [Быстрый старт](getting-started.md) | Первая успешная лабораторная сессия |
| [Конфигурация](configuration.md) | Переменные окружения и Compose |
| [Аутентификация](authentication.md) | Сессии, `vmware-api-session-id`, привилегии |
| [Версии API](api-versions.md) | Catalog majors 69 и hot-swap |
| [Поверхность API](api-surface.md) | Маршрутизация REST/SOAP, coverage registry, stubs |
| [Покрытие API](api-coverage.md) | Broadcom universe vs реализованная поверхность |
| [Клиенты и примеры](clients.md) | Python, Go, Java, Perl, Ansible, Terraform, Pulumi |
| [Профили seed](seed-profiles.md) | Детерминированные фикстуры инвентаря |
| [Домены](domains/README.md) | Session, inventory, VM, storage, networking, tagging, SOAP, tasks, … |
| [Web UI](web-ui.md) | Интерактивная консоль и каталоги |
| [Эксплуатация](operations.md) | Reseed, migrate, release, upgrade |
| [Kubernetes / Helm](kubernetes.md) | Образ Hub + Ingress + Let's Encrypt |
| [Безопасность](security.md) | Модель угроз лаборатории и учётные данные |
| [Наблюдаемость](observability.md) | Эндпоинты health и логирование |
| [Порты](ports.md) | Опубликованные порты хоста и внутренние сервисы |
| [Устранение неполадок](troubleshooting.md) | Типичные сбои |
| [FAQ](faq.md) | Краткие ответы |
| [Архитектура](architecture.md) | Границы компонентов |
| [Совместимость](compatibility.md) | Модель evidence и матрица релизов |
Исполняемые cookbook'и: [`examples/`](../../examples/README.ru.md).
Интеграционные наборы: [`pulumi-tests/`](../../pulumi-tests/README.ru.md)
(`make pulumi-tests`).
+167
View File
@@ -0,0 +1,167 @@
**Language / Язык:** [English](../api-coverage.md) | [Русский](api-coverage.md)
# Матрица покрытия vSphere API
Реестр, ориентированный на автоматизацию: [`app/vsphere/rest/coverage.py`](../../app/vsphere/rest/coverage.py).
Стабы universe от Broadcom: [`app/vsphere/rest/universe.json`](../../app/vsphere/rest/universe.json) (из публичного индекса операций).
Уровни по мажорам + бандлы стаб-OpenAPI: [`app/vsphere/contracts/matrix.py`](../../app/vsphere/contracts/matrix.py) → `contracts/vsphere/<version>/manifest.json`.
## Broadcom в сравнении с этим симулятором
Публичный источник (собран скрапингом): [Индекс операций vSphere Automation API (9.1 Latest)](https://developer.broadcom.com/xapis/vsphere-automation-api/latest/operation-index/)
| Поверхность | Количество | Примечания |
|---|---:|---|
| Индекс операций Broadcom | **1348** | GET 628 / POST 422 / DELETE 114 / PUT 93 / PATCH 91 |
| Сгенерированные уникальные маршруты `verb + path` | **~1037** | Один и тот же HTTP-путь может обслуживать несколько именованных операций (`?action=…`, `$Task`) |
| Реестр симулятора (core + стабы + `/rest`) | **1077** | Глубокие core-обработчики перезаписывают записи стабов на том же пути |
| Глубокие core-обработчики | **104** | Поведение seeded-инвентаря / жизненного цикла / authz |
| Строки поверхности, поддерживаемые БД (`vsphere_api_state`) | **~540+** | Загружаются seed для каждого GET-маршрута `/api` + лабораторные дополнения |
Регенерируйте universe после обновления дампа индекса:
```bash
python scripts/generate_vsphere_universe.py
make vsphere-bundles
```
Обновление живой статистики / регенерация артефактов:
```bash
curl -sk https://localhost/ui/api/compatibility?major=9
make vsphere-surface
python scripts/write_vsphere_bundles.py
python scripts/write_vsphere_evidence.py
```
| Мажор | Метка | Реализовано / universe | Покрытие | Примечания |
|---|---|---:|---:|---|
| 6 | vSphere 7.0 | 31 / 1077 | 2.9% | Только floor каталога/evidence |
| 7 | vSphere 7.0 U3 | 77 / 1077 | 7.2% | Только floor каталога/evidence |
| 8 | vSphere 8.0 | 103 / 1077 | 9.6% | Только floor каталога/evidence |
| 9 | vSphere 8.0 U2 / поверхность Automation 9.1 | **1077 / 1077** | **100%** | Глубокие обработчики + DB-backed поверхность Broadcom |
Числа берутся из `GET /ui/api/compatibility?major=N` и
`evidence/vsphere-*.json` (`make vsphere-bundles`).
Hot-swap (`POST /ui/api/contract/apply?major=N`) меняет **catalog** major для
Web UI / evidence-отчётов. **Runtime всегда обслуживает полную
зарегистрированную поверхность** — известные пути не получают HTTP 501 из-за
version floor.
## Плоскости
| Плоскость | По умолчанию | Примечания |
|---|---|---|
| Native REST `/api`, `/rest` | включена | Основная лабораторная поверхность |
| Native SOAP `/sdk` | включена | Подмножество PropertyCollector + задачи ВМ |
| Стаб Proxmox `/api2/*` | **выключена** (`ENABLE_PVE_STUB=false`) | Опциональный legacy |
## Auth и синтетические данные
| Пункт | Детали |
|---|---|
| Пользователи | `administrator`, `readonly`, `operator`, `vmadmin` `@vsphere.local` / `VMware1!` |
| AuthZ | Проверка привилегий по роли на мутирующих эндпоинтах (403 `unauthorized`) |
| Seed `large` | 10 хостов, **1000 ВМ**, 4 datastore, DVS, папки, права |
| Seed `demo-cluster` | 20 хостов, 1000 ВМ (загрузка demo в UI) |
| Seed `small` | 3 хоста, 5 именованных ВМ (тесты) |
## Домены REST
### Глубокие (core) на мажоре 9
- Сессия / задачи CIS / роли+права AuthZ / провайдеры идентичности / стаб TLS-сертификата
- Список/получение/создание/удаление/power ВМ, оборудование, снапшоты,
клонирование, relocate, tools, идентичность/сети/питание/customization
гостя, консольные тикеты, template/unregister
- Список/получение хостов + maintenance + storage-device + сети
- Список/получение datastore + метаданные файлов
- Список сетей + создание DVS/DVPG
- CRUD для datacenter / cluster / folder (+ дети) / resource-pool
- Тегирование, content library + OVF, политики хранения (+ привязки к ВМ), привилегии
- Версия/health/сети/timesync appliance
- Стаб списка сервисов метамодели `vapi`
### DB-backed поверхность Automation (catch-all universe Broadcom)
Оставшиеся маршруты Automation API из индекса операций 9.1 зарегистрированы
и обслуживаются [`app/vsphere/rest/stub_surface.py`](../../app/vsphere/rest/stub_surface.py) против PostgreSQL:
- таблица `vsphere_api_state` (миграция `011_vsphere_api_state.sql`)
- seed через `seed_api_surface()` при каждом профиле, включая
**`demo-cluster`** / UI `POST /ui/api/demo/load`
- overlay инвентаря для оборудования ВМ (cdrom/scsi/boot/…), сетей/хранения
хоста, тегирования, content library
- PUT/PATCH сохраняются в `vsphere_api_state`; POST добавляет строки
коллекции; DELETE их удаляет
Нет маркеров `"stub": true` — зондам нужны реальные seeded-payload'ы на
мажоре 9.
## Домены SOAP (govmomi / Terraform / Pulumi / pyvmomi)
- RetrieveServiceContent (+ TaskManager / SearchIndex / GuestOperationsManager / FileManager / OvfManager)
- RetrieveProperties / RetrievePropertiesEx / **ContinueRetrievePropertiesEx** (токены пагинации; `<objects>` во множественном числе)
- PropertyCollector: цепочка предков Ancestors, однохоповый `childEntity`
ListFolder, обход ContainerView `view`
- `Folder.childType` как `ArrayOfString`; строковые свойства несут
`xsi:type="xsd:string"` (декодирование govmomi)
- `Datastore.host` как `ArrayOfDatastoreHostMount`; **environmentBrowser** у
Cluster/Host
- **QueryConfigOption** / QueryConfigOptionEx / QueryConfigOptionDescriptor / QueryConfigTarget
- CreateFilter / WaitForUpdatesEx (токены версий; пустые опросы)
- FindByInventoryPath (пути govmomi не включают корневую `Datacenters`),
FindByUuid/Dns/Ip, FindChild
- **CreateVM_Task** / CreateChildVM_Task, CreateFolder,
Power/Clone/Snapshot/Rename/Reconfig/Relocate/Destroy/Unregister/MarkAsTemplate/CustomizeVM_Task + CancelTask
- Файловые операции гостя: ListFilesInGuest,
InitiateFileTransferTo/FromGuest, DeleteFileInGuest, MakeDirectoryInGuest
- Реальные ID задач из `vsphere_tasks` (включая MoRef в `info.result` при
create/clone)
- `/sdk/vimService.wsdl`, `/sdk/about.do`, стаб `/pbm`
- Строгий по типам поиск MOR: `VirtualApp:resgroup-*` не резолвится как
обычный ResourcePool (путь CreateVM в Terraform)
## Дополнения REST для Ansible / Python-приложений
- Power ВМ возвращает `{ "task": "task-…" }` для опроса задач CIS
- Виртуальная файловая система гостя:
`/api/vcenter/vm/{vm}/guest/filesystem` (+ листинг локальной файловой
системы)
- Сессии обновления/загрузки content library для лабораторных потоков
push/pull OVF
## Legacy `/rest`
Обёртки `{ "value": … }` для
vm/host/datastore/network/datacenter/cluster/power/appliance.
## Мажоры контракта (browse в сравнении с runtime)
Hot-swap (`POST /ui/api/contract/apply?major=N`) всё ещё переключает мажор
**каталога** для просмотра/evidence в UI. **Runtime всегда обслуживает
полную зарегистрированную поверхность** глубокими обработчиками или
DB-backed стабами — известные пути никогда не получают HTTP 501 из-за
уровня версии. Уровни каталога остаются историческими только для
документации.
## Поверхности платформы (доступны в лаборатории)
Исторически они считались «отложенными»; теперь они возвращают
**непустые seeded лабораторные данные** и принимают базовые мутации:
| Область | REST | SOAP |
|---|---|---|
| NSX (tier0 / проекты / edges / VPC / подсети) | Seeded-пути Automation под `namespace-management` / `namespaces` | — |
| Supervisor / WCP | namespace, классы ВМ, сводка/идентичность supervisor, политики инфраструктуры | — |
| vSAN | Политики хранения с `policy_type: VSAN` (+ лабораторная политика RAID1) | — |
| SAML / OIDC | `GET/POST/PATCH/DELETE /api/vcenter/identity/providers` (LocalOS + OIDC + SAML) | — |
| VECS / сертификаты | TLS, CSR TLS, доверенные цепочки корней, сертификаты/запросы подписи supervisor | — |
| HttpNfcLease | `PUT/GET /nfc/{lease}/files/...` | `ImportVApp_Task`, `CreateImportSpec`, ход/завершение lease |
| Customization гостя | GET+POST `/api/vcenter/vm/{vm}/guest/customization` | `CustomizeVM_Task` |
Это всё ещё **лабораторный** заменитель (не бинарно совместимый с NSX
Manager / не настоящее хранилище VECS / не полная матрица XML устройств
Broadcom). Perf/Event/Alarm по-прежнему отвечают, но не симулируются
глубоко.
+91
View File
@@ -0,0 +1,91 @@
**Language / Язык:** [English](../api-surface.md) | [Русский](api-surface.md)
# Поверхность API
## Путь запроса
1. Middleware назначает или пересылает ID запроса (`REQUEST_ID_HEADER`).
2. FastAPI направляет запрос в роутер vSphere REST (`/api`, `/rest`), роутер
SOAP (`/sdk`) или (если `ENABLE_PVE_STUB=true`) в опциональный legacy-стаб.
3. `/api/session` (либо `/rest/com/vmware/cis/session`, либо SOAP `Login`)
определяет принципала и выдаёт `vmware-api-session-id`.
4. Зависимости `require_read` / `require_privilege(...)` проверяют роли
сессии перед раскрытием или мутацией ресурсов.
5. Глубокий обработчик (базовая логика инвентаря/жизненного цикла/тегов/
контента/appliance) или DB-backed поверхность стабов выполняется против
состояния, хранимого в PostgreSQL.
6. Долгие операции (power, clone, relocate, snapshot, деплой OVF) создают
долговечную CIS-задачу и возвращают `{ "task": "task-…" }`.
## Две REST-поверхности в одном реестре
- **Core (глубокие) обработчики** — ~104 комбинации verb+path в
[`app/vsphere/rest/router.py`](../../app/vsphere/rest/router.py),
`vm_ext.py`, `inventory_ext.py`, `platform_rest.py`, `tagging_rest.py`,
`content_rest.py`, `appliance_ext.py`, `nfc_rest.py`, `tasks.py`. Они
читают и мутируют напрямую seeded-таблицы инвентаря/тегов/контента/
appliance.
- **DB-backed поверхность стабов** —
[`app/vsphere/rest/stub_surface.py`](../../app/vsphere/rest/stub_surface.py)
отвечает на оставшиеся маршруты индекса операций Broadcom Automation API
(зарегистрированные из `universe.json`) против `vsphere_api_state`. GET
возвращает живые payload'ы, производные от инвентаря, когда это возможно,
иначе — seeded-строки; PUT/PATCH сохраняются в `vsphere_api_state`; POST
добавляет строки коллекции; DELETE их удаляет. Маркер `"stub": true` не
возвращается — зонды видят реальные seeded-payload'ы.
Обе поверхности используют одну таблицу маршрутов; core-обработчики имеют
приоритет над записями стабов, зарегистрированными для того же verb+path.
## Legacy `/rest`
[`app/vsphere/rest/legacy.py`](../../app/vsphere/rest/legacy.py) оборачивает
чтения vm/host/datastore/network/datacenter/cluster/power/appliance (и
power ВМ) в конверты `{ "value": … }` для более старых клиентов
`com.vmware.vcenter.*`.
## Ошибки ([`app/vsphere/errors.py`](../../app/vsphere/errors.py))
| Статус | `error_type` | Типичная причина |
|---|---|---|
| 400 | `invalid_argument` / `already_exists` | Некорректное тело, дублирующееся имя |
| 401 | `unauthenticated` | Отсутствующая/недействительная/истёкшая сессия |
| 403 | `unauthorized` | У сессии нет требуемой привилегии |
| 404 | `not_found` | Неизвестный параметр MOID/path |
| 409 | (зависит от обработчика) | Недопустимый переход состояния питания, конфликт блокировки |
| 501 | `error` | Достижимо только через fallback опционального legacy-стаба для необъявленных методов |
Все тела ошибок следуют форме vSphere Automation:
`{ "error_type": "...", "messages": [{ "default_message": "...", "id": "...", "args": [] }] }`.
## Задачи
Асинхронная работа (power, clone, snapshot, relocate, деплой OVF, guest
customize) возвращает id задачи. Опрашивайте:
```text
GET /api/cis/tasks/{task}
```
Строки задач фиксируются в `vsphere_tasks`; `progress` равен `100`, как
только `status` становится `SUCCEEDED`/`FAILED`. HTTP 200/201 на запросе
мутации означает «принято», а не «ВМ уже в конечном состоянии». См.
[Задачи](domains/tasks.md).
## Исследование
- Интерактивная документация FastAPI: `/docs`
- Инспектор методов в Web UI: `/` → каталог → метод
- Вспомогательные API UI: `/ui/api/catalog`, `/ui/api/method`,
`/ui/api/compatibility`
- Реестр покрытия: [`app/vsphere/rest/coverage.py`](../../app/vsphere/rest/coverage.py)
- Матрица уровней пути / каталога: [`app/vsphere/contracts/matrix.py`](../../app/vsphere/contracts/matrix.py)
## Эндпоинты совместимости
| Path | Формат |
|---|---|
| `/ui/api/compatibility?major=N` | JSON |
См. [Совместимость](compatibility.md) и [Покрытие API](api-coverage.md) для
полной разбивки Broadcom-universe в сравнении с реализованным.
+77
View File
@@ -0,0 +1,77 @@
**Language / Язык:** [English](../api-versions.md) | [Русский](api-versions.md)
# Версии API (vSphere catalog majors 69)
Web UI и evidence/compatibility отчёты просматривают четыре целочисленных **catalog
majors**, которые сопоставляются с label floors vSphere Automation API:
| Major | Метка vSphere | Строка версии contract |
|---|---|---|
| 6 | 7.0 | `7.0.0` |
| 7 | 7.0 U3 | `7.0.3` |
| 8 | 8.0 | `8.0.0` |
| 9 | 8.0 U2 (Automation 9.1 surface) | `8.0.2` |
Определения находятся в [`app/vsphere/contracts/matrix.py`](../../app/vsphere/contracts/matrix.py)
(`VERSIONS`, `PATH_FLOOR`). Каждый зарегистрированный REST path имеет **floor**
наименьший major, при котором он появляется в catalog — из того же
модуля. Undated paths по умолчанию получают наивысший major (9), пока не catalogued.
## Runtime vs catalog
Это самое важное различие в проекте:
- **Catalog major** — управляет тем, что показывает Web UI endpoint tree, `/ui/api/catalog`,
и compatibility/evidence отчёты для данного major.
- **Runtime surface** — симулятор всегда обслуживает **полную зарегистрированную
route table** с deep handlers или DB-backed stubs, независимо от
активного catalog major. Известный path никогда не возвращается как HTTP 501 из-за
version floor.
Hot-swap catalog major — это **documentation/browse**
переключатель, а не compatibility gate для live traffic. См.
[`available_for_request()`](../../app/vsphere/contracts/matrix.py) для точной
политики.
## Cold start
`GET /api/appliance/system/version` сообщает version string текущего
выбранного runtime source (по умолчанию `8.0.2` / major 9, если процесс не
переопределяет `app.state.runtime_source_version`).
## Hot-swap (catalog browse)
Просматривайте любой major в Web UI catalog или вызывайте:
```http
POST /ui/api/contract/apply?major=7
```
Эффекты:
- Web UI catalog, `/ui/api/compatibility` и evidence отчёты переключаются на
floor major 7 и ledger (`evidence/vsphere-7.0.3.json`).
- Изменение **process-local** и **не сохраняется**; restart возвращает
default (major 9).
- Зарегистрированные REST/SOAP routes продолжают отвечать своими реальными
handlers независимо от применённого major.
### Рекомендации для клиентов
- Большинству клиентов (pyvmomi, govmomi, Terraform, Pulumi, Ansible `uri`) не
нужно pin'ить catalog major — runtime surface не меняет форму
на его основе.
- Используйте catalog majors, когда нужно, чтобы Web UI / evidence view
отражали более старую метку vSphere для документации или скриншотов.
- После apply перепроверьте `/ui/api/compatibility?major=N` для активного
catalog state.
## Регенерация catalog artifacts
```bash
make vsphere-bundles # stub OpenAPI matrices + evidence ledgers
make vsphere-universe # regenerate universe.json from the Broadcom operations index
make evidence # regenerate per-major verified surface evidence ledgers
```
См. [Поверхность API](api-surface.md) и [Совместимость](compatibility.md).
+77
View File
@@ -0,0 +1,77 @@
**Language / Язык:** [English](../architecture.md) | [Русский](architecture.md)
# Архитектура
## Цели
`vmware-api-simulator` — stateful лабораторный эмулятор vSphere (Automation REST +
VIM SOAP). Главная цель дизайна — **практическая совместимость клиентов**:
сессии, inventory, жизненный цикл VM, обходы PropertyCollector, задачи,
stubs tagging/content library и роли AuthZ реализованы поверх большого
синтетического datastore, чтобы инструменты вроде curl, govc-подобных
потоков, pyvmomi и Terraform могли прогонять типовые пути без реального
vCenter.
Catalog majors **69** соответствуют floors vSphere 7.0 / 7.0U3 / 8.0 / 8.0U2.
Hot-swap меняет каталог только для browse/evidence в Web UI — он **не**
гейтит живые маршруты. Опциональный stub Proxmox `/api2/*` остаётся за
`ENABLE_PVE_STUB` (по умолчанию выключен).
## Контекст системы
```mermaid
flowchart LR
Client["API clients<br/>pyvmomi / Terraform / govc / REST SDKs"]
Admin["Lab operator"]
UI["Web lab UI"]
API["FastAPI application"]
Gateway["HTTPS gateway :443"]
Contract["vSphere contract matrix"]
Domain["vsphere domain + inventory"]
DB[(PostgreSQL)]
Obs["Logs / Prometheus / OpenTelemetry"]
Client -->|"/api /rest /sdk"| Gateway
Gateway --> API
UI --> Gateway
Admin -->|"seed / migrate"| API
API --> Contract
API --> Domain
Domain --> DB
API --> Obs
```
## Плоскости
| Плоскость | Путь | Заметки |
|---|---|---|
| Automation REST | `/api`, `/rest` | Заголовок сессии `vmware-api-session-id` |
| VIM SOAP | `/sdk` | Подмножество PropertyCollector + VM tasks |
| Lab UI helpers | `/ui/api/*` | Каталог, demo seed, совместимость |
| Опциональный PVE stub | `/api2/*` | Выкл., пока `ENABLE_PVE_STUB=true` |
## Модель данных
Inventory живёт в `vsphere_objects` (MOID, типы, props JSON, parent-ссылки).
Sessions, credentials, tasks, tags, libraries, snapshots и permissions —
соседние таблицы (миграции `009_vsphere.sql`, `010_vsphere_platform.sql`).
DB-backed Automation stubs используют `vsphere_api_state` (`011`); сессии
transfer content library и строки HttpNfcLease — в `vsphere_transfer_sessions` /
`vsphere_nfc_leases` (`012`); views/tokens PropertyCollector и console tickets —
в `vsphere_pc_state` / `vsphere_console_tickets` (`013`).
Профили seed (`small` / `large` / `demo-cluster`) строят детерминированный
кластер — по умолчанию **large** это ~10 hosts / **1000 VMs**.
## AuthZ
Credentials отображаются в roles → privilege sets. Мутирующие обработчики
используют `require_privilege(...)`; пути чтения — `require_read`. SOAP Login
выдаёт cookie, совместимый с VIM-сессиями.
## Связанные документы
- [Покрытие API](api-coverage.md)
- [Аутентификация](authentication.md)
- [Web UI](web-ui.md)
- [Клиенты](clients.md)
+101
View File
@@ -0,0 +1,101 @@
**Language / Язык:** [English](../authentication.md) | [Русский](authentication.md)
# Аутентификация
Основная плоскость: **vSphere Automation REST** sessions (`vmware-api-session-id`).
SOAP `/sdk` использует собственные `Login`/`Logout` на VIM `SessionManager`. Опциональная
legacy Proxmox stub-плоскость (`ENABLE_PVE_STUB=true`) сохраняет историческое поведение
`/api2/json/access/ticket` из общей platform lineage — это не default lab path и далее
не рассматривается.
## Session login (REST)
```http
POST /api/session
Authorization: Basic base64(user:password)
```
Успешный ответ:
- Body: JSON string session id (например, `"a1b2c3…"`)
- Header: `vmware-api-session-id: <id>`
- Cookie: `vmware-api-session-id=<id>` (`SameSite=Strict`, TTL 2 часа)
Legacy wrapper (те же credentials, форма `{ "value": "<session-id>" }`):
```http
POST /rest/com/vmware/cis/session
Authorization: Basic base64(user:password)
```
### Вызов API
```bash
SID=$(curl -sk -u 'administrator@vsphere.local:VMware1!' \
-X POST 'https://localhost/api/session' | tr -d '"')
curl -sk -H "vmware-api-session-id: $SID" \
'https://localhost/api/vcenter/vm'
```
Cookie-only клиенты также работают после login (`credentials: include` в
браузерном Web UI).
### Inspect / logout сессии
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/session` | HTTP 200 с заголовками `x-vmware-session-user` / `x-vmware-session-roles` |
| DELETE | `/api/session` | Инвалидирует сессию и очищает cookie |
| GET / DELETE | `/rest/com/vmware/cis/session` | Legacy эквиваленты `{ "value": … }` |
Сессии хранятся в PostgreSQL (`vsphere_sessions`) с 2-часовым sliding
expiry — каждый аутентифицированный запрос продлевает `expires_at`. Истёкшие сессии
возвращают HTTP 401 при следующем lookup и лениво удаляются.
## Засеянные lab principals
Пароль для всех: `VMware1!`
| Principal | Роль |
|---|---|
| `administrator@vsphere.local` | Administrator |
| `readonly@vsphere.local` | ReadOnly |
| `operator@vsphere.local` | VirtualMachinePowerUser |
| `vmadmin@vsphere.local` | VirtualMachineAdministrator |
Credentials хранятся в `vsphere_credentials` (scrypt-hashed passwords,
массив `roles`) и идемпотентно re-insert'ятся при первом вызове `/api/session`
и каждым seed profile. См. [Authorization](domains/authz.md) для модели
привилегий и [Профили seed](seed-profiles.md) для соответствия четырёх
principals inventory-scoped permissions.
Mutating endpoints проверяют привилегии через `require_privilege(...)`; вызов
mutate path как `readonly@vsphere.local` возвращает **403**.
## SOAP `/sdk`
```xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:vim25">
<soapenv:Body>
<urn:Login>
<urn:_this type="SessionManager">SessionManager</urn:_this>
<urn:userName>administrator@vsphere.local</urn:userName>
<urn:password>VMware1!</urn:password>
</urn:Login>
</soapenv:Body>
</soapenv:Envelope>
```
`Login` выдаёт тот же underlying session id, возвращаемый как
`vmware-api-session-id` и как cookie `vmware_soap_session`; последующие SOAP
вызовы (pyvmomi, govmomi, Terraform provider `hashicorp/vsphere`, Pulumi)
передают этот cookie автоматически. `Logout` удаляет сессию. См.
[SOAP / VIM](domains/soap.md).
## Опциональная legacy Proxmox stub
Только при `ENABLE_PVE_STUB=true`: ticket login на `/api2/json/access/ticket`
с `PVEAuthCookie` + CSRF, унаследованный от общей simulator platform, от которой
этот проект fork'нулся. По умолчанию выключен (`ENABLE_PVE_STUB=false`) и
не используется vSphere docs, examples или test suites в этом репозитории.
+88
View File
@@ -0,0 +1,88 @@
**Language / Язык:** [English](../clients.md) | [Русский](clients.md)
# Клиенты
Используйте симулятор из распространённых стеков автоматизации VMware:
Python, Ansible, Terraform, Pulumi.
## Матрица подключений
| Стек | Транспорт | Примечания | Код |
|---|---|---|---|
| REST (curl / SDK) | HTTPS `:443` | `vmware-api-session-id` после Basic-сессии | `examples/python/vsphere_rest_smoke.py`, `vsphere_lifecycle.py` |
| SOAP / VIM | HTTPS `:443/sdk` | провайдеры pyvmomi / govmomi / Terraform / Pulumi | `examples/python/vsphere_soap_smoke.py` |
| Legacy `/rest` | HTTPS `:443` | обёртки `{ "value": … }` | `/rest/vcenter/vm` |
| Terraform | HTTPS `:443` | источники данных `hashicorp/vsphere` + опциональный ресурс ВМ | `examples/terraform/vsphere/` |
| Ansible | HTTPS `:443` | playbook жизненного цикла REST (модуль `uri`) | `examples/ansible/vsphere_playbook.yml` |
| Pulumi | HTTPS `:443` | кулинарная книга REST ComponentResource | `examples/pulumi/` |
| govc | HTTPS `:443` | `GOVC_URL=https://…` insecure | см. ниже |
| Go / Java / Perl | HTTPS `:443` | минимальные кулинарные книги REST (сессия по Basic-auth) | `examples/go/`, `examples/java/`, `examples/perl/` |
## Учётные данные (seed)
| Пользователь | Пароль | Роль |
|---|---|---|
| `administrator@vsphere.local` | `VMware1!` | Administrator |
| `readonly@vsphere.local` | `VMware1!` | ReadOnly |
| `operator@vsphere.local` | `VMware1!` | VirtualMachinePowerUser |
| `vmadmin@vsphere.local` | `VMware1!` | VirtualMachineAdministrator |
## Seed инвентаря
```bash
make seed # large: 10 хостов / 1000 ВМ
VSPHERE_PROFILE=demo-cluster make seed
VSPHERE_PROFILE=small make seed
```
## Быстрые кулинарные книги
```bash
# Все четыре стека (в стиле Python/Ansible/Terraform/Pulumi) внутри Compose
make client-cookbooks
# Python REST + SOAP CreateVM / NFC
VSPHERE_BASE=https://localhost python examples/python/vsphere_lifecycle.py
# Ansible
ansible-playbook -i examples/ansible/inventory.ini examples/ansible/vsphere_playbook.yml
# Terraform — источники данных hashicorp/vsphere (plan) + опциональный ресурс CreateVM
cd examples/terraform/vsphere
terraform init
TF_VAR_vsphere_server=localhost TF_VAR_create_lab_vm=false terraform plan
TF_VAR_vsphere_server=localhost TF_VAR_create_lab_vm=true terraform apply
# Pulumi REST
cd examples/pulumi && pulumi up
```
Проверено против gateway (`:443`): жизненный цикл Python, playbook Ansible,
REST в стиле Pulumi и `terraform plan` (источники данных
datacenter/cluster/datastore/network/VM) — всё зелёное. SOAP
`CreateVM_Task` доступен для пути ресурса; используйте свежий seed, если
имена папок были переименованы зондами (`make seed`).
## govc (опциональный инструмент на хосте)
```bash
export GOVC_URL=https://localhost
export GOVC_USERNAME=administrator@vsphere.local
export GOVC_PASSWORD='VMware1!'
export GOVC_INSECURE=1
govc about
govc ls /
govc find / -type m | head
govc vm.info web-01
```
## Smoke-тест pyvmomi
```bash
docker compose run --rm --no-deps -e VSPHERE_BASE=http://simulator:8080 \
-e TEST_DATABASE_URL=postgresql://vmware:vmware@postgres:5432/vmware_simulator \
dev pytest tests/compatibility/test_vsphere_pyvmomi.py -q
```
Руководства по языкам: [examples/overview.md](examples/overview.md). Покрытие:
[api-coverage.md](api-coverage.md).
+87
View File
@@ -0,0 +1,87 @@
**Language / Язык:** [English](../compatibility-0.1.0.md) | [Русский](compatibility-0.1.0.md)
# Отчёт совместимости — 0.1.0
Этот отчёт фиксирует evidence для релиза симулятора 0.1.0 относительно реестра
маршрутов vSphere Automation API (catalog majors 69, основной contract major 9 /
8.0 U2). Это матрица ограничений по измерениям *качества / внешней интеграции*,
а не утверждение общей аппаратной совместимости с vCenter/ESXi.
Пользовательский обзор — [compatibility.md](compatibility.md). Живые
машиночитаемые счётчики всегда доступны из
`/ui/api/compatibility?major=N`, когда симулятор запущен.
## Сводка (major 9 / основной контракт vSphere 8.0 U2)
| Уровень | Methods | Доля universe | Evidence |
|---|---:|---:|---|
| Declared in universe (Broadcom operations index → route table) | 1077 | 100% | `app/vsphere/rest/universe.json` |
| Implemented at major 9 (catalog floor) | **1077** | **100%** | `app/vsphere/contracts/matrix.py` |
| Core deep handlers (inventory/lifecycle/tagging/content/appliance) | 104 | 9.7% | `app/vsphere/rest/coverage.py` (`CORE_IMPLEMENTED`) |
| DB-backed stub surface (остальной реестр) | ~973 | 90.3% | `app/vsphere/rest/stub_surface.py` против `vsphere_api_state` |
| Verified / observed surface ledger | **1077** | **100%** | `evidence/vsphere-8.0.2.json` |
## Покрытие по catalog major
| Major | Метка vSphere | Implemented | Universe | Coverage |
|---|---|---:|---:|---:|
| 6 | 7.0 | 31 | 1077 | 2.88% |
| 7 | 7.0 U3 | 77 | 1077 | 7.15% |
| 8 | 8.0 | 103 | 1077 | 9.56% |
| 9 | 8.0 U2 | 1077 | 1077 | 100.00% |
**Implemented** здесь — оценка catalog-floor для browse в Web UI и
evidence-отчётов, перегенерируется через `make evidence` / `make vsphere-bundles`
и защищена `tests/compatibility/test_verified_surface.py`. Она **не**
гейтит живой трафик — почему runtime всегда обслуживает зарегистрированный
маршрут независимо от применённого major, см. [Поверхность API](api-surface.md).
## Реализованная поверхность (верхний уровень)
- **Session**: `/api/session`, `/rest/com/vmware/cis/session`, SOAP
`Login`/`Logout` — всё устойчиво в PostgreSQL (`vsphere_sessions`,
`vsphere_credentials`).
- **Inventory**: list+get для VM/host/datastore/network/datacenter/cluster/folder/resource-pool,
плюс create/delete для datacenter/cluster/folder/resource-pool.
- **VM lifecycle**: create, delete, power, hardware (CPU/memory/disk/NIC/boot),
snapshots, clone, relocate, guest identity/networking/power/customization,
console tickets, tools.
- **Tasks**: `/api/cis/tasks`, реальные ids из `vsphere_tasks`, SOAP task MoRefs.
- **Tagging / content library**: categories, tags, associations, libraries,
library items, update/download sessions, OVF deploy.
- **Authorization**: privileges, roles, permissions CRUD, identity providers.
- **Appliance**: version, health, networking (hostname/DNS), timesync.
- **SOAP / VIM**: RetrieveServiceContent, PropertyCollector
(RetrieveProperties/Ex, ContinueRetrievePropertiesEx, CreateFilter,
WaitForUpdatesEx), FindBy* / FindChild, CreateVM_Task и связанные, guest
file operations, HttpNfcLease import flow, WSDL stub.
- **Platform lab surfaces**: seeded (не бинарно совместимые) stand-in'ы
NSX/Supervisor/vSAN/SAML-OIDC/VECS-cert — точный список и оговорки в
[Покрытие API](api-coverage.md).
## Принцип персистентности
Каждый путь create/update/delete пишет в PostgreSQL (таблицы и/или catch-all
`vsphere_api_state`). Секреты могут храниться, но не должны отдаваться на GET.
Пользовательские ошибки «not supported in the emulator» для зарегистрированных
путей запрещены — см. `.cursor/rules/durable-simulator.mdc`.
## Известные ограничения
| Область | Текущее поведение |
|---|---|
| Внешние системы | NSX/LDAP/SAML/OIDC/ACME не обращаются к реальным remotes; состояние симулируется локально |
| TLS | Локальный nginx gateway только с закоммиченным self-signed development key |
| Сертификация клиентов | SOAP smoke в стиле pyvmomi/govmomi + cookbook'и Ansible/Terraform/Pulumi; не формальный certification suite для каждой версии провайдера |
| Smoke провайдера | Набор `pulumi-vsphere` в `pulumi-tests/` (`make pulumi-tests`) гоняет SOAP inventory/VM/tag с проверкой непустых export'ов; семантическая глубина по-прежнему разная (deep handlers vs DB-backed stubs) |
Полное покрытие реестра на major 9 означает, что HTTP 501 «handler pending»
не должен появляться ни для одного маршрута в реестре симулятора. *Качество*
совместимости (точный паритет крайних случаев vSphere) по-прежнему углубляется
тестами и observation.
При импорте обновлённого дампа Broadcom operations index: перегенерируйте
`universe.json` (`make vsphere-universe`), bundles/evidence
(`make vsphere-bundles`, `make evidence`), запустите
`pytest tests/compatibility/test_verified_surface.py` и закоммитьте обновлённые
ledgers `evidence/vsphere-*.json`.
+79
View File
@@ -0,0 +1,79 @@
**Language / Язык:** [English](../compatibility.md) | [Русский](compatibility.md)
# Совместимость
Этот документ объясняет, как симулятор заявляет совместимость с vSphere
Automation API по мажорам каталога **69**. Когда процесс запущен,
предпочитайте живые отчёты.
## Живые отчёты
| URL | Формат |
|---|---|
| `/ui/api/compatibility?major=N` | JSON |
Web UI также предоставляет панель совместимости, управляемую этим
endpoint'ом.
## Покрытие реестра в сравнении с проверенной поверхностью
| Мажор | Метка vSphere | Реализовано / universe | Покрытие |
|---|---|---:|---:|
| 6 | 7.0 | 31 / 1077 | 2.9% |
| 7 | 7.0 U3 | 77 / 1077 | 7.2% |
| 8 | 8.0 | 103 / 1077 | 9.6% |
| 9 | 8.0 U2 (поверхность Automation 9.1) | **1077 / 1077** | **100%** |
- **Universe** — уникальные маршруты verb+path, полученные из публичного
[индекса операций vSphere Automation API](https://developer.broadcom.com/xapis/vsphere-automation-api/latest/operation-index/)
(1348 документированных операций → ~1037 уникальных маршрутов → 1077
зарегистрированных в таблице маршрутов этого симулятора, поскольку
некоторые пути обслуживают несколько именованных операций).
- **Реализовано (по мажору)** — маршруты, чей уровень каталога
(`app/vsphere/contracts/matrix.py`) равен этому мажору или ниже. Это
оценка **каталога/документации**, а не ограничение живого трафика.
- **Runtime** — независимо от применённого мажора каталога, каждый
зарегистрированный маршрут всегда обслуживается своим реальным
обработчиком (104 глубоких обработчика) или DB-backed поверхностью
стабов. См. [Поверхность API](api-surface.md).
После **Apply as runtime** (`POST /ui/api/contract/apply?major=N`) живой
отчёт загружает журнал этого мажора (`evidence/vsphere-{version}.json`), так
что панель совместимости Web UI отражает выбранный мажор.
## Измерения evidence
Журналы по мажорам в `evidence/vsphere-{version}.json` записывают счётчики
`declared`, `implemented`, `observed` и `verified`, а также разбивки по
HTTP-методам и доменам (`auth_session`, `inventory`, …). Регенерируйте с
помощью:
```bash
make evidence # app/evidence_gen.py
make vsphere-bundles # стаб-бандлы OpenAPI + журналы evidence вместе
```
Исполняемое подтверждение этих заявлений:
| Набор тестов | Роль |
|---|---|
| `tests/compatibility/test_verified_surface.py` | hot-swap + дрейф журнала + пороги оценки |
| `tests/compatibility/test_group_smoke.py` | представительные мутации групп REST с PostgreSQL |
| `tests/compatibility/test_vsphere_pyvmomi.py` | внешний smoke-тест SOAP через pyvmomi |
| `tests/integration/test_vsphere_full_api.py` | широкое интеграционное покрытие REST/SOAP |
Дополнительные cookbook'и под [`examples/`](../../examples/README.ru.md)
и lab-набор `pulumi-vsphere` под
[`pulumi-tests/`](../../pulumi-tests/README.ru.md) (`make pulumi-tests`)
выполняются вручную или опционально в CI.
## Известные поведенческие ограничения
| Область | Поведение |
|---|---|
| Внешние системы | NSX Manager, живые LDAP/SAML/OIDC IdP и ACME-директории не обращаются к реальным удалённым сервисам; только seeded/локальное состояние |
| TLS | Только локальный self-signed development-gateway (Compose); используйте свои сертификаты / cert-manager для реальных развёртываний |
| Гипервизор | Нет реального выполнения ESXi/KVM; нет бинарных загрузок NFC |
| Корпус наблюдений | Санированные данные наблюдений реального vCenter остаются ограниченными; глубокий семантический паритет проверяется путь-за-путём указанными выше наборами тестов, а не исчерпывающим сравнением с production |
Исторические заметки о релизах: [compatibility-0.1.0.md](compatibility-0.1.0.md).
+96
View File
@@ -0,0 +1,96 @@
**Language / Язык:** [English](../configuration.md) | [Русский](configuration.md)
# Конфигурация
Настройки приложения загружаются из окружения (см. `.env.example`).
Docker Compose инжектирует многие из них для сервиса `simulator`; значения,
объявленные в `environment:` в `docker-compose.yml`, переопределяют `.env` для этого
сервиса. Типизированная модель настроек находится в [`app/config.py`](../../app/config.py).
## Основное
| Переменная | По умолчанию / пример | Значение |
|---|---|---|
| `APP_HOST` | `0.0.0.0` | Адрес bind |
| `APP_PORT` | `8080` | Внутренний порт uvicorn (не публикуется; gateway публикует vCenter HTTPS) |
| `DATABASE_URL` | `postgresql://vmware:vmware@postgres:5432/vmware_simulator` | asyncpg DSN |
| `DB_POOL_MIN_SIZE` | `1` | Минимум пула |
| `DB_POOL_MAX_SIZE` | `10` | Максимум пула |
| `DB_CONNECT_TIMEOUT_SECONDS` | `10` | Таймаут подключения |
| `DB_COMMAND_TIMEOUT_SECONDS` | `30` | Таймаут команды |
| `LOG_LEVEL` | `INFO` | Уровень логирования |
| `REQUEST_ID_HEADER` | `X-Request-ID` | Заголовок корреляции запросов |
## vSphere seed inventory
| Переменная | По умолчанию | Значение |
|---|---|---|
| `SEED_VSPHERE_PROFILE` | `large` | `small` \| `large` \| `demo-cluster` — см. [Профили seed](seed-profiles.md) |
| `SEED_VSPHERE_LARGE_HOSTS` | `10` | Число хостов для профиля `large` |
| `SEED_VSPHERE_LARGE_VMS` | `1000` | Число VM для профиля `large` |
## Опциональная legacy-плоскость
| Переменная | По умолчанию | Значение |
|---|---|---|
| `ENABLE_PVE_STUB` | `false` | Включает legacy Proxmox VE `/api2/*` stub-плоскость, унаследованную из общей platform lineage. Нативная vSphere `/api` + `/rest` + `/sdk` — основная плоскость по умолчанию независимо от этого флага. |
## Contract и catalog
| Переменная | Значение |
|---|---|
| `CONTRACT_SNAPSHOT` | Опциональный путь к нормализованному PVE-style snapshot (актуально только при `ENABLE_PVE_STUB=true`) |
| `CONTRACT_FALLBACK` | `error` (default), `schema-default`, или `fixture` — fallback-поведение для опциональной stub-плоскости |
| `COMPATIBILITY_EVIDENCE` | Опциональный путь к evidence JSON для отчётов совместимости |
| `CATALOG_ARTIFACT_URL_6``_9` | Метки catalog majors vSphere (6→7.0, 7→7.0 U3, 8→8.0, 9→8.0 U2); stub URLs, не live downloads |
Runtime hot-swap (Web UI / `POST /ui/api/contract/apply?major=N`) переключает
активный **catalog** major, используемый Web UI и compatibility/evidence
отчётами. Он не ограничивает зарегистрированную REST/SOAP поверхность — каждый
известный маршрут всегда обслуживается реальным обработчиком или DB-backed stub.
См. [Версии API](api-versions.md).
## Безопасность и задачи
| Переменная | Значение |
|---|---|
| `TICKET_SIGNING_KEY` | HMAC signing key для сессий (**меняйте вне toy labs**) |
| `TASK_WORKER_CONCURRENCY` | Число leased asyncio workers (132) |
| `TASK_LEASE_SECONDS` | Длительность lease PostgreSQL-задачи |
| `SIMULATION_TIME_SCALE` | Ускоряет симулированные длительности задач (выше = быстрее) |
## Client test hooks
| Переменная | Значение |
|---|---|
| `TEST_DATABASE_URL` | DSN для integration-тестов |
| `VSPHERE_BASE` | Базовый URL для cookbooks/probes (`https://localhost` с хоста, `http://simulator:8080` изнутри Compose) |
## Порты и TLS
| Endpoint | Назначение |
|---|---|
| `https://localhost` | Основная vCenter HTTPS точка входа (curl, browsers, pyvmomi, govmomi, Terraform, большинство examples) |
| `http://localhost` | HTTP-грань для лабораторных нужд |
| `localhost:5434` | PostgreSQL (только localhost) |
| Internal `simulator:8080` | Прямой процесс FastAPI; доступен только внутри Compose network |
Вшитый сертификат в `docker/tls/` — одноразовый development material.
Никогда не используйте его вне локальных labs. См. [Безопасность](security.md) и
[Порты](ports.md).
## Заметки по Compose
- `migrate` выполняется один раз; `simulator` ждёт успешного migrate.
- Development Compose bind-mount'ит репозиторий и включает Uvicorn reload.
- Сервис `api-gateway` (nginx) публикует `443`/`80` и проксирует на
внутренний процесс `simulator:8080`; устанавливает `X-VMware-Service` /
`X-Forwarded-Port`, чтобы будущие routers могли определить использованный listener.
## Открытые и неиспользуемые example keys
`.env.example` всё ещё перечисляет несколько ключей из общей platform lineage, которые
**не** потребляются текущей vSphere-first моделью настроек, в частности
`SIMULATION_SEED`, `SIMULATOR_ADMIN_ENABLED` и `SIMULATOR_ADMIN_TOKEN`. Не
предполагайте, что аутентифицированный admin API `/_simulator` существует сегодня — см.
[Безопасность](security.md).
+43
View File
@@ -0,0 +1,43 @@
**Language / Язык:** [English](../../domains/README.md) | [Русский](README.md)
# Руководства по доменам
Эти страницы описывают устойчивую семантику по областям API. Для исчерпывающих
списков методов используйте каталог Web UI или OpenAPI (`/docs`), либо
непосредственно
[`app/vsphere/rest/coverage.py`](../../../app/vsphere/rest/coverage.py) —
runtime всегда обслуживает полную зарегистрированную поверхность независимо от
активного catalog major.
| Руководство | Темы |
|---|---|
| [Session](session.md) | `/api/session`, legacy `/rest` session, SOAP `Login`/`Logout` |
| [Inventory](inventory.md) | Datacenter, cluster, folder, resource pool, host, datastore, network CRUD |
| [Виртуальные машины](vm.md) | Create/delete, power, hardware, snapshots, clone, relocate, guest ops |
| [Storage](storage.md) | Datastores, files, host storage devices, storage policies |
| [Networking](networking.md) | Standard/distributed portgroups, DVS, host networking |
| [Tagging](tagging.md) | Categories, tags, associations |
| [Content library](content-library.md) | Libraries, items, update/download sessions, OVF deploy |
| [SOAP / VIM](soap.md) | RetrieveServiceContent, PropertyCollector, task-returning operations |
| [Tasks](tasks.md) | CIS task ids, polling, workers |
| [Appliance](appliance.md) | Version, health, networking, timesync |
| [Авторизация](authz.md) | Roles, privileges, permissions |
## Карта персистентности
- Объекты inventory (hosts, VMs, datastores, networks, folders, …) →
`vsphere_objects` (MOID, type, name, parent, `props` JSONB).
- Sessions / credentials → `vsphere_sessions`, `vsphere_credentials`.
- Tasks → `vsphere_tasks`.
- Tags / categories / associations → `vsphere_tag_categories`,
`vsphere_tags`, `vsphere_tag_associations`.
- Content libraries / items → `vsphere_libraries`, `vsphere_library_items`.
- Метаданные файлов datastore → `vsphere_datastore_files`.
- Оставшиеся маршруты Broadcom Automation API (DB-backed stub surface) →
`vsphere_api_state` (миграция `011`).
- Update/download sessions content library → `vsphere_transfer_sessions`
(миграция `012`).
- Состояние transfer HttpNfcLease → `vsphere_nfc_leases` (миграция `012`).
- Views PropertyCollector / токены WaitForUpdates → `vsphere_pc_state`
(миграция `013`).
- Console tickets → `vsphere_console_tickets` (миграция `013`).
+35
View File
@@ -0,0 +1,35 @@
**Language / Язык:** [English](../../domains/appliance.md) | [Русский](appliance.md)
# Appliance
Поверхности vCenter Server Appliance (VCSA) — version, health, networking,
timesync:
[`app/vsphere/rest/appliance_ext.py`](../../../app/vsphere/rest/appliance_ext.py),
[`app/vsphere/domain/appliance.py`](../../../app/vsphere/domain/appliance.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/appliance/system/version` | Читается без сессии; отражает метку активного catalog major |
| GET | `/api/appliance/health/system` | Сводка общего health |
| GET/PUT/POST | `/api/appliance/networking` | Hostname, DNS, default gateway, interfaces, proxy |
| GET/PUT/POST | `/api/appliance/networking/dns/hostname` \| `/dns/servers` \| `/dns/domains` | Сфокусированные зеркала, синхронизированные с `/networking` |
| GET | `/api/appliance/timesync` | Режим NTP + servers |
| GET | `/api/vcenter/certificate-management/vcenter/tls[-csr]` \| `/trusted-root-chains` | Stand-in'ы machine-cert / CSR / trust-chain |
## Основные моменты
- Defaults моделируют реалистичный single-nic VCSA (`vcenter.lab.local`,
`192.168.1.50/24`, gateway `192.168.1.1`, DNS `8.8.8.8`/`1.1.1.1`).
- `save_networking` держит сфокусированные DNS-зеркала
(`/dns/hostname`, `/dns/servers`, `/dns/domains`) согласованными с полным
документом `/networking`, чтобы работали оба стиля клиентов Automation API.
- Состояние идемпотентно засевается один раз на свежую БД
(`seed_appliance_state`) и хранится в `vsphere_api_state`.
- Эндпоинты TLS/certificate-management — seeded stand-in'ы, не настоящее
хранилище сертификатов VECS — см. [Покрытие API](../api-coverage.md).
`/api/appliance/system/version` намеренно не требует сессию в этой lab-сборке
(поведение реального vCenter зависит от версии), чтобы smoke-скрипты могли
проверить доступность до аутентификации.
+51
View File
@@ -0,0 +1,51 @@
**Language / Язык:** [English](../../domains/authz.md) | [Русский](authz.md)
# Авторизация
Gate роль → privilege для мутирующих REST-эндпоинтов (и decorator-style hook
для SOAP): [`app/vsphere/security/authz.py`](../../../app/vsphere/security/authz.py),
[`platform_rest.py`](../../../app/vsphere/rest/platform_rest.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/vcenter/privilege` | Каталог привилегий |
| GET | `/api/vcenter/authorization/roles` | Role → набор privilege |
| GET/POST/DELETE | `/api/vcenter/authorization/permissions[/{permission_id}]` | Привязки principal ↔ role ↔ entity |
| GET/POST/PATCH/DELETE | `/api/vcenter/identity/providers[/{provider}]` | Stand-in'ы identity-provider LocalOS + OIDC + SAML |
## Роли (seed)
| Роль | Область |
|---|---|
| `Administrator` | Каждая привилегия в каталоге |
| `ReadOnly` | `System.Anonymous`, `System.Read`, `System.View`, `Datastore.Browse` |
| `VirtualMachinePowerUser` | Read + взаимодействия power/snapshot/clone |
| `VirtualMachineAdministrator` | Набор power-user + привилегии create/delete/reconfigure/tag/content-library |
`ROLE_PRIVILEGES` в `authz.py` задаёт точные наборы привилегий; неполный
пример gated-привилегий: `VirtualMachine.Inventory.Create`,
`VirtualMachine.Inventory.Delete`, `VirtualMachine.Interact.PowerOn`,
`VirtualMachine.Config.CPUCount`, `VirtualMachine.Provisioning.Clone`,
`Datastore.FileManagement`, `Network.Assign`,
`InventoryService.Tagging.CreateTag`, `ContentLibrary.AddLibraryItem`,
`Authorization.ModifyPermissions`.
## Как работает gating
- `require_privilege(*needed)` — фабрика зависимостей FastAPI: резолвит
сессию, загружает роли (из сессии или `vsphere_credentials`, если нет),
и поднимает HTTP 403 (`unauthorized`), если отсутствует любая из
перечисленных привилегий.
- `require_read` — сокращение для `require_privilege("System.Read")`.
- Permissions также могут ограничить роль конкретным entity MOID
(`PermissionSpec(principal, role, entity_moid, propagate)`); seed
ограничивает `readonly@vsphere.local` datacenter'ом, а двух VM-admin
принципалов — папкой VM.
## Seeded-принципалы
Четыре принципала `@vsphere.local` и их роли — в
[Аутентификация](../authentication.md); как permissions скоупятся по
профилю — в [Профили seed](../seed-profiles.md).
+38
View File
@@ -0,0 +1,38 @@
**Language / Язык:** [English](../../domains/content-library.md) | [Русский](content-library.md)
# Content library
Локальные content libraries, library items, upload/download sessions и OVF
deploy:
[`app/vsphere/rest/content_rest.py`](../../../app/vsphere/rest/content_rest.py),
[`nfc_rest.py`](../../../app/vsphere/rest/nfc_rest.py),
[`app/vsphere/domain/content.py`](../../../app/vsphere/domain/content.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/content/library` | Список library ids |
| POST | `/api/content/local-library` | Создать local library |
| GET/POST | `/api/content/library/item` | Список / create items (`?library_id=`) |
| POST | `/api/vcenter/ovf/library-item/{item_id}` | Deploy OVF item → новая `VirtualMachine` + task |
| POST | `/api/content/library/item/update-session[/{session_id}[/file]]` | Поток push-upload (стиль Ansible/Terraform) |
| GET/POST | `/api/content/library/item/download-session[/{session_id}[/file]]` | Поток pull-download |
| GET/PUT/POST | `/nfc/{lease}` \| `/nfc/{lease}/files/{filename}` \| `/nfc/{lease}/complete` | Эндпоинты transfer в стиле HttpNfcLease для SOAP import path |
## Основные моменты
- Libraries/items живут в `vsphere_libraries` / `vsphere_library_items`;
seed создаёт две libraries («Local Content», «Published Templates») с
OVF-typed items (`ubuntu-22.04`, `centos-stream-9`, `golden-image`).
- Update/download sessions живут в PostgreSQL (`vsphere_transfer_sessions`,
миграция `012`) и моделируют handshake передачи файлов — не реальное
byte-for-byte хранилище OVF/VMDK. Строки HttpNfcLease — в `vsphere_nfc_leases`.
- `deploy_ovf_from_library` создаёт реальную строку `VirtualMachine` и
возвращает task id, зеркаля SOAP-поток `ImportVApp_Task` /
`CreateImportSpec` + `HttpNfcLease*`, используемый govc-style `ovf.import`.
- Для create нужны `ContentLibrary.CreateLocalLibrary` / `.AddLibraryItem`,
для deploy — `VirtualMachine.Provisioning.DeployTemplate`.
Операции HttpNfcLease progress/complete/abort для upload-heavy клиентов —
[SOAP / VIM](soap.md).
+46
View File
@@ -0,0 +1,46 @@
**Language / Язык:** [English](../../domains/inventory.md) | [Русский](inventory.md)
# Inventory
Listing + CRUD для datacenter, cluster, folder, resource pool, host и
datastore/network:
[`app/vsphere/rest/router.py`](../../../app/vsphere/rest/router.py),
[`app/vsphere/domain/inventory_ops.py`](../../../app/vsphere/domain/inventory_ops.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/vcenter/datacenter` | Список |
| POST/DELETE | `/api/vcenter/datacenter[/{datacenter}]` | Create засевает подпапки host/vm/datastore/network |
| GET | `/api/vcenter/cluster` | Список |
| POST/DELETE | `/api/vcenter/cluster[/{cluster}]` | Create засевает `ResourcePool` |
| GET | `/api/vcenter/folder` | Список; `GET /api/vcenter/folder/{folder}/children` |
| POST/DELETE | `/api/vcenter/folder[/{folder}]` | |
| GET | `/api/vcenter/resource-pool` | Список |
| POST/DELETE | `/api/vcenter/resource-pool[/{resource_pool}]` | |
| GET | `/api/vcenter/host[/{host}]` | Connection state, CPU/memory, IP, storage devices, networking |
| POST | `/api/vcenter/host/{host}/maintenance` | Переключение maintenance mode |
| GET | `/api/vcenter/datastore[/{datastore}]` | Type, capacity, free space, accessibility |
| GET | `/api/vcenter/network` | Standard networks + distributed portgroups |
Legacy `/rest/vcenter/*` зеркалит большинство GET-путей с конвертом
`{ "value": … }` — см. [Поверхность API](../api-surface.md).
## Основные моменты
- Каждый объект inventory — строка в `vsphere_objects` (MOID, type, name,
`parent_moid`, `props` JSONB) — см.
[`app/vsphere/inventory.py`](../../../app/vsphere/inventory.py).
- Конвенции MOID следуют формам реального vCenter: `datacenter-NN`,
`domain-cNN` (cluster), `resgroup-NN` (resource pool), `group-vNN`/`group-hNN`/
`group-sNN`/`group-nNN` (папки VM/host/datastore/network), `host-NN`,
`datastore-NN`, `network-NN` / `dvportgroup-NN`.
- `list_hosts`/`list_clusters`/и т.п. фильтруют живое состояние PostgreSQL;
отдельного кэша для инвалидации после мутации нет.
- Список VM (`GET /api/vcenter/vm`) поддерживает фильтры: `names`,
`power_states`, `hosts`, `folders`, `datacenters`, `clusters`,
`resource_pools`, плюс пагинацию `limit`/`cursor`.
Форма топологии по умолчанию — [Профили seed](../seed-profiles.md);
операции по VM — [Виртуальные машины](vm.md).
+35
View File
@@ -0,0 +1,35 @@
**Language / Язык:** [English](../../domains/networking.md) | [Русский](networking.md)
# Networking
Standard networks, distributed portgroups/switches и host networking:
[`app/vsphere/rest/router.py`](../../../app/vsphere/rest/router.py),
[`platform_rest.py`](../../../app/vsphere/rest/platform_rest.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/vcenter/network` | Объекты standard `Network` + `DistributedVirtualPortgroup` |
| GET/POST | `/api/vcenter/network/dvs` | Distributed virtual switches |
| POST | `/api/vcenter/network/dvpg` | Создать distributed portgroup |
| GET | `/api/vcenter/host/{host}/networking` | DNS, default gateway, интерфейс `vmk0`, routing |
| GET/PUT/POST | `/api/appliance/networking` \| `/networking/dns/{hostname,servers,domains}` | Networking на уровне appliance vCenter (см. [Appliance](appliance.md)) |
Legacy `GET /rest/vcenter/network` зеркалит список.
## Основные моменты
- `nics[].value.backing` каждой VM указывает либо на `STANDARD_PORTGROUP`
(`network-41`, «VM Network»), либо на `DISTRIBUTED_PORTGROUP`
(`dvportgroup-4N`, с `vlan_id`).
- Топология по умолчанию засевает один `VmwareDistributedVirtualSwitch`
(`dvs-51`, `mtu: 9000`) и 1–3 дополнительных distributed portgroup в
зависимости от размера профиля.
- Host networking (`GET /api/vcenter/host/{host}/networking`) возвращает DNS
servers/domains, default gateway и один management-интерфейс `vmk0` с
детерминированным IPv4 по индексу host.
- Пути Automation API с меткой NSX (tier-0 gateway, projects, edges,
VPC/subnets) — seeded lab stand-in'ы под `namespace-management` — см.
таблицу «Platform surfaces» в [Покрытие API](../api-coverage.md); это не
реальный NSX Manager.
+32
View File
@@ -0,0 +1,32 @@
**Language / Язык:** [English](../../domains/session.md) | [Русский](session.md)
# Session
Устойчивая идентичность сессии, общая для REST и SOAP:
[`app/vsphere/security/session.py`](../../../app/vsphere/security/session.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| POST | `/api/session` | Basic auth → JSON-строка session id + заголовок/cookie `vmware-api-session-id` |
| GET | `/api/session` | HTTP 200; заголовки `x-vmware-session-user` / `x-vmware-session-roles` |
| DELETE | `/api/session` | Инвалидирует сессию, очищает cookie |
| POST/GET/DELETE | `/rest/com/vmware/cis/session` | Legacy-эквиваленты `{ "value": … }` |
| POST | SOAP `SessionManager.Login` | Возвращает тот же session id; ставит cookie `vmware_soap_session` |
| POST | SOAP `SessionManager.Logout` | Удаляет сессию |
## Основные моменты
- Сессии — непрозрачные 32-символьные hex-токены в `vsphere_sessions` со
**скользящим TTL 2 часа** — каждый аутентифицированный вызов продлевает
`expires_at`.
- Четыре лабораторные учётки (`vsphere_credentials`, scrypt-хеш) идемпотентно
обеспечиваются при первом login и каждым профилем seed
(`ensure_default_credentials`).
- `require_session` резолвит сессию из заголовка или cookie
`vmware-api-session-id`; отсутствует/истекла → HTTP 401.
- Роли привязываются к сессии при lookup (`vsphere_credentials.roles`) и
управляют [Авторизацией](authz.md).
Полные примеры запросов — [Аутентификация](../authentication.md).
+68
View File
@@ -0,0 +1,68 @@
**Language / Язык:** [English](../../domains/soap.md) | [Русский](soap.md)
# SOAP / VIM
Минимальный VIM SDK для клиентов в стиле pyvmomi / govmomi (провайдер
Terraform `hashicorp/vsphere`, Pulumi, govc):
[`app/vsphere/soap/router.py`](../../../app/vsphere/soap/router.py),
[`property_collector.py`](../../../app/vsphere/soap/property_collector.py),
[`pbm.py`](../../../app/vsphere/soap/pbm.py).
## Эндпоинт
Все операции POST'ят SOAP-конверт на `/sdk` (также `/sdk/`). Вспомогательные
маршруты:
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/sdk/vimService.wsdl` (alias `/sdk/vim.wsdl`) | WSDL stub со списком реализованных операций |
| GET | `/sdk/about.do` (alias `/about.do`) | Человекочитаемая страница «VMware vCenter Server» |
| POST | `/sdk/vim25/{version}/SessionManager/SessionManager/Login` | Login-вариант с JSON-телом, используемый некоторыми SDK |
## Реализованные операции
- `RetrieveServiceContent`, `Login`, `Logout`
- `RetrieveProperties`, `RetrievePropertiesEx`, **ContinueRetrievePropertiesEx**
(токены пагинации; plural `<objects>`), `CreateFilter`,
`WaitForUpdatesEx` (version tokens; пустые polls), `CreateContainerView`,
`DestroyPropertyFilter`
- `FindByInventoryPath` (пути без корневой папки `Datacenters`, как в
конвенциях govmomi), `FindByUuid`, `FindByDnsName`, `FindByIp`, `FindChild`
- `CreateVM_Task`, `CreateChildVM_Task`, `CreateFolder`, `PowerOnVM_Task`,
`PowerOffVM_Task`, `CloneVM_Task`, `CreateSnapshot_Task`, `Rename_Task`,
`ReconfigVM_Task`, `RelocateVM_Task`, `Destroy_Task`, `CustomizeVM_Task`,
`CancelTask`, `CurrentTime`
- Guest file ops: `InitiateFileTransferToGuest`,
`InitiateFileTransferFromGuest`, `ListFilesInGuest`, `DeleteFileInGuest`,
`MakeDirectoryInGuest`
- Import/upload: `ImportVApp_Task`, `CreateImportSpec`,
`HttpNfcLeaseComplete`, `HttpNfcLeaseProgress`, `HttpNfcLeaseAbort`,
`HttpNfcLeaseGetManifest` (в паре с REST `/nfc/{lease}`
см. [Content library](content-library.md))
- `QueryConfigOption`, `QueryConfigOptionEx`, `QueryConfigOptionDescriptor`,
`QueryConfigTarget`
- Stub PBM (`/pbm`) для клиентов, учитывающих storage policy
## Основные моменты
- `Login` выдаёт ту же underlying-сессию, что и REST (`vmware-api-session-id`
cookie/header плюс cookie `vmware_soap_session`) — см.
[Session](session.md).
- `VIM_VERSION` зафиксирован как `8.0.2` с ≤3 компонентами через точку, так
как `hashicorp/vsphere` строго парсит `AboutInfo.version`.
- Type-strict MOR lookup отклоняет ссылку `VirtualApp:resgroup-*`,
резолвящуюся как plain `ResourcePool` — важно для Terraform resource path
`CreateVM_Task`.
- Операции, возвращающие задачу, создают реальную строку в `vsphere_tasks`
(общую с REST — см. [Tasks](tasks.md)), включая MoRefs `info.result` при
create/clone.
- Filters PropertyCollector, ContainerViews и version tokens WaitForUpdatesEx
живут в `vsphere_pc_state` (миграция `013`) между перезапусками процесса
в рамках лаборатории.
- `Folder.childType` отдаётся как `ArrayOfString`; строковые свойства несут
`xsi:type="xsd:string"`, чтобы их принимал decoder govmomi; `Datastore.host`
`ArrayOfDatastoreHostMount`; у `Cluster`/`Host` есть `environmentBrowser`.
Примеры подключений pyvmomi/govmomi/Terraform/Pulumi — [Клиенты](../clients.md);
минимальный raw-XML smoke —
[examples/python/vsphere_soap_smoke.py](../../../examples/python/vsphere_soap_smoke.py).
+37
View File
@@ -0,0 +1,37 @@
**Language / Язык:** [English](../../domains/storage.md) | [Русский](storage.md)
# Storage
Datastores, метаданные файлов datastore, устройства хранения host и storage
policies:
[`app/vsphere/rest/router.py`](../../../app/vsphere/rest/router.py),
[`content_rest.py`](../../../app/vsphere/rest/content_rest.py),
[`platform_rest.py`](../../../app/vsphere/rest/platform_rest.py),
[`app/vsphere/domain/content.py`](../../../app/vsphere/domain/content.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/vcenter/datastore[/{datastore}]` | Type (`VMFS`/`NFS`), capacity, free space, `multiple_host_access` |
| GET/POST | `/api/vcenter/datastore/{datastore}/files` | Список / регистрация метаданных файлов (пути ISO, VMX, VMDK) |
| GET | `/api/vcenter/host/{host}/storage/storage-device` | Seeded local disk devices (`naa.*`, capacity, флаг SSD) |
| GET | `/api/vcenter/storage/policies[/{policy}/vm]` | Storage-based policy management, в т.ч. lab-политики `policy_type: VSAN` |
Legacy `GET /rest/vcenter/datastore` зеркалит список в конверте
`{ "value": … }`.
## Основные моменты
- Строки datastore засеваются реалистичными парами capacity/free-space
(`type`, `capacity`, `free_space`, `accessible`,
`multiple_host_access`) — см.
[`app/vsphere/profiles.py`](../../../app/vsphere/profiles.py).
- Метаданные файлов живут в `vsphere_datastore_files` (`path`, `size`, `type`);
seed заранее заполняет ISO и записи `.vmx`/`.vmdk` VM
(`seed_platform_extras`).
- Среди storage policies есть lab-политика с меткой vSAN `RAID1` — оговорку
по vSAN (seeded lab data, не реальный кластер vSAN) см. в таблице
«Platform surfaces» в [Покрытие API](../api-coverage.md).
- Устройства хранения host — синтетические диски на host, не реальные extents
ESXi VMFS; флаги capacity/SSD детерминированно зависят от индекса host.
+33
View File
@@ -0,0 +1,33 @@
**Language / Язык:** [English](../../domains/tagging.md) | [Русский](tagging.md)
# Tagging
Сервис CIS tagging (categories, tags, object associations):
[`app/vsphere/rest/tagging_rest.py`](../../../app/vsphere/rest/tagging_rest.py),
[`app/vsphere/domain/tagging.py`](../../../app/vsphere/domain/tagging.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET/POST | `/api/cis/tagging/category` | Список / create (`cardinality`, `associable_types`) |
| GET/DELETE | `/api/cis/tagging/category/{category_id}` | |
| GET/POST | `/api/cis/tagging/tag` | Список / create под category |
| GET/DELETE | `/api/cis/tagging/tag/{tag_id}` | |
| POST | `/api/cis/tagging/tag-association` | Attach/detach тега к/от объекта |
## Основные моменты
- Id category и tag следуют реальной форме
`urn:vmomi:InventoryServiceCategory:…` /
`urn:vmomi:InventoryServiceTag:…:GLOBAL`.
- Строки живут в `vsphere_tag_categories`, `vsphere_tags`,
`vsphere_tag_associations` — устойчивы между перезапусками, заменяются
при reseed.
- Seed создаёт две categories (`Environment`, `Owner`) с тегами `prod`/
`staging`/`platform` и прикрепляет `prod` к двум seeded VM
(`seed_platform_extras` в
[`app/vsphere/domain/content.py`](../../../app/vsphere/domain/content.py)).
- Attach/create тега требует привилегии
`InventoryService.Tagging.CreateCategory` / `.CreateTag` / `.AttachTag`
см. [Авторизация](authz.md).
+38
View File
@@ -0,0 +1,38 @@
**Language / Язык:** [English](../../domains/tasks.md) | [Русский](tasks.md)
# Tasks
Длительные операции (power, clone, relocate, snapshot, OVF deploy, guest
customize) возвращают CIS-style task id:
[`app/vsphere/domain/tasks.py`](../../../app/vsphere/domain/tasks.py),
[`app/vsphere/rest/tasks.py`](../../../app/vsphere/rest/tasks.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/cis/tasks` | Список недавних задач (до 200 последних) |
| GET | `/api/cis/tasks/{task}` | Status, progress, `service`/`operation`, `result`/`error` |
## Паттерн клиента
1. Мутация `POST`/`DELETE` → прочитайте task id из `{ "task": "task-…" }`
(REST) или SOAP MoRef `*_Task`.
2. Опрашивайте `GET /api/cis/tasks/{task}`, пока `status` не станет
`SUCCEEDED` или `FAILED`.
3. В `result` — результат операции (например `{"vm": "vm-104"}` при
create/clone/deploy).
## Основные моменты
- Строки задач коммитятся в `vsphere_tasks` (`id`, `description`, `status`,
`service`, `operation`, `result`, `error`, `completed_at`).
- `progress` синтезируется как `50` во время выполнения и `100` в терминальном
состоянии — дробный progress этот симулятор не моделирует.
- Одно и то же хранилище задач обслуживает и REST `/api/cis/tasks`, и SOAP
task MoRefs, поэтому Terraform apply (SOAP `CreateVM_Task`) и REST-опрос
того же id видят согласованное состояние.
- Длительности симуляции учитывают `SIMULATION_TIME_SCALE`
(больше = быстрее завершение).
См. [Поверхность API](../api-surface.md) и [Эксплуатация](../operations.md).
+50
View File
@@ -0,0 +1,50 @@
**Language / Язык:** [English](../../domains/vm.md) | [Русский](vm.md)
# Виртуальные машины
Полный REST lifecycle для объектов `VirtualMachine`:
[`app/vsphere/rest/router.py`](../../../app/vsphere/rest/router.py),
[`vm_ext.py`](../../../app/vsphere/rest/vm_ext.py),
[`app/vsphere/domain/vm_ops.py`](../../../app/vsphere/domain/vm_ops.py).
## Эндпоинты
| Метод | Путь | Заметки |
|---|---|---|
| GET | `/api/vcenter/vm` | Список с фильтрами `names`/`power_states`/`hosts`/`folders`/`datacenters`/`clusters`/`resource_pools`/`limit`/`cursor` |
| GET/DELETE | `/api/vcenter/vm/{vm}` | Get / delete (должна быть powered off) |
| POST | `/api/vcenter/vm` | Create — `placement.{folder,host,datastore,resource_pool}`, `cpu.count`, `memory.size_MiB`, `disks`, `nics` |
| GET/POST | `/api/vcenter/vm/{vm}/power` | Get power state / `?action=start\|stop\|suspend\|reset` — возвращает `{ "task": "task-…" }` |
| GET | `/api/vcenter/vm/{vm}/hardware` | Сводка |
| GET/PATCH | `/api/vcenter/vm/{vm}/hardware/cpu` \| `/memory` | Смена CPU count / memory (privilege-gated) |
| GET/POST | `/api/vcenter/vm/{vm}/hardware/disk` \| `/ethernet` | Добавить disk / NIC |
| GET | `/api/vcenter/vm/{vm}/hardware/boot` | Boot type/order |
| GET/POST/DELETE | `/api/vcenter/vm/{vm}/snapshots[/{snapshot}]` | Create, revert (`?action=revert`), delete |
| POST | `/api/vcenter/vm/{vm}/clone` \| `/relocate` | Возвращают задачу |
| GET/POST | `/api/vcenter/vm/{vm}/tools` | Статус guest tools / upgrade |
| GET | `/api/vcenter/vm/{vm}/guest/identity` \| `/networking` | Имя guest OS, синтетический IP |
| GET/POST | `/api/vcenter/vm/{vm}/guest/power` | Guest-level power ops |
| POST | `/api/vcenter/vm/{vm}/guest/customization` | Спека customization в стиле sysprep/cloud-init |
| POST | `/api/vcenter/vm/{vm}/console/tickets` | Console-тикет (стиль VNC/WebMKS) |
| GET/PUT/DELETE | `/api/vcenter/vm/{vm}/guest/filesystem` | Lab virtual guest filesystem (потоки write-a-file Ansible/Terraform) |
| GET | `/api/vcenter/vm/{vm}/guest/filesystem/files` \| `/guest/local-filesystem` | Listing |
## Основные моменты
- У каждой строки VM реалистичная форма устройств: `nics`, `disks`, `cdroms`,
`floppies`, `serials`, `scsi_adapters`, `boot`/`boot_devices`, `identity`
(`instance_uuid`, `bios_uuid`) и синтетическая карта `guest_ip` /
`guest_filesystems` — те же поля питают и REST hardware-эндпоинты, и SOAP
`VirtualMachineConfigInfo`.
- Create требует `VirtualMachine.Inventory.Create`; delete требует
`VirtualMachine.Inventory.Delete` **и** VM должна быть `POWERED_OFF`.
- Power/clone/snapshot/relocate/customize создают устойчивую CIS-задачу (см.
[Tasks](tasks.md)), а не мутируют синхронно в теле ответа.
- Console tickets из `/api/vcenter/vm/{vm}/console/tickets` живут в
`vsphere_console_tickets` (миграция `013`).
- MOID следуют конвенции `vm-{100+n}`, засеваемой
[`app/vsphere/profiles.py`](../../../app/vsphere/profiles.py).
Семантика datastore/disk-file — [Storage](storage.md); эквивалентные
операции `CreateVM_Task`/`PowerOnVM_Task`/… для pyvmomi, govmomi, Terraform и
Pulumi — [SOAP / VIM](soap.md).
+23
View File
@@ -0,0 +1,23 @@
**Language / Язык:** [English](../../examples/ansible.md) | [Русский](ansible.md)
# Ansible
Playbook использует модуль `uri` против HTTPS-шлюза
(`https://localhost`): вход по Basic-auth сессии, затем вызовы с
заголовком `vmware-api-session-id` для остального жизненного цикла.
```bash
cd examples/ansible
ansible-playbook -i inventory.ini vsphere_playbook.yml
```
[`vsphere_playbook.yml`](../../../examples/ansible/vsphere_playbook.yml) охватывает:
вход в сессию, список ВМ, создание, power on, опрос CIS-задачи
(`/api/cis/tasks/{task}`), запись файла в лабораторную гостевую виртуальную ФС,
power off, удаление и выход из сессии.
Перед опорой на фиксированные имена ВМ/MOID из предыдущего запуска выполните
повторный seed симулятора (`make seed`).
Для lab-набора на официальном `pulumi-vsphere` (непустые export'ы, HTML-отчёт)
см. [`pulumi-tests/`](../../../pulumi-tests/README.ru.md) или `make pulumi-tests`.
+21
View File
@@ -0,0 +1,21 @@
**Language / Язык:** [English](../../examples/go.md) | [Русский](go.md)
# Go
Использует стандартную библиотеку Go (`net/http`) против
`https://localhost` с Basic-auth сессией
(`vmware-api-session-id`).
```bash
cd examples/go
go run .
```
Переопределите значения по умолчанию через `VSPHERE_BASE`, `VSPHERE_USER`,
`VSPHERE_PASSWORD`, `VSPHERE_VM_NAME`. См. [`main.go`](../../../examples/go/main.go)
для потока session → list → create → power → wait-task → delete и вспомогательной
функции `waitTask`, которая опрашивает `GET /api/cis/tasks/{task}`.
Проверка TLS отключена в HTTP-клиенте только для локального самоподписанного
сертификата разработческого шлюза — не переиспользуйте такой transport против
реального vCenter.
+22
View File
@@ -0,0 +1,22 @@
**Language / Язык:** [English](../../examples/java.md) | [Русский](java.md)
# Java
Cookbook на Java 11+ `HttpClient` с Basic-auth сессией
(`vmware-api-session-id`) против `https://localhost`. Без сторонних
JSON-библиотек — ответы разбираются простым строковым извлечением полей,
достаточным для лабораторного smoke.
```bash
cd examples/java
javac Cookbook.java && java Cookbook
```
Переопределите значения по умолчанию переменными окружения `VSPHERE_BASE`,
`VSPHERE_USER`, `VSPHERE_PASSWORD`, `VSPHERE_VM_NAME`. См.
[`Cookbook.java`](../../../examples/java/Cookbook.java) для потока session →
create → power → wait-task → delete.
Клиент устанавливает trust-all `SSLContext` только для локального
самоподписанного сертификата разработческого шлюза — не переиспользуйте его
против реального vCenter.
+53
View File
@@ -0,0 +1,53 @@
**Language / Язык:** [English](../../examples/overview.md) | [Русский](overview.md)
# Обзор примеров клиентов
## Чеклист запуска
```bash
make up
curl -skf https://localhost/health/ready
make seed
curl -sk https://localhost/api/appliance/system/version
```
## Конечные точки
| URL | Когда использовать |
|---|---|
| `https://localhost` | curl, pyvmomi, govmomi, Terraform, Pulumi, Ansible, Go, Java, Perl — всё из `examples/` |
| `http://localhost` | Лабораторный HTTP без TLS (без рукопожатия TLS) |
## Краткая справка по аутентификации
**Сессия (REST)**
```bash
SID=$(curl -sk -u 'administrator@vsphere.local:VMware1!' \
-X POST https://localhost/api/session | tr -d '"')
curl -sk -H "vmware-api-session-id: $SID" https://localhost/api/vcenter/vm
```
**SOAP Login**
```bash
python examples/python/vsphere_soap_smoke.py https://localhost
```
## Ожидание задач
Не считайте HTTP-ответ мутации достаточным признаком «ВМ запущена». Вызовы
power, clone, relocate, snapshot и OVF-deploy возвращают `{ "task": "task-…" }`;
опрашивайте `GET /api/cis/tasks/{task}`, пока `status` не станет `SUCCEEDED` или
`FAILED`. См. [Задачи](../domains/tasks.md).
## Предупреждение о повторном seed
`make seed` заменяет инвентарь PostgreSQL. После этого обновите состояние
Terraform/Pulumi/Ansible — см. [Профили seed](../seed-profiles.md).
## Дерево исполняемых примеров
См. [`examples/README.ru.md`](../../../examples/README.ru.md). Lab-набор на
официальном `pulumi-vsphere` — в
[`pulumi-tests/`](../../../pulumi-tests/README.ru.md) (`make pulumi-tests`).
+20
View File
@@ -0,0 +1,20 @@
**Language / Язык:** [English](../../examples/perl.md) | [Русский](perl.md)
# Perl
Cookbook на `HTTP::Tiny` + `JSON` с Basic-auth сессией
(`vmware-api-session-id`) против `https://localhost`.
```bash
cd examples/perl
cpanm --installdeps . # или установите HTTP::Tiny, JSON, IO::Socket::SSL вручную
perl cookbook.pl
```
Переопределите значения по умолчанию переменными окружения `VSPHERE_BASE`,
`VSPHERE_USER`, `VSPHERE_PASSWORD`, `VSPHERE_VM_NAME`. См.
[`cookbook.pl`](../../../examples/perl/cookbook.pl) для потока session → list →
create → power → wait-task → delete.
`HTTP::Tiny` создаётся с `verify_SSL => 0` только для локального самоподписанного
сертификата разработческого шлюза.
+32
View File
@@ -0,0 +1,32 @@
**Language / Язык:** [English](../../examples/pulumi.md) | [Русский](pulumi.md)
# Pulumi
[`examples/pulumi/`](../../../examples/pulumi/) — Python-программа Pulumi на
официальном [`pulumi-vsphere`](https://www.pulumi.com/registry/packages/vsphere/)
(SOAP/VIM) против симулятора — тот же путь провайдера, что у Terraform
`hashicorp/vsphere`.
```bash
cd examples/pulumi
pip install -r requirements.txt
pulumi plugin install resource vsphere 4.17.0
pulumi stack init dev # один раз
pulumi config set server localhost
pulumi config set --secret password 'VMware1!'
pulumi up
```
Конфигурация (`pulumi config set`): `server` (по умолчанию `localhost`), `user`
(по умолчанию `administrator@vsphere.local`), `password` (secret), `datacenter`,
`datastore`, `cluster`, `network`, `vm_name` (по умолчанию `pulumi-lab-01`).
Та же осторожность при reseed, что и для Terraform: состояние PostgreSQL
симулятора и state Pulumi независимы. Закрепите major каталога для
воспроизводимого CI, если ваш workflow зависит от вывода Web UI/evidence (см.
[Версии API](../api-versions.md)) — сами runtime-маршруты доступны всегда
независимо от major.
Для lab-набора (inventory + folder + VM + tags, проверки непустых export'ов,
HTML-отчёт) см. [`pulumi-tests/`](../../../pulumi-tests/README.ru.md) или
`make pulumi-tests` из корня репозитория.
+33
View File
@@ -0,0 +1,33 @@
**Language / Язык:** [English](../../examples/python-requests.md) | [Русский](python-requests.md)
# Python — REST (requests / stdlib)
Сырой HTTP к REST-шлюзу vSphere без vendor SDK.
```bash
pip install -r examples/python/requirements.txt
python examples/python/requests_cookbook.py
```
[`requests_cookbook.py`](../../../examples/python/requests_cookbook.py)
демонстрирует общий поток session → create → wait-for-task → power on → wait →
power off → delete с помощью `requests`; идентификатор сессии передаётся в
заголовке `vmware-api-session-id`.
Для варианта без внешних зависимостей, только на стандартной библиотеке
(`urllib`), см. [`vsphere_rest_smoke.py`](../../../examples/python/vsphere_rest_smoke.py):
```bash
python examples/python/vsphere_rest_smoke.py https://localhost
```
Для комбинированного smoke REST-create + SOAP-`CreateVM_Task` + guest-filesystem
см. [`vsphere_lifecycle.py`](../../../examples/python/vsphere_lifecycle.py):
```bash
VSPHERE_BASE=https://localhost python examples/python/vsphere_lifecycle.py
```
Все три скрипта по умолчанию используют `administrator@vsphere.local` / `VMware1!`
и отключают проверку TLS только для локального самоподписанного сертификата
разработческого шлюза.
+32
View File
@@ -0,0 +1,32 @@
**Language / Язык:** [English](../../examples/terraform.md) | [Русский](terraform.md)
# Terraform
[`examples/terraform/vsphere/`](../../../examples/terraform/vsphere/) использует
официальный провайдер `hashicorp/vsphere` (SOAP `/sdk` под капотом), направленный
на локальный HTTPS-шлюз (`https://localhost`) с
`allow_unverified_ssl = true` для разработческого сертификата.
```bash
cd examples/terraform/vsphere
terraform init
TF_VAR_create_lab_vm=false terraform plan # только data sources (datacenter/cluster/datastore/network/VM)
TF_VAR_create_lab_vm=true terraform apply # также создаёт лабораторную ВМ (SOAP CreateVM_Task)
```
Значения по умолчанию (`variables.tf`): `vsphere_server = "localhost"`,
`vsphere_user = "administrator@vsphere.local"`,
`vsphere_password = "VMware1!"`, `datacenter = "Datacenter"`,
`cluster = "Cluster"`, `datastore = "datastore1"`,
`network = "VM Network"`, `vm_name = "web-01"` (ВМ из seed `small`/`large`).
Версии плагинов провайдера меняются быстро — закрепите версии в блоке
`required_providers` под то, что вы протестировали. После `make seed` обновите
или пересоздайте state, чтобы допущения об именах ВМ/MOID оставались согласованными.
Этот cookbook — отправная точка для лабораторного CI, а не сертификация каждого
resource/data source `hashicorp/vsphere` против полного реестра маршрутов. См.
[SOAP / VIM](../domains/soap.md) для точных операций, лежащих в основе create/read
путей провайдера, и
[`pulumi-tests/`](../../../pulumi-tests/README.ru.md) для lab-набора
`pulumi-vsphere` (`make pulumi-tests`).
@@ -0,0 +1,15 @@
**Language / Язык:** [English](../../examples/troubleshooting-clients.md) | [Русский](troubleshooting-clients.md)
# Устранение неполадок клиентов
| Симптом | Решение |
|---|---|
| Ошибки TLS-сертификата | Используйте `:443` с `verify=False` / `insecure`/`allow_unverified_ssl=true` **только** локально или plain HTTP `:80` |
| 401 на первом вызове | Отправляйте `Authorization: Basic …` только на `/api/session` (или SOAP `Login`); все остальные вызовы требуют `vmware-api-session-id` |
| 403 на power/create | Возможно, вы используете `readonly@vsphere.local` — переключитесь на `administrator@vsphere.local` или `operator@vsphere.local` |
| ВМ не найдена | Имена ВМ seed `small`: `web-01`, `web-02`, `db-01`, `app-01`, `jumpbox` — не числовые VMID в стиле Proxmox |
| Create возвращает MOID, а не task | REST `POST /api/vcenter/vm` синхронно возвращает MOID новой ВМ; только **power/clone/relocate/snapshot/OVF-deploy** возвращают `{ "task": "…" }` |
| Create провайдера vs task | Опрашивайте `/api/cis/tasks/{task}`; многие провайдеры (Terraform, Pulumi) уже ждут внутри — сырые HTTP/Go/Java/Perl клиенты часто забывают |
| Drift после reseed | Обновите/пересоздайте state Terraform/Pulumi/Ansible после `make seed` |
| Сессия истекла во время выполнения | Сессии имеют скользящий TTL 2 часа; выполните повторный login, если длинный скрипт простаивал дольше |
| SOAP `Login` не проходит | Убедитесь, что envelope направлен на `/sdk` с `SOAPAction` (пустая строка допустима) и `Content-Type: text/xml` |
+57
View File
@@ -0,0 +1,57 @@
**Language / Язык:** [English](../faq.md) | [Русский](faq.md)
# FAQ
## Это настоящий vCenter / ESXi?
Нет. Это симулятор API и состояния. Хосты, VM, datastores и сети —
устойчивые модели PostgreSQL, а не ESXi-хосты или процессы KVM/vmkernel.
## Вы действительно покрываете vSphere Automation API?
**Runtime** всегда обслуживает полную зарегистрированную route table (1077 маршрутов:
104 deep handlers + DB-backed stub surface для остальных) — см.
[Поверхность API](api-surface.md). **Catalog** majors 6–8 — намеренно
низкопокрытые исторические floors (2.9%9.6%); только major 9 (8.0 U2 / Automation
9.1 surface) объявлен как 100% в catalog. См.
[Версии API](api-versions.md) и [Совместимость](compatibility.md).
## Можно ли использовать это в CI для Terraform / Ansible / pyvmomi / custom clients?
Да. Это основной сценарий использования. Засейте профиль и направьте клиентов на
HTTPS gateway `:443` (REST `/api`/`/rest` или SOAP `/sdk`). См.
[Клиенты](clients.md).
## Почему некоторые NSX / Supervisor / vSAN / SAML calls «успешны» без remotes?
Эти области сохраняют **локальное, засеянное** состояние симулятора (см. таблицу
«Platform surfaces» в [Покрытие API](api-coverage.md)). Они намеренно не
обращаются к реальному NSX Manager, Tanzu Supervisor или IdP.
## Означает ли registry coverage perfect vSphere parity?
Это означает, что каждый зарегистрированный маршрут имеет устойчивый обработчик
или DB-backed stub и проходит verification suites проекта. Точное совпадение
краевых случаев с физическим ESXi-кластером может отличаться; используйте
`/ui/api/compatibility` и собственные client tests для certification claims.
## Где Web UI?
[https://localhost/](https://localhost/) после `make up` (gateway).
## Можно ли развернуть в Kubernetes?
Да. Используйте Helm chart в `helm/vmware-api-simulator` с опубликованным
образом Hub. Поддерживаются Ingress + cert-manager Let's Encrypt — см.
[Kubernetes / Helm](kubernetes.md).
## Что такое `ENABLE_PVE_STUB`?
Опциональная, выключенная по умолчанию legacy Proxmox VE `/api2/*` stub-плоскость,
унаследованная из общей platform lineage. Нативная vSphere REST/SOAP всегда
включена и является основной поверхностью проекта независимо от этого флага.
## Какие VM использует seed `small`?
`web-01`, `web-02`, `db-01`, `app-01`, `jumpbox` — см.
[Профили seed](seed-profiles.md).
+176
View File
@@ -0,0 +1,176 @@
**Language / Язык:** [English](../getting-started.md) | [Русский](getting-started.md)
# Быстрый старт
Поднимите локальную лабораторию vSphere, пройдите аутентификацию и выполните первый
цикл чтения/мутации против симулятора.
## Требования
- Docker и Docker Compose
- `make` (необязательно, но используется в документированных командах)
Python, линтеры и тесты запускаются **внутри** контейнеров. Для повседневной работы
локальный Python-инструментарий не нужен.
## Выберите путь
| Путь | Когда использовать |
|---|---|
| [Опубликованный образ](#1a-опубликованный-образ-docker-hub) | Самая быстрая лаборатория на `inecs/vmware-api-simulator` |
| [Helm / Kubernetes](kubernetes.md) | Установка в кластер с Ingress + Let's Encrypt |
| [Development checkout](#1b-development-checkout) | Вклад в код / bind-mount исходников |
## 1a. Опубликованный образ (Docker Hub)
Использует [`docker-compose.release.yml`](../../docker-compose.release.yml) — PostgreSQL +
runtime-симулятор + HTTPS gateway с Hub. Сборка исходников не нужна, но Compose
нужно запускать из **checkout этого репозитория**, чтобы смонтировались
`docker/gateway/` и `docker/tls/`. Seed выполняется автоматически после готовности
симулятора.
```bash
# из git checkout этого репозитория (нужны docker/gateway и docker/tls)
docker compose -f docker-compose.release.yml pull
docker compose -f docker-compose.release.yml up -d --wait
```
Закрепить версию:
```bash
IMAGE_TAG=0.1.0 docker compose -f docker-compose.release.yml up -d --wait
```
Make-хелперы (git checkout):
```bash
make release-up
# опциональный повторный seed: make release-seed PROFILE=small
```
| Порт хоста | Сервис |
|---|---|
| `443` | HTTPS gateway (основная точка входа vCenter) |
| `80` | HTTP lab face |
| `5434` | PostgreSQL (только localhost) |
Миграции выполняются автоматически через one-shot сервис `migrate`.
Далее — с [Дождитесь готовности](#2-дождитесь-готовности).
## 1b. Development checkout
```bash
make install
make up
```
Сервисы (полная картина — [Порты](ports.md)):
| Порт хоста | Сервис |
|---|---|
| `443` | HTTPS gateway (nginx) → simulator |
| `80` | HTTP lab face |
| `5434` | PostgreSQL (только localhost) |
Миграции применяются автоматически до готовности симулятора. Внутренний
процесс FastAPI слушает `8080` и на хост не публикуется.
## 2. Дождитесь готовности
```bash
curl -sk https://localhost/health/live
curl -sk https://localhost/health/ready
```
`/health/ready` возвращает HTTP 503, пока PostgreSQL недоступен **и** пока
не применена последняя упакованная миграция.
## 3. Засейте профиль
```bash
make seed # default: large — 10 hosts / 1000 VMs
VSPHERE_PROFILE=small make seed
```
`small` создаёт 3-хостовый кластер с пятью именованными VM (`web-01`, `web-02`,
`db-01`, `app-01`, `jumpbox`), datastores, standard portgroup и четырьмя
лабораторными принципалами. Другие размеры — [Профили seed](seed-profiles.md).
## 4. Проверьте версию API
```bash
curl -sk https://localhost/api/appliance/system/version | jq .
```
Catalog major при холодном старте по умолчанию — **9** (vSphere 8.0 U2 /
поверхность Automation 9.1) в Docker Compose. Просмотр и hot-swap majors 69 —
из Web UI или [Версии API](api-versions.md).
## 5. Аутентификация
```bash
SID=$(curl -sk -u 'administrator@vsphere.local:VMware1!' \
-X POST https://localhost/api/session | tr -d '"')
echo "$SID"
```
`SID` — это `vmware-api-session-id`. Передавайте его в каждом последующем
вызове как заголовок (или опирайтесь на cookie, которую также выставляет
ответ login):
```bash
curl -sk -H "vmware-api-session-id: $SID" https://localhost/api/vcenter/vm
```
Подробности: [Аутентификация](authentication.md).
## 6. Список VM и включение одной
```bash
curl -sk -H "vmware-api-session-id: $SID" \
https://localhost/api/vcenter/vm | jq .
curl -sk -X POST -H "vmware-api-session-id: $SID" \
"https://localhost/api/vcenter/vm/vm-104/power?action=start" | jq .
```
Power-действия и другие длительные операции возвращают CIS task id.
Опрашивайте задачу до завершения:
```bash
curl -sk -H "vmware-api-session-id: $SID" \
"https://localhost/api/cis/tasks/${TASK_ID}" | jq .
```
## 7. Откройте Web UI
Откройте [https://localhost/](https://localhost/) — интерактивная
консоль, каталог эндпоинтов (vSphere majors 69), вид совместимости, apply
runtime-контракта и управление demo-cluster. Скриншоты светлой/тёмной темы и
полный список возможностей — [Web UI](web-ui.md).
## 8. Попробуйте клиентскую библиотеку
```bash
# from the repository root after make up + seed
python examples/python/vsphere_rest_smoke.py https://localhost
python examples/python/vsphere_soap_smoke.py https://localhost
```
Другие стеки: [Клиенты](clients.md) и [`examples/`](../../examples/README.ru.md).
## Готово, когда…
- `/health/ready` возвращает `{"status": "ok"}` (или эквивалентное OK-тело)
- `/api/appliance/system/version` сообщает версию активного catalog major
- Session login успешен для `administrator@vsphere.local`
- `/api/vcenter/vm` перечисляет seeded VM
- Power-действие возвращает task id, который доходит до `SUCCEEDED`
## Дальше
- [Конфигурация](configuration.md) — env vars, workers, размер seed
- [Версии API](api-versions.md) — hot-swap catalog majors 69
- [Клиенты](clients.md) — Python, Ansible, Terraform, Pulumi
- [Эксплуатация](operations.md) — reseed, migrate, upgrades
+166
View File
@@ -0,0 +1,166 @@
**Language / Язык:** [English](../kubernetes.md) | [Русский](kubernetes.md)
# Kubernetes / Helm
Разверните опубликованный образ runtime из Docker Hub с помощью чарта
[`helm/vmware-api-simulator`](../../helm/vmware-api-simulator).
Образ: [`inecs/vmware-api-simulator`](https://hub.docker.com/r/inecs/vmware-api-simulator)
## Требования
- Kubernetes 1.27+ (или сопоставимая версия)
- Helm 3.14+
- [Ingress NGINX](https://kubernetes.github.io/ingress-nginx/) (или другой
IngressClass с поддержкой HTTP-01)
- [cert-manager](https://cert-manager.io/), установленный на весь кластер
Пример установки cert-manager:
```bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml
```
## Быстрая установка (Hub-релиз + Ingress + Let's Encrypt)
Из git checkout этого репозитория:
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
-f ./helm/vmware-api-simulator/values-ingress-example.yaml \
--set certManager.email=you@example.com \
--set ingress.hosts[0].host=vmware-sim.example.com \
--set ingress.tls[0].hosts[0]=vmware-sim.example.com \
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
--set postgresql.auth.password="$(openssl rand -hex 16)"
```
Что это делает:
1. Скачивает `inecs/vmware-api-simulator:0.1.0` (см. `image.tag` в примерном файле).
2. Устанавливает встроенный PostgreSQL 17 (`postgres:17.5-bookworm`, как и в Compose).
3. Выполняет миграции схемы в init-контейнере (идемпотентно).
4. Загружает лабораторный профиль `small` (`seed.enabled=true`).
5. Создаёт ресурсы `ClusterIssuer`:
- `letsencrypt-prod`
- `letsencrypt-staging`
6. Создаёт Ingress с
`cert-manager.io/cluster-issuer: letsencrypt-prod` и TLS-секретом
`vmware-api-simulator-tls`.
DNS для `vmware-sim.example.com` должен указывать на ваш Ingress-контроллер.
Затем:
```bash
kubectl -n vmware-sim get certificate,ingress,pods
# дождитесь Certificate READY=True
curl -sS https://vmware-sim.example.com/health/ready
open https://vmware-sim.example.com/
```
Seeded-логин по умолчанию: `administrator@vsphere.local` / `VMware1!`.
### Сначала staging (рекомендуется)
Проверьте HTTP-01, не расходуя лимиты запросов production:
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
-f ./helm/vmware-api-simulator/values-ingress-example.yaml \
--set certManager.email=you@example.com \
--set certManager.useStaging=true \
--set ingress.hosts[0].host=vmware-sim.example.com \
--set ingress.tls[0].hosts[0]=vmware-sim.example.com \
--set secret.ticketSigningKey="$(openssl rand -hex 32)"
```
Браузеры не будут доверять staging CA — используйте `curl -k` во время
тестирования. Переключите `certManager.useStaging=false` и пересоздайте
Certificate/TLS-секрет для production.
## Минимальная установка (ClusterIP + port-forward)
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
--set seed.enabled=true
kubectl -n vmware-sim port-forward svc/vmware-sim-vmware-api-simulator 8080:8080
```
Откройте http://127.0.0.1:8080/. Service выставляет внутренний порт
приложения (`8080`, см. [Порты](ports.md)) — чарт не запускает TLS-gateway
nginx, используемый Compose; в production выставляйте TLS перед сервисом
через Ingress, либо обращайтесь к обычному HTTP-сервису для локального
тестирования.
## Внешний PostgreSQL
```bash
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
-n vmware-sim --create-namespace \
--set postgresql.enabled=false \
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
--set secret.databaseUrl='postgresql://user:pass@pg.example.com:5432/vmware_simulator'
```
Либо используйте `secret.existingSecret` с ключами `DATABASE_URL` и
`TICKET_SIGNING_KEY`.
## Как работает выпуск TLS
Когда `certManager.enabled=true` и `certManager.createClusterIssuer=true`,
чарт создаёт объекты ACME `ClusterIssuer`, которые решают HTTP-01 через ваш
Ingress-класс. Шаблон Ingress добавляет:
```yaml
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- secretName: vmware-api-simulator-tls
hosts: [vmware-sim.example.com]
```
Затем cert-manager создаёт `Certificate`, проходит HTTP-01 и сохраняет пару
ключей Let's Encrypt в этом TLS-секрете. Чарт **не** устанавливает
cert-manager или Ingress-контроллер — только issuer'ы и связку с Ingress.
Если ClusterIssuer'ы уже существуют на уровне кластера, задайте:
```yaml
certManager:
enabled: true
createClusterIssuer: false
issuerName: your-existing-issuer
```
## Эксплуатация
```bash
# логи
kubectl -n vmware-sim logs -l app.kubernetes.io/instance=vmware-sim -c simulator -f
# reseed
kubectl -n vmware-sim exec deploy/vmware-sim-vmware-api-simulator -- \
python -m app.simulation.seed_cli
# SEED_VSPHERE_PROFILE через: kubectl set env ... либо --set seed.profile=demo-cluster и upgrade
# удаление
helm -n vmware-sim uninstall vmware-sim
```
## Справочник по values
См. [`helm/vmware-api-simulator/values.yaml`](../../helm/vmware-api-simulator/values.yaml)
и [README чарта](../../helm/vmware-api-simulator/README.ru.md). Связанная
документация:
- [Быстрый старт](getting-started.md) — пути через Compose
- [Эксплуатация](operations.md) — публикация в Docker Hub / release compose
- [Безопасность](security.md) — лабораторные учётные данные и граница доверия
- [Порты](ports.md) — внутренний `8080` в сравнении с опубликованными портами gateway
+47
View File
@@ -0,0 +1,47 @@
**Language / Язык:** [English](../observability.md) | [Русский](observability.md)
# Наблюдаемость
## Health
| Path | Значение |
|---|---|
| `GET /health/live` | Liveness процесса — без проверки зависимостей |
| `GET /health/ready` | База данных доступна через `database.is_ready()`; HTTP 503, если нет |
Пример:
```bash
curl -sk https://localhost/health/live
curl -sk https://localhost/health/ready
```
Реализация: [`app/observability/health.py`](../../app/observability/health.py).
## Корреляция запросов
Входящие запросы принимают или генерируют ID через `REQUEST_ID_HEADER`
(по умолчанию `X-Request-ID`). Структурированные логи содержат поля
корреляции и маскируют известные шаблоны секретов (session id, пароли,
токены в стиле ticket).
## Метрики / трейсинг
В текущем приложении **нет** endpoint для scrape `/metrics` Prometheus и
**нет** встроенного экспортера OpenTelemetry. Заметки в архитектурной
документации, где они упоминаются, описывают целевой дизайн, а не
реально поставляемую телеметрию.
Не путайте пути vSphere REST под `/api/vcenter/activity-history` или
seeded-эндпоинты health/timesync appliance с телеметрией самого процесса
симулятора — эти обработчики симулируют состояние appliance vCenter внутри
PostgreSQL, а не собственные метрики этого процесса.
## Evidence совместимости
Отчёты о совместимости в эксплуатации:
- `/ui/api/compatibility?major=N`
Также доступны через панель совместимости Web UI. См.
[Совместимость](compatibility.md).
+151
View File
@@ -0,0 +1,151 @@
**Language / Язык:** [English](../operations.md) | [Русский](operations.md)
# Эксплуатация
## Команды day-2
```bash
make up # запуск стека
make down # остановка стека
make restart
make logs
make dev # foreground-workflow, ориентированный на reload
make db-migrate # идемпотентные миграции
make seed # атомарный reseed (SEED_VSPHERE_PROFILE=large по умолчанию)
make shell # интерактивный контейнер с инструментами
```
## Миграции
Упорядоченные SQL-файлы применяются транзакционно и записывают контрольные
суммы SHA-256. Повторный запуск `make db-migrate` безопасен. Изменение уже
применённой миграции отклоняется. `/health/ready` остаётся недоступным, пока
не появится последняя упакованная миграция.
## Reseed
```bash
make seed # large (по умолчанию)
VSPHERE_PROFILE=small make seed
VSPHERE_PROFILE=demo-cluster make seed
```
Reseed атомарно заменяет инвентарь в PostgreSQL. Состояние внешней
автоматизации (файлы состояния Terraform, стеки Pulumi, инвентари Ansible,
кодирующие MOID/имена ВМ) может после этого разойтись — обновите или
пересоздайте эти внешние каналы. См. [Профили seed](seed-profiles.md).
## Восстановление workers
CIS task workers используют аренды PostgreSQL (`FOR UPDATE SKIP LOCKED`).
После сбоя или перезапуска просроченные аренды переиспользуются, и
незавершённая работа безопасно возобновляется. Настраиваемые параметры:
`TASK_WORKER_CONCURRENCY`, `TASK_LEASE_SECONDS`, `SIMULATION_TIME_SCALE`.
## Изменение мажора каталога по умолчанию
1. Мажор каталога по умолчанию — **9** (поверхность 8.0 U2 / Automation 9.1)
при холодном старте; это не ограничивает таблицу маршрутов runtime (см.
[Версии API](api-versions.md)).
2. Используйте «Apply as runtime» в Web UI или
`POST /ui/api/contract/apply?major=N`, чтобы переключить мажор каталога
локально для процесса, для целей просмотра/evidence.
## Резервное копирование состояния лаборатории
PostgreSQL — это система записи (system of record). Используйте обычные
backup/restore для Postgres (`pg_dump` / снимки томов), если нужно сохранить
seeded-лабораторию. Контейнеры приложения одноразовые, пока сохраняется том
базы данных.
## Публикация в Docker Hub
`make release` собирает образ **runtime** (build target `runtime`, а не
локальный bind-mounted образ `dev`) и публикует его в Docker Hub:
```bash
docker login # один раз; учётная запись должна владеть или иметь права push в DOCKERHUB_USER
make release
```
Значения по умолчанию:
| Переменная | По умолчанию | Значение |
|---|---|---|
| `DOCKERHUB_USER` | `inecs` | Namespace/организация Docker Hub |
| `IMAGE_NAME` | `vmware-api-simulator` | Имя репозитория |
| `VERSION` | из `pyproject.toml` | Тег образа |
| `PUSH_LATEST` | `1` | Также помечать/публиковать `:latest` |
Примеры:
```bash
make release
make release VERSION=0.2.0
make release DOCKERHUB_USER=myorg PUSH_LATEST=0
make release-build # локальная сборка/тегирование без публикации
```
Опубликованные теги:
- `inecs/vmware-api-simulator:<version>`
- `inecs/vmware-api-simulator:latest` (если не задано `PUSH_LATEST=0`)
## Быстрый старт с опубликованным compose-файлом
[`docker-compose.release.yml`](../../docker-compose.release.yml) скачивает
runtime-образ из Hub и запускает PostgreSQL + migrate + симулятор + HTTPS
gateway:
```bash
docker compose -f docker-compose.release.yml up -d
docker compose -f docker-compose.release.yml run --rm --entrypoint python \
simulator -m app.simulation.seed_cli
curl -sk https://localhost/health/ready
open https://localhost/
```
Вспомогательные команды из git checkout:
```bash
make release-up
make release-seed PROFILE=small
make release-down
```
Полезные переопределения:
| Переменная | По умолчанию | Значение |
|---|---|---|
| `DOCKER_IMAGE` | `inecs/vmware-api-simulator` | Репозиторий образа |
| `IMAGE_TAG` | `latest` | Тег для скачивания |
| `HTTP_PORT` | `80` | Порт хоста для HTTP |
| `HTTPS_PORT` | `443` | Порт хоста для HTTPS |
| `POSTGRES_PORT` | `127.0.0.1:5434` | Bind хоста для Postgres |
| `TICKET_SIGNING_KEY` | лабораторное значение по умолчанию | Меняйте вне игрушечных лабораторий |
| `POSTGRES_PASSWORD` | `vmware` | Пароль БД |
Для Kubernetes с публичным TLS (cert-manager / Let's Encrypt) используйте
Helm-чарт — см. [Kubernetes / Helm](kubernetes.md).
## Обновления
1. Скачайте / пересоберите образы (`make install` / `make docker-build` по
ситуации).
2. Выполните миграции (`make db-migrate`).
3. Убедитесь, что `/health/ready` отвечает нормально.
4. Перепроверьте `/ui/api/compatibility?major=9` и
`/api/appliance/system/version`.
5. При необходимости заново запустите `make test-vsphere` /
`make vsphere-matrix`, если проверяете поверхность после обновления.
## Сброс лаборатории
```bash
make seed PROFILE=small
# или через UI: unload demo → small, затем снова seed
```
Для жёсткого сброса базы данных используйте `make db-reset` (деструктивно —
см. справку Makefile).
+50
View File
@@ -0,0 +1,50 @@
**Language / Язык:** [English](../ports.md) | [Русский](ports.md)
# Порты vCenter в этом симуляторе
Справочник: [vSphere Networking Ports](https://ports.esp.vmware.com/) (vCenter Server).
`api-gateway` (nginx) публикует **основной HTTPS-listener vCenter** плюс
HTTP-грань для лабораторных нужд. Каждый опубликованный порт проксирует к
одному и тому же процессу FastAPI, который сам маршрутизирует по path REST
(`/api`, `/rest`) и SOAP (`/sdk`) — отдельного порта на протокол нет. Gateway
также выставляет `X-VMware-Service` / `X-Forwarded-Port`, чтобы клиенты и
будущие роутеры могли определить, какой порт был использован.
## Опубликовано через Compose (`api-gateway`)
| Сервис | Порт контейнера | Порт хоста (dev compose) |
|---|---:|---:|
| HTTP-грань для лабораторных нужд | 80 | 80 |
| vCenter HTTPS (основная точка входа UI/API) | 443 | 443 |
Порты хоста совпадают с реальными defaults vCenter, чтобы удалённые клиенты
ходили на `https://<host>/` и `http://<host>/` без нестандартного порта.
При необходимости переопределяйте в release compose через `HTTP_PORT` /
`HTTPS_PORT`.
Также публикуется Compose (не через gateway):
| Сервис | Порт хоста (dev compose) |
|---|---:|
| PostgreSQL | `5434` (только localhost) |
Внутренний процесс симулятора (не публикуется на хост): `8080`.
## Раскладка путей на HTTPS
| Поверхность | Префикс пути | Статус |
|---|---|---|
| vSphere REST | `/api/…`, `/rest/…` | реализовано (базовый инвентарь + сессия) |
| SOAP / VIM SDK | `/sdk` | реализовано (подмножество RetrieveServiceContent / Login / RetrieveProperties) |
| HttpNfcLease / NFC | `/nfc/…` | lab transfer handshake на том же HTTPS-слушателе |
| Лабораторная консоль | `/` | да |
| Health | `/health/live`, `/health/ready` | да |
## Задокументировано, но пока не опубликовано
| Сервис | Порты |
|---|---|
| VAMI / управление appliance | 5480 |
| Управление хостом ESXi (если будет симулировано позже) | 443 (отдельный хост) |
| Syslog / прочее | разное |
+57
View File
@@ -0,0 +1,57 @@
**Language / Язык:** [English](../security.md) | [Русский](security.md)
# Безопасность
## Модель угроз лаборатории
Этот проект — **локальный / CI лабораторный симулятор**. Он не защищён как
multi-tenant публичный сервис vCenter. Учётные данные по умолчанию, демо-
элементы управления в UI и endpoint'ы совместимости удобны для разработки и
намеренно открыты в стандартном стеке Compose.
Не публикуйте порты `443` / `80` в недоверенные сети без дополнительных
средств защиты, которые вы предоставляете самостоятельно.
## Учётные данные и секреты
- Пароли хранятся как хэши scrypt (`vsphere_credentials.password_hash`).
- Session id — это непрозрачные токены (`vmware-api-session-id`) со
скользящим сроком действия 2 часа, отслеживаемые в PostgreSQL
(`vsphere_sessions`).
- Логи маскируют распознанные представления session id и паролей.
- Ответы сессий обновления/загрузки content library раскрывают только
endpoint'ы загрузки/скачивания, а не сырые секреты.
Меняйте `TICKET_SIGNING_KEY` для любой общей лаборатории. Заменяйте seeded-
пароли перед демонстрацией другим людям.
## Материалы TLS
`docker/tls/` содержит закоммиченный self-signed сертификат для локального
сервиса nginx `api-gateway`. Он существует, чтобы немодифицированные
TLS-клиенты (pyvmomi, govmomi, провайдер Terraform `hashicorp/vsphere`) могли
подключаться с установленным `insecure`/`verify=False`. **Никогда** не
используйте эти файлы повторно в production.
## Администрирование симулятора
На данный момент **нет** отдельно аутентифицируемой административной
control plane. Вспомогательные маршруты Web UI под `/ui/api/*` доступны,
когда процесс достижим по сети, — включая действия reseed и hot-swap.
Считайте сетевую доступность границей доверия.
## Авторизация
Мутирующие REST-эндпоинты проверяют привилегии, производные от роли
(`app/vsphere/security/authz.py`), прежде чем обращаться к инвентарю.
Seeded-принципал `readonly@vsphere.local` не может включать/создавать/
удалять ВМ (HTTP 403). См. [Авторизация](domains/authz.md).
## Симулированные внешние системы
Заменители NSX/Supervisor/vSAN/SAML-OIDC/VECS-сертификатов (см.
[Покрытие API](api-coverage.md)) сохраняют только локальное состояние
симулятора. Они не открывают реальных соединений с внешними IdP, NSX
Manager или живым кластером vSAN. Не полагайтесь на симулятор для
тестирования защиты от эксфильтрации живых учётных данных против реальных
провайдеров.
+76
View File
@@ -0,0 +1,76 @@
**Language / Язык:** [English](../seed-profiles.md) | [Русский](seed-profiles.md)
# Профили seed
Seed **атомарно** заменяет инвентарь vSphere, используя детерминированные
MOID, чтобы лаборатории были воспроизводимыми. Определения находятся в
[`app/vsphere/profiles.py`](../../app/vsphere/profiles.py).
```bash
make seed # по умолчанию: large (10 хостов / 1000 ВМ)
VSPHERE_PROFILE=small make seed
```
## Профили
| Профиль | Содержимое |
|---|---|
| `small` | 3 хоста ESXi, 2 datastore, 2 сети, один datacenter/cluster/resource-pool и пять именованных ВМ: `web-01`, `web-02`, `db-01`, `app-01`, `jumpbox` (смешанные состояния питания). Используется unit/integration-тестами. |
| `large` (по умолчанию) | Настраиваемое число хостов/ВМ (`SEED_VSPHERE_LARGE_HOSTS` по умолчанию 10, `SEED_VSPHERE_LARGE_VMS` по умолчанию 1000), 4 datastore, 4 сети/portgroup, `VmwareDistributedVirtualSwitch`, папки ВМ production/staging/templates. Первые пять ВМ совпадают по именам с `small` для стабильности кулинарных книг; остальные генерируются (префиксы ролей `web-`, `app-`, `db-`, `cache-`, `batch-`, `jump-`, `ci-`, `mon-`, `log-`, `ml-`). |
| `demo-cluster` | `large` с 20 хостами / 1000 ВМ — набор данных в форме предприятия для демо UI. |
Каждый профиль также загружает четыре лабораторные учётные записи, права,
привязанные к ролям (см. [Авторизация](domains/authz.md)), и — там, где
существуют таблицы платформы — стартовую content library, категории/теги
тегирования и метаданные файлов datastore (`seed_platform_extras`).
## Примеры
```bash
make seed # large, 10 хостов / 1000 ВМ
VSPHERE_PROFILE=small make seed
VSPHERE_PROFILE=demo-cluster make seed
VSPHERE_PROFILE=large VSPHERE_HOSTS=20 VSPHERE_VMS=5000 make seed
```
Либо запустите CLI seed напрямую с базовыми переменными окружения (например,
из скрипта без `make` или на шаге CI):
```bash
SEED_VSPHERE_PROFILE=small \
docker compose run --rm --entrypoint python simulator -m app.simulation.seed_cli
```
## Форма топологии
Каждый профиль строит один и тот же скелет (папка `Datacenters`
`Datacenter` → подпапки host/vm/datastore/network → один
`ClusterComputeResource` + `ResourcePool`), затем масштабирует хосты,
datastore, portgroup и ВМ. MOID ВМ имеют вид `vm-{100+n}`; MOID хостов —
`host-{10+n}`; каждая ВМ несёт одинаковую форму оборудования, используемую
как REST (`hardware/*`), так и SOAP (`VirtualMachineConfigInfo`) ответами —
NIC, диски, CD-ROM, порядок загрузки и синтетический guest IP/файловая
система.
## Демо-кластер через UI
Интерактивная консоль может загружать демо-набор данных и делать reseed по
запросу:
- `POST /ui/api/demo/load` — загружает `demo-cluster`
- `POST /ui/api/demo/unload` — очищает состояние, созданное через API, затем
загружает `small`
- `GET /ui/api/demo/state`
- `POST /ui/api/vsphere/seed?profile=small|large|demo-cluster` — reseed
любого профиля
Эти вспомогательные эндпоинты UI ориентированы на разработку и сегодня не
имеют отдельной аутентификации. Считайте их только лабораторными органами
управления.
## Reseed в сравнении с состоянием клиентов
Terraform, Pulumi и Ansible могут по-прежнему хранить состояние ресурсов
после reseed (MOID и имена ВМ могут измениться). Выполните refresh или
destroy/recreate внешнего состояния после замены инвентаря PostgreSQL. См.
[Эксплуатация](operations.md) и [Клиенты](clients.md).
+75
View File
@@ -0,0 +1,75 @@
**Language / Язык:** [English](../troubleshooting.md) | [Русский](troubleshooting.md)
# Устранение неполадок
## Ready остаётся недоступным
1. Проверьте Postgres: `make logs` / health в Compose.
2. Выполните `make db-migrate`.
3. Снова вызовите `/health/ready`.
Task workers могут повторять попытки, пока миграции не догонят после позднего migrate.
## Неожиданный HTTP 501
У каждого зарегистрированного маршрута должен быть реальный обработчик или
DB-backed стаб — 501 не должен появляться для известного пути. Если вы его видите:
- Убедитесь, что вызываете точный зарегистрированный path/verb (проверьте
`app/vsphere/rest/coverage.py` или `/docs`).
- 501 от опционального legacy-стаба (`ENABLE_PVE_STUB=true`) ожидается для
необъявленных методов в стиле PVE, когда `CONTRACT_FALLBACK=error`; это
не относится к native vSphere-поверхности.
- Сообщите о регрессии — на native vSphere-плоскости ожидается полное
покрытие реестра.
## 401 / 403
- Сессия истекла (скользящий TTL 2 часа) или заголовок/cookie
`vmware-api-session-id` не отправлен.
- Некорректный Basic auth на `/api/session` (отсутствует заголовок, неверный
base64 от `user:password`).
- Отказ по правам — попробуйте сравнить `administrator@vsphere.local` и
`readonly@vsphere.local` (см. [Авторизация](domains/authz.md)).
## Задача никогда не завершается
- Изучите `/api/cis/tasks/{task}`.
- Проверьте логи worker/симулятора (`make logs`).
- Убедитесь, что `TASK_WORKER_CONCURRENCY` > 0 и аренды в базе данных можно
забрать (claim).
- Очень высокий `SIMULATION_TIME_SCALE` даёт необычные замедления (больше =
быстрее симуляция); чаще виноваты неверно заданные worker-аренды.
## Сбои TLS / gateway
- Используйте порт хоста **443** (gateway) для TLS-клиентов — pyvmomi,
govmomi, провайдер Terraform `hashicorp/vsphere`, Pulumi.
- Устанавливайте `verify_ssl=False` / `allow_unverified_ssl=true` **только**
для локального self-signed development-сертификата.
- Внутри Compose обращайтесь напрямую к `simulator:8080` (обычный HTTP, без
gateway).
- Seeded-имена ВМ для `small``web-01`, `web-02`, `db-01`, `app-01`,
`jumpbox`, а не Proxmox-style `pve01`/VMID.
## Drift Terraform / Pulumi / Ansible после reseed
Reseed заменяет инвентарь в PostgreSQL (MOID-ы и имена ВМ могут измениться);
состояние внешних инструментов автоматически не обновляется. Выполните
refresh, import или пересоберите стеки после `make seed`.
## Hot-swap «ничего не сделал»
- Просмотр каталога ≠ apply. Используйте **Apply as runtime** или
`POST /ui/api/contract/apply?major=N`.
- Применение мажора меняет **каталог Web UI / представление evidence**, а не
живую таблицу маршрутов — runtime всегда обслуживает полную
зарегистрированную поверхность. См. [Версии API](api-versions.md).
- Apply локален для процесса; перезапуск Compose возвращает к значению по
умолчанию (мажор 9).
## Demo unload удивил
`POST /ui/api/demo/unload` очищает состояние, созданное через API, и
загружает `small`. Повторите `make seed` (или снова загрузите
`demo-cluster`), чтобы восстановить более богатую фикстуру.
+68
View File
@@ -0,0 +1,68 @@
**Language / Язык:** [English](../web-ui.md) | [Русский](web-ui.md)
# Web UI
Откройте [https://localhost/](https://localhost/) после `make up`
(gateway).
Внутренний порт симулятора — `8080`; лабораторный UI также доступен на этом хосте.
UI — это лабораторная консоль для симулятора **vSphere**, а не замена
vSphere Client. Она поддерживает светлую и тёмную темы, мажоры каталога
**69** (уровни vSphere 7–8.0U2), редактирование запроса/ответа, историю и
применение runtime-контракта.
## Возможности
- Дерево endpoint'ов и селектор метода, управляемые выбранным мажором каталога
- Параметры и примеры payload, производные от контракта
- Редактор запроса, просмотрщик ответа и история
- Вход в сессию через `POST /api/session` (Basic) → заголовок/cookie
`vmware-api-session-id`
- Сводка окружения (версия runtime, хосты, ВМ, кластеры, datastore, сети)
- Предпросмотр запросов в виде curl
- Индикатор покрытия для реализованного реестра REST
- Hot-swap **Apply as runtime** для активного уровня мажора
- Загрузка demo / seed (профили large / demo-cluster)
- Компактная консоль на `/console.html`
- Ссылка на OpenAPI по адресу `/docs`
## Аутентификация (лаборатория)
| Пользователь | Пароль | Роль |
|---|---|---|
| `administrator@vsphere.local` | `VMware1!` | Administrator |
| `readonly@vsphere.local` | `VMware1!` | ReadOnly |
| `operator@vsphere.local` | `VMware1!` | VirtualMachinePowerUser |
| `vmadmin@vsphere.local` | `VMware1!` | VirtualMachineAdministrator |
После входа кнопка Send автоматически прикладывает `vmware-api-session-id`.
## Вспомогательные методы backend
| Метод | Path | Назначение |
|---|---|---|
| GET | `/ui/api/versions` | Мажоры каталога в сравнении с runtime |
| GET | `/ui/api/catalog?major=N` | Каталог для мажора 6–9 |
| GET | `/ui/api/method?...` | Метаданные одного метода |
| GET | `/ui/api/compatibility?major=N` | Payload покрытия |
| POST | `/ui/api/contract/apply?major=N` | Hot-swap runtime-контракта |
| GET | `/ui/api/demo/state` | Состояние демо-набора данных |
| POST | `/ui/api/demo/load` | Загрузить `demo-cluster` |
| POST | `/ui/api/vsphere/seed?profile=…` | Reseed `small` / `large` / `demo-cluster` |
## Workflow работы с версиями
1. Выберите мажор **6 / 7 / 8 / 9** в каталоге.
2. Изучите методы и покрытие.
3. Используйте **Apply as runtime**, когда нужно, чтобы живые маршруты были
ограничены уровнем этого мажора.
4. Подтвердите через `/api/appliance/system/version` и `/ui/api/compatibility`.
Hot-swap хранится только в памяти; перезапуск восстанавливает настройки по
умолчанию. Подробности: [Версии API](api-versions.md).
## Замечание о безопасности
Эндпоинты UI и demo предназначены для локальной разработки. В текущей
сборке они не защищены отдельным admin-токеном. Не выставляйте порт
симулятора в недоверенные сети.
+54
View File
@@ -0,0 +1,54 @@
**Language / Язык:** [English](security.md) | [Русский](ru/security.md)
# Security
## Lab threat model
This project is a **local / CI laboratory simulator**. It is not hardened as
a multi-tenant public vCenter service. Default credentials, UI demo controls,
and compatibility endpoints are convenient for development and intentionally
open in the default Compose stack.
Do not expose ports `443` / `80` to untrusted networks without additional
controls you supply yourself.
## Credentials and secrets
- Passwords are stored as scrypt hashes (`vsphere_credentials.password_hash`).
- Session ids are opaque tokens (`vmware-api-session-id`) with a 2-hour
sliding expiry, tracked in PostgreSQL (`vsphere_sessions`).
- Logs redact recognized session-id and password representations.
- Content library update/download session responses expose upload/download
endpoints, not raw secrets.
Change `TICKET_SIGNING_KEY` for any shared lab. Replace seeded passwords
before demoing to others.
## TLS materials
`docker/tls/` contains a checked-in self-signed certificate for the local
`api-gateway` nginx service. It exists so unmodified TLS clients (pyvmomi,
govmomi, Terraform's `hashicorp/vsphere` provider) can connect with
`insecure`/`verify=False` set. **Never** reuse these files in production.
## Simulator administration
There is currently **no** separately authenticated admin control plane. Web
UI helper routes under `/ui/api/*` are available whenever the process is
reachable — including reseed and hot-swap actions. Treat network exposure as
the trust boundary.
## Authorization
Mutating REST endpoints check role-derived privileges
(`app/vsphere/security/authz.py`) before touching inventory. The seeded
`readonly@vsphere.local` principal cannot power on/create/delete VMs (HTTP
403). See [Authorization](domains/authz.md).
## Simulated remotes
NSX/Supervisor/vSAN/SAML-OIDC/VECS-certificate stand-ins (see
[API coverage](api-coverage.md)) persist local simulator state only. They do
not open real connections to external IdPs, NSX Manager, or a live vSAN
cluster. Do not rely on the simulator for testing live credential
exfiltration defenses against real providers.
+70
View File
@@ -0,0 +1,70 @@
**Language / Язык:** [English](seed-profiles.md) | [Русский](ru/seed-profiles.md)
# Seed profiles
Seeds replace the vSphere inventory **atomically** using deterministic MOIDs
so labs are reproducible. Definitions live in
[`app/vsphere/profiles.py`](../app/vsphere/profiles.py).
```bash
make seed # default: large (10 hosts / 1000 VMs)
VSPHERE_PROFILE=small make seed
```
## Profiles
| Profile | Contents |
|---|---|
| `small` | 3 ESXi hosts, 2 datastores, 2 networks, one datacenter/cluster/resource-pool, and five named VMs: `web-01`, `web-02`, `db-01`, `app-01`, `jumpbox` (mixed power states). Used by unit/integration tests. |
| `large` (default) | Configurable hosts/VMs (`SEED_VSPHERE_LARGE_HOSTS` default 10, `SEED_VSPHERE_LARGE_VMS` default 1000), 4 datastores, 4 networks/portgroups, a `VmwareDistributedVirtualSwitch`, production/staging/templates VM folders. The first five VMs match the `small` names for cookbook stability; the rest are generated (`web-`, `app-`, `db-`, `cache-`, `batch-`, `jump-`, `ci-`, `mon-`, `log-`, `ml-` role prefixes). |
| `demo-cluster` | `large` with 20 hosts / 1000 VMs — an enterprise-shaped dataset for UI demos. |
Every profile also seeds the four lab credentials, role-scoped permissions
(see [Authorization](domains/authz.md)), and — where the platform tables
exist — a starter content library, tag categories/tags, and datastore file
metadata (`seed_platform_extras`).
## Examples
```bash
make seed # large, 10 hosts / 1000 VMs
VSPHERE_PROFILE=small make seed
VSPHERE_PROFILE=demo-cluster make seed
VSPHERE_PROFILE=large VSPHERE_HOSTS=20 VSPHERE_VMS=5000 make seed
```
Or run the seed CLI directly with the underlying environment variables (for
example from a non-`make` script or CI step):
```bash
SEED_VSPHERE_PROFILE=small \
docker compose run --rm --entrypoint python simulator -m app.simulation.seed_cli
```
## Topology shape
Every profile builds the same skeleton (`Datacenters` folder → `Datacenter`
host/vm/datastore/network sub-folders → one `ClusterComputeResource` +
`ResourcePool`), then scales hosts, datastores, portgroups, and VMs. VM MOIDs
are `vm-{100+n}`; host MOIDs are `host-{10+n}`; each VM carries the same
hardware shape used by both REST (`hardware/*`) and SOAP (`VirtualMachineConfigInfo`)
responses — NICs, disks, CD-ROM, boot order, and a synthetic guest IP/filesystem.
## Demo cluster via UI
The interactive console can load the demo dataset and reseed on demand:
- `POST /ui/api/demo/load` — loads `demo-cluster`
- `POST /ui/api/demo/unload` — wipes API-created state, then loads `small`
- `GET /ui/api/demo/state`
- `POST /ui/api/vsphere/seed?profile=small|large|demo-cluster` — reseed any profile
These UI helper endpoints are development-oriented and are not separately
authenticated today. Treat them as lab controls only.
## Reseed vs client state
Terraform, Pulumi, and Ansible may still hold resource state after a reseed
(VM MOIDs and names can change). Refresh or destroy/recreate external state
after replacing the PostgreSQL inventory. See [Operations](operations.md) and
[Clients](clients.md).
+71
View File
@@ -0,0 +1,71 @@
**Language / Язык:** [English](troubleshooting.md) | [Русский](ru/troubleshooting.md)
# Troubleshooting
## Ready stays unavailable
1. Confirm Postgres: `make logs` / Compose health.
2. Run `make db-migrate`.
3. Hit `/health/ready` again.
Task workers may retry until migrations catch up after a late migrate.
## Unexpected HTTP 501
Every registered route should have a real handler or DB-backed stub — 501
should not appear for a known path. If you see it:
- Confirm you are calling the exact registered path/verb (check
`app/vsphere/rest/coverage.py` or `/docs`).
- 501 from the optional legacy stub (`ENABLE_PVE_STUB=true`) is expected for
undeclared PVE-style methods when `CONTRACT_FALLBACK=error`; it is
unrelated to the vSphere surface.
- Report a regression — full registry coverage is expected on the native
vSphere plane.
## 401 / 403
- Session expired (2-hour sliding TTL) or `vmware-api-session-id` header/cookie
not sent.
- Basic auth malformed on `/api/session` (missing header, wrong
`user:password` base64).
- Privilege denial — try `administrator@vsphere.local` vs
`readonly@vsphere.local` to compare (see [Authorization](domains/authz.md)).
## Task never finishes
- Inspect `/api/cis/tasks/{task}`.
- Check worker/simulator logs (`make logs`).
- Verify `TASK_WORKER_CONCURRENCY` > 0 and database leases can be claimed.
- Extremely high `SIMULATION_TIME_SCALE` slowdowns are unusual (higher =
faster simulation); mis-set worker leases are more common culprits.
## TLS / gateway failures
- Use host port **443** (gateway) for TLS clients — pyvmomi, govmomi,
Terraform's `hashicorp/vsphere` provider, Pulumi.
- Set `verify_ssl=False` / `allow_unverified_ssl=true` **only** for the local
self-signed development certificate.
- Inside Compose, target `simulator:8080` directly (plain HTTP, no gateway).
- Seeded VM names for `small` are `web-01`, `web-02`, `db-01`, `app-01`,
`jumpbox` — not Proxmox-style `pve01`/VMIDs.
## Terraform / Pulumi / Ansible drift after reseed
Reseed replaces PostgreSQL inventory (MOIDs and VM names can change);
external tool state does not update automatically. Refresh, import, or
rebuild stacks after `make seed`.
## Hot-swap "did nothing"
- Catalog browse ≠ apply. Use **Apply as runtime** or
`POST /ui/api/contract/apply?major=N`.
- Applying a major changes the **Web UI catalog / evidence view**, not the
live route table — the runtime always serves the full registered surface.
See [API versions](api-versions.md).
- Apply is process-local; a Compose restart returns to the default (major 9).
## Demo unload surprised you
`POST /ui/api/demo/unload` clears API-created state and loads `small`. Re-run
`make seed` (or load `demo-cluster` again) to restore a richer fixture.
+64
View File
@@ -0,0 +1,64 @@
**Language / Язык:** [English](web-ui.md) | [Русский](ru/web-ui.md)
# Web UI
Open [https://localhost/](https://localhost/) after `make up` (gateway).
Internal simulator port is `8080`; the lab UI is also on that host.
The UI is a laboratory console for the **vSphere** simulator — not a vSphere Client
replacement. It supports light and dark themes, catalog majors **69** (vSphere 78.0U2
floors), request/response editing, history, and runtime contract apply.
## Features
- Endpoint tree and method selector driven by the selected catalog major
- Contract-derived parameters and example payloads
- Request editor, response viewer, and history
- Session login via `POST /api/session` (Basic) → `vmware-api-session-id` header/cookie
- Environment summary (runtime version, hosts, VMs, clusters, datastores, networks)
- Curl / request previews
- Coverage meter for the implemented REST registry
- **Apply as runtime** hot-swap for the active major floor
- Demo / seed load (large / demo-cluster profiles)
- Compact console at `/console.html`
- Link to OpenAPI at `/docs`
## Auth (lab)
| User | Password | Role |
|---|---|---|
| `administrator@vsphere.local` | `VMware1!` | Administrator |
| `readonly@vsphere.local` | `VMware1!` | ReadOnly |
| `operator@vsphere.local` | `VMware1!` | VirtualMachinePowerUser |
| `vmadmin@vsphere.local` | `VMware1!` | VirtualMachineAdministrator |
After sign-in, Send attaches `vmware-api-session-id` automatically.
## Backend helpers
| Method | Path | Purpose |
|---|---|---|
| GET | `/ui/api/versions` | Catalog majors vs runtime |
| GET | `/ui/api/catalog?major=N` | Catalog for major 69 |
| GET | `/ui/api/method?...` | Single method metadata |
| GET | `/ui/api/compatibility?major=N` | Coverage payload |
| POST | `/ui/api/contract/apply?major=N` | Hot-swap runtime contract |
| GET | `/ui/api/demo/state` | Demo dataset state |
| POST | `/ui/api/demo/load` | Load `demo-cluster` |
| POST | `/ui/api/vsphere/seed?profile=…` | Reseed `small` / `large` / `demo-cluster` |
## Version workflow
1. Pick major **6 / 7 / 8 / 9** in the catalog.
2. Inspect methods and coverage.
3. **Apply as runtime** when you want live routes gated to that majors floor.
4. Confirm with `/api/appliance/system/version` and `/ui/api/compatibility`.
Hot-swap is memory-only; restart restores the default settings. Details:
[API versions](api-versions.md).
## Security note
UI and demo endpoints are intended for local development. They are not gated by
a separate admin token in the current build. Do not expose the simulator port to
untrusted networks.