vcf-super-cli 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vcf_super_cli-0.4.0/CHANGELOG.md +125 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/PKG-INFO +3 -2
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/README.md +2 -1
- vcf_super_cli-0.4.0/docs/commands.md +112 -0
- vcf_super_cli-0.4.0/docs/design.md +89 -0
- vcf_super_cli-0.4.0/docs/index.md +46 -0
- vcf_super_cli-0.4.0/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.3-ergonomics-design.md +232 -0
- vcf_super_cli-0.4.0/docs/usage.md +101 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/pyproject.toml +15 -1
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_builder.py +1 -1
- vcf_super_cli-0.4.0/tests/test_complete.py +100 -0
- vcf_super_cli-0.4.0/tests/test_complete_cache.py +107 -0
- vcf_super_cli-0.4.0/tests/test_complete_dynamic.py +205 -0
- vcf_super_cli-0.4.0/tests/test_filter_pagination_cli.py +174 -0
- vcf_super_cli-0.4.0/tests/test_filters.py +66 -0
- vcf_super_cli-0.4.0/tests/test_paginate.py +54 -0
- vcf_super_cli-0.4.0/tests/test_pyvmomi_events.py +138 -0
- vcf_super_cli-0.4.0/tests/test_pyvmomi_inventory.py +96 -0
- vcf_super_cli-0.4.0/tests/test_pyvmomi_perf.py +138 -0
- vcf_super_cli-0.4.0/tests/test_pyvmomi_tasks.py +62 -0
- vcf_super_cli-0.4.0/tests/test_resources.py +115 -0
- vcf_super_cli-0.4.0/tests/test_vmomi.py +131 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/uv.lock +1 -1
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/_version.py +1 -1
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/app.py +14 -1
- vcf_super_cli-0.4.0/vsc/connect/vmomi.py +102 -0
- vcf_super_cli-0.4.0/vsc/gen/builder.py +434 -0
- vcf_super_cli-0.4.0/vsc/gen/complete.py +62 -0
- vcf_super_cli-0.4.0/vsc/gen/complete_cache.py +128 -0
- vcf_super_cli-0.4.0/vsc/gen/complete_dynamic.py +145 -0
- vcf_super_cli-0.4.0/vsc/gen/filters.py +88 -0
- vcf_super_cli-0.4.0/vsc/gen/paginate.py +40 -0
- vcf_super_cli-0.4.0/vsc/gen/resources.py +135 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/errors.py +26 -0
- vcf_super_cli-0.4.0/vsc/pyvmomi/__init__.py +7 -0
- vcf_super_cli-0.4.0/vsc/pyvmomi/events.py +98 -0
- vcf_super_cli-0.4.0/vsc/pyvmomi/inventory.py +91 -0
- vcf_super_cli-0.4.0/vsc/pyvmomi/perf.py +118 -0
- vcf_super_cli-0.4.0/vsc/pyvmomi/runner.py +51 -0
- vcf_super_cli-0.4.0/vsc/pyvmomi/tasks.py +41 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/skill/assets/SKILL.md +17 -0
- vcf_super_cli-0.2.0/CHANGELOG.md +0 -60
- vcf_super_cli-0.2.0/docs/commands.md +0 -64
- vcf_super_cli-0.2.0/docs/design.md +0 -31
- vcf_super_cli-0.2.0/docs/index.md +0 -38
- vcf_super_cli-0.2.0/docs/usage.md +0 -55
- vcf_super_cli-0.2.0/vsc/gen/builder.py +0 -243
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/docs.yml +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/e2e.yml +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/lint.yml +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/release.yml +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/test.yml +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.gitignore +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/AGENTS.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/LICENSE +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/install.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/profiles.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/superpowers/specs/2026-06-05-vcf-super-cli-design.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.2-writes-design.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/writes.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/mkdocs.yml +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/README.md +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/conftest.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/test_read_smoke.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_cli.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_config.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_discover.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_errors.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_params.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_preview.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_render.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_session.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_skill.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_version.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_write_errors.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/profiles.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/skill.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/config/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/config/schema.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/config/store.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/connect/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/connect/session.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/connect/targets.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/discover.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/model.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/params.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/preview.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/logging_config.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/exit_codes.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/render.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/skill/__init__.py +0 -0
- {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/skill/export.py +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `vcf-super-cli` are documented here. The format is based
|
|
4
|
+
on [Keep a Changelog](https://keepachangelog.com/), and from v1.0.0 the project
|
|
5
|
+
will follow [Semantic Versioning](https://semver.org/). While on `0.x`, minor
|
|
6
|
+
versions may include breaking changes.
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## v0.4.0 — 2026-06-08
|
|
11
|
+
|
|
12
|
+
Live resource-id completion. Pressing `<TAB>` on an id-typed argument can now
|
|
13
|
+
suggest **real ids** from the live inventory — strictly opt-in, cached, and
|
|
14
|
+
fail-soft. The agent-facing contract is unchanged: stable JSON, error envelope,
|
|
15
|
+
exit codes, dry-run-by-default writes, and `--help` stays fully offline.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Live resource-id completion** (#44), opt-in via `VSC_COMPLETE_DYNAMIC=1` and
|
|
20
|
+
off by default. When enabled, `<TAB>` on an id arg/option suggests live ids for
|
|
21
|
+
the resource type (VMs, hosts, clusters, datacenters, datastores, resource
|
|
22
|
+
pools), with each resource's name shown as completion help. Built on:
|
|
23
|
+
- a resource-type → list-op **registry** derived purely by introspecting the
|
|
24
|
+
SDK metadata (#45), no network;
|
|
25
|
+
- a **TTL cache** under the platform cache dir, keyed by
|
|
26
|
+
profile/backend/resource-type (default 60s, `VSC_COMPLETE_TTL` override) (#46);
|
|
27
|
+
- a **dynamic completer** whose fetch is time-bounded (`VSC_COMPLETE_TIMEOUT`,
|
|
28
|
+
default 2s) and blanket fail-soft — any error, missing auth, or timeout
|
|
29
|
+
yields no suggestions and is never cached (#47).
|
|
30
|
+
- Static/offline completion (enums, output formats, profiles, filter enums) is
|
|
31
|
+
unchanged and remains the default. `--help` and command execution never open a
|
|
32
|
+
connection for completion.
|
|
33
|
+
|
|
34
|
+
### Notes
|
|
35
|
+
|
|
36
|
+
- Live completion is a human convenience only; agents should keep using `list`
|
|
37
|
+
to discover ids and must not depend on completion for correctness (#48).
|
|
38
|
+
|
|
39
|
+
## v0.3.0 — 2026-06-05
|
|
40
|
+
|
|
41
|
+
Ergonomics: friendlier filtering and paging, offline shell completion, and a
|
|
42
|
+
pyVmomi fallback surface for gaps the REST/vAPI layer doesn't cover. The
|
|
43
|
+
agent-facing contract is unchanged — stable JSON, error envelope, exit codes,
|
|
44
|
+
and writes still dry-run by default.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- **Static shell completion** (#32), fully offline — never opens a connection.
|
|
49
|
+
Completes enum option choices, output formats, configured profile names, and
|
|
50
|
+
`list` filter enum values. Install with `vsc --install-completion`. (Live
|
|
51
|
+
resource-id completion is intentionally deferred to a later release.)
|
|
52
|
+
- **Per-field filter flags** (#33). `list` commands flatten the SDK filter spec
|
|
53
|
+
into typed `--<field>` options (repeatable for list/set fields; enum choices
|
|
54
|
+
validated and completed), e.g. `vsc vsphere vm list --power-states POWERED_ON
|
|
55
|
+
--names web-1`. The raw `--filter '<json>'` blob stays as a base layer that
|
|
56
|
+
per-field flags override.
|
|
57
|
+
- **Pagination helpers** (#33): `--all` follows the NSX cursor across pages;
|
|
58
|
+
`--max-items N` caps the total; `--limit N` is a client-side cap for
|
|
59
|
+
non-paginated (vSphere) lists. Without `--all`, a paginated `list` returns one
|
|
60
|
+
page and surfaces its `cursor` for manual paging.
|
|
61
|
+
- **pyVmomi fallback commands** (read-only) under `vsc vsphere`, for areas the
|
|
62
|
+
REST/vAPI surface lacks — same JSON / error-envelope / exit-code contract:
|
|
63
|
+
- `perf vm|host --metric <group.name>` — performance counters via the
|
|
64
|
+
PerformanceManager (#34).
|
|
65
|
+
- `events list [--vm|--host] [--since 1h]` and `tasks list` — recent events
|
|
66
|
+
and recent/running tasks (#35).
|
|
67
|
+
- `inventory vm|host [--props <path>]…` — a PropertyCollector property walk
|
|
68
|
+
(#36).
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- Documentation and the bundled agent `SKILL.md` describe completion, the filter
|
|
73
|
+
and paging flags, and the pyVmomi fallback surface.
|
|
74
|
+
|
|
75
|
+
## v0.2.0 — 2026-06-05
|
|
76
|
+
|
|
77
|
+
First PyPI release. Adds the **write** surface on top of the v0.1 read-only
|
|
78
|
+
foundation, with a dry-run-by-default safety model.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- **Write operations** (`POST`/`PUT`/`PATCH`/`DELETE`) are generated from the
|
|
83
|
+
`vcf-sdk` vAPI metadata alongside reads (#20). Clean, collision-free verbs:
|
|
84
|
+
`POST ?action=X` → `X`; otherwise `PUT` → `set`, `PATCH` → `patch`,
|
|
85
|
+
`DELETE` → `delete` (force variants prefixed `force-`); `POST` without an
|
|
86
|
+
action keeps its operation id.
|
|
87
|
+
- **Dry-run by default + `--apply`** (#21). Every write previews the resolved
|
|
88
|
+
request and changes nothing — and opens **no connection** — unless `--apply`
|
|
89
|
+
is passed. `--apply` is the only gate (no interactive prompt), so the CLI
|
|
90
|
+
stays fully scriptable. New `vsc/gen/preview.py` builds the request plan.
|
|
91
|
+
- **Stable write envelope**, identical across modes — branch on `applied`:
|
|
92
|
+
- dry-run: `{ "applied": false, "request": { method, url, path_params, query, body, … }, "apply_hint": … }`
|
|
93
|
+
- applied: `{ "applied": true, "request": { … }, "result": <sdk response> }`
|
|
94
|
+
- **Body/spec construction from JSON** for both conventions: NSX's named request
|
|
95
|
+
body and vCenter's spec parameters, built into the SDK binding structs.
|
|
96
|
+
- **Catalog expansion**: vSphere `resource-pool` and the VM power/hardware leaves
|
|
97
|
+
(`power`, `cpu`, `memory`, `disk`, `ethernet`); NSX `ip-pools`,
|
|
98
|
+
`dhcp-server-configs`, `dhcp-relay-configs`, and Tier-1 `locale-services`.
|
|
99
|
+
- **`CONFLICT` (7) / `UNAVAILABLE` (8)** exit-code coverage for write errors
|
|
100
|
+
(#22), derived from the error-type map so it can't drift.
|
|
101
|
+
|
|
102
|
+
### Changed
|
|
103
|
+
|
|
104
|
+
- A malformed or incomplete write body is reported during the dry-run as the
|
|
105
|
+
structured usage envelope (exit `2`) — before any connection — not as a
|
|
106
|
+
traceback.
|
|
107
|
+
- Documentation and the bundled agent `SKILL.md` describe the write surface and
|
|
108
|
+
the `--apply` safety model (#23).
|
|
109
|
+
|
|
110
|
+
## v0.1.0 — 2026-06-05
|
|
111
|
+
|
|
112
|
+
Initial read-only foundation (not published to PyPI).
|
|
113
|
+
|
|
114
|
+
### Added
|
|
115
|
+
|
|
116
|
+
- Command tree **generated dynamically** by introspecting the installed `vcf-sdk`
|
|
117
|
+
vAPI bindings (`VapiInterface` services + `OperationRestMetadata`), fully
|
|
118
|
+
offline — `--help` works without a server or credentials.
|
|
119
|
+
- Read (`GET`) commands for vSphere/vCenter (`vsc vsphere …`) and NSX Policy
|
|
120
|
+
(`vsc nsx …`), split into two product groups.
|
|
121
|
+
- Named **profiles** with env-var overrides and optional OS-keyring secret
|
|
122
|
+
storage; `--profile/-p` selection.
|
|
123
|
+
- **Output contract**: JSON by default, `--output table` for humans; a stable
|
|
124
|
+
error envelope on stderr and documented, frozen **exit codes**.
|
|
125
|
+
- Bundled agent **Skill** shipped in the wheel, exportable via `vsc skill export`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vcf-super-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Modern, agent-friendly CLI for VMware Cloud Foundation 9, with a command tree generated dynamically from the vcf-sdk vAPI bindings.
|
|
5
5
|
Project-URL: Homepage, https://github.com/thomaschristory/vcf-super-cli
|
|
6
6
|
Project-URL: Documentation, https://thomaschristory.github.io/vcf-super-cli/
|
|
@@ -73,7 +73,8 @@ $ vsc vsphere power stop vm-42 --apply # writes are dry-run without --app
|
|
|
73
73
|
| vSphere / vCenter read (`vsc vsphere …`) | ✅ v0.1 |
|
|
74
74
|
| NSX **Policy API** read (`vsc nsx …`) | ✅ v0.1 |
|
|
75
75
|
| Writes — dry-run by default + `--apply` (`vsc vsphere …` / `vsc nsx …`) | ✅ v0.2 |
|
|
76
|
-
|
|
|
76
|
+
| Ergonomics — offline shell completion, per-field filter flags + paging, pyVmomi fallback (`perf`/`events`/`tasks`/`inventory`) | ✅ v0.3 |
|
|
77
|
+
| Live resource-id completion | planned |
|
|
77
78
|
| NSX Manager / Global-Manager, SDDC Manager, Operations, LCM | deferred |
|
|
78
79
|
|
|
79
80
|
## Install
|
|
@@ -41,7 +41,8 @@ $ vsc vsphere power stop vm-42 --apply # writes are dry-run without --app
|
|
|
41
41
|
| vSphere / vCenter read (`vsc vsphere …`) | ✅ v0.1 |
|
|
42
42
|
| NSX **Policy API** read (`vsc nsx …`) | ✅ v0.1 |
|
|
43
43
|
| Writes — dry-run by default + `--apply` (`vsc vsphere …` / `vsc nsx …`) | ✅ v0.2 |
|
|
44
|
-
|
|
|
44
|
+
| Ergonomics — offline shell completion, per-field filter flags + paging, pyVmomi fallback (`perf`/`events`/`tasks`/`inventory`) | ✅ v0.3 |
|
|
45
|
+
| Live resource-id completion | planned |
|
|
45
46
|
| NSX Manager / Global-Manager, SDDC Manager, Operations, LCM | deferred |
|
|
46
47
|
|
|
47
48
|
## Install
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Commands
|
|
2
|
+
|
|
3
|
+
The `vsphere` and `nsx` command trees are **generated from the `vcf-sdk` vAPI
|
|
4
|
+
bindings**, so `vsc --help` (and each sub-`--help`) is always the authoritative,
|
|
5
|
+
version-accurate reference. This page is an overview.
|
|
6
|
+
|
|
7
|
+
Generated leaves expose both **read** and **write** verbs:
|
|
8
|
+
|
|
9
|
+
- `list` — list resources (optional `--filter '<json>'`)
|
|
10
|
+
- `get <id>` — fetch one resource by id, **where the SDK provides a by-id GET**
|
|
11
|
+
(e.g. `vm`, `cluster`, `datacenter`, `datastore`; some leaves such as `host`,
|
|
12
|
+
`folder`, and `network` are `list`-only)
|
|
13
|
+
- writes — `create`, `delete`, `set` (PUT upsert), `patch`, and action verbs
|
|
14
|
+
(e.g. `start`/`stop`/`reset` under `power`). **Writes are dry-run by default and
|
|
15
|
+
require `--apply`** — see [Writes](writes.md).
|
|
16
|
+
|
|
17
|
+
## `vsc vsphere …` (vCenter)
|
|
18
|
+
|
|
19
|
+
Generated from `com.vmware.vcenter`:
|
|
20
|
+
|
|
21
|
+
| Group | Examples |
|
|
22
|
+
|-------|----------|
|
|
23
|
+
| `vm` | `vsc vsphere vm list`, `vsc vsphere vm get <vm>`, `vsc vsphere vm delete <vm> --apply` |
|
|
24
|
+
| `host` | `vsc vsphere host list`, `vsc vsphere host disconnect <host> --apply` |
|
|
25
|
+
| `cluster` | `vsc vsphere cluster list` |
|
|
26
|
+
| `datacenter` | `vsc vsphere datacenter list` |
|
|
27
|
+
| `datastore` | `vsc vsphere datastore list` |
|
|
28
|
+
| `folder` | `vsc vsphere folder list` |
|
|
29
|
+
| `network` | `vsc vsphere network list` |
|
|
30
|
+
| `resource-pool` | `vsc vsphere resource-pool create --spec '<json>' --apply` |
|
|
31
|
+
| `power` | `vsc vsphere power start\|stop\|reset\|suspend <vm> --apply` |
|
|
32
|
+
| `cpu` / `memory` / `disk` / `ethernet` | VM hardware reads + writes, e.g. `vsc vsphere cpu update <vm> --spec '<json>' --apply` |
|
|
33
|
+
| `perf` / `events` / `tasks` / `inventory` | **pyVmomi fallback** (read-only) — perf counters, recent events, recent/running tasks, and a property walk the REST/vAPI surface lacks (see below) |
|
|
34
|
+
|
|
35
|
+
### pyVmomi fallback commands
|
|
36
|
+
|
|
37
|
+
A few inventory/performance areas are only reachable over the older pyVmomi SOAP
|
|
38
|
+
API. Those commands live under `vsc vsphere` alongside the generated ones, are
|
|
39
|
+
**read-only** (no `--apply`), and emit the same JSON / error envelope / exit codes.
|
|
40
|
+
|
|
41
|
+
- `vsc vsphere perf vm <vm> [--metric group.name]… [--max-samples N]` — performance
|
|
42
|
+
counters via the PerformanceManager. Metrics are `group.name` (e.g. `cpu.usage`)
|
|
43
|
+
or `group.name.rollup` (e.g. `cpu.usage.average`); repeat `--metric` for several.
|
|
44
|
+
- `vsc vsphere perf host <host> …` — the same for an ESXi host.
|
|
45
|
+
- `vsc vsphere events list [--vm <vm> | --host <host>] [--since 1h] [--max-count N]`
|
|
46
|
+
— recent events via the EventManager. `--since` takes a duration (`30s`/`15m`/
|
|
47
|
+
`2h`/`1d`); scope to at most one entity.
|
|
48
|
+
- `vsc vsphere tasks list [--max-count N]` — recent and running tasks via the
|
|
49
|
+
TaskManager.
|
|
50
|
+
- `vsc vsphere inventory vm <vm> [--props path]…` / `inventory host <host> …` —
|
|
51
|
+
managed-object properties the REST list ops omit (device tree, custom attributes),
|
|
52
|
+
via the PropertyCollector. `--props` is repeatable (e.g. `--props config.hardware`);
|
|
53
|
+
with none, a small per-type summary set is returned.
|
|
54
|
+
|
|
55
|
+
## `vsc nsx …` (NSX Policy)
|
|
56
|
+
|
|
57
|
+
Generated from `vcf.nsx.policy`:
|
|
58
|
+
|
|
59
|
+
| Group | Examples |
|
|
60
|
+
|-------|----------|
|
|
61
|
+
| `segments` | `vsc nsx segments list`, `vsc nsx segments set <id> --segment '<json>' --apply` |
|
|
62
|
+
| `tier0s` / `tier1s` | `vsc nsx tier1s list`, `vsc nsx tier1s set <id> --tier1 '<json>' --apply` |
|
|
63
|
+
| `services` | `vsc nsx services list` |
|
|
64
|
+
| `groups` | `vsc nsx groups list`, `vsc nsx groups delete <domain> <group> --apply` |
|
|
65
|
+
| `security-policies` | `vsc nsx security-policies list` |
|
|
66
|
+
| `gateway-policies` | `vsc nsx gateway-policies list` |
|
|
67
|
+
| `ip-pools` | `vsc nsx ip-pools set <id> --ip-address-pool '<json>' --apply` |
|
|
68
|
+
| `dhcp-server-configs` / `dhcp-relay-configs` | DHCP config reads + writes |
|
|
69
|
+
| `locale-services` | Tier-1 locale services reads + writes |
|
|
70
|
+
|
|
71
|
+
## Curated commands
|
|
72
|
+
|
|
73
|
+
- `vsc profiles …` — manage connection profiles (see [Profiles](profiles.md))
|
|
74
|
+
- `vsc skill export <dir> [--apply]` — export the bundled agent Skill
|
|
75
|
+
- `vsc --version`
|
|
76
|
+
|
|
77
|
+
## Filtering
|
|
78
|
+
|
|
79
|
+
`list` commands expose each field of the SDK filter spec as its own typed flag.
|
|
80
|
+
List-valued fields are repeatable; enum fields validate their choices and
|
|
81
|
+
tab-complete:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
vsc --profile prod vsphere vm list --power-states POWERED_ON --names web-1 --names web-2
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The raw JSON spec is still accepted as a base layer / escape hatch; per-field
|
|
88
|
+
flags merge **over** it (a flag wins over the same key in the blob):
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
vsc --profile prod vsphere vm list --filter '{"clusters": ["domain-c1"]}' --power-states POWERED_ON
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Pagination
|
|
95
|
+
|
|
96
|
+
`list` commands accept paging flags:
|
|
97
|
+
|
|
98
|
+
| Flag | Effect |
|
|
99
|
+
|------|--------|
|
|
100
|
+
| `--all` | follow the cursor and return **every** page (paginated backends, e.g. NSX) |
|
|
101
|
+
| `--max-items N` | cap the total number of items returned |
|
|
102
|
+
| `--limit N` | client-side cap for non-paginated (vSphere) lists |
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
vsc --profile prod nsx segments list --all # every page, concatenated
|
|
106
|
+
vsc --profile prod nsx segments list --page-size 50 # one page (cursor surfaced for manual paging)
|
|
107
|
+
vsc --profile prod vsphere vm list --limit 20 # first 20 only
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Without `--all`, a paginated `list` returns one page and surfaces the `cursor` so
|
|
111
|
+
an agent can drive pagination itself. On non-paginated backends (vSphere) `--all`
|
|
112
|
+
is a no-op — the output stays a plain array.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Design
|
|
2
|
+
|
|
3
|
+
`vsc` builds its command tree by **statically introspecting the installed
|
|
4
|
+
`vcf-sdk` vAPI bindings**. Both `vmware-vcenter` and `vcf-nsx` are vAPI stub
|
|
5
|
+
libraries on the shared `vmware-vapi-runtime`:
|
|
6
|
+
|
|
7
|
+
- Each service is a `VapiInterface` subclass (`VM`, `Host`, `Cluster`,
|
|
8
|
+
`Datastore`, `Network`, …).
|
|
9
|
+
- Every operation carries `OperationRestMetadata` (`http_method`,
|
|
10
|
+
`path_variables`, `query_parameters`).
|
|
11
|
+
- Parameters are typed via `StructType` binding types.
|
|
12
|
+
|
|
13
|
+
A single generator walks those classes and emits one Typer command per
|
|
14
|
+
operation. `http_method == "GET"` ⇒ a read command; other verbs ⇒ writes
|
|
15
|
+
(dry-run by default + `--apply`). Because the metadata ships inside the SDK, the
|
|
16
|
+
tree builds **offline** — no server needed to render `--help`.
|
|
17
|
+
|
|
18
|
+
Top-level grouping:
|
|
19
|
+
|
|
20
|
+
- `vsc vsphere …` → `com.vmware.vcenter` (+ selected `com.vmware.esx`)
|
|
21
|
+
- `vsc nsx …` → `vcf.nsx.policy` (NSX Policy API)
|
|
22
|
+
|
|
23
|
+
The introspected intermediate representation is a list of `Operation` objects,
|
|
24
|
+
each carrying typed `Param`s (`vsc/gen/model.py`). Everything below is built from
|
|
25
|
+
that one model.
|
|
26
|
+
|
|
27
|
+
## Ergonomics (v0.3)
|
|
28
|
+
|
|
29
|
+
These build on the introspected `Param` model without changing the agent
|
|
30
|
+
contract.
|
|
31
|
+
|
|
32
|
+
### Offline shell completion
|
|
33
|
+
|
|
34
|
+
Completion values come entirely from the model and local config — **never a
|
|
35
|
+
network call**, so `<TAB>` stays fast and `--help` stays offline. Enum options
|
|
36
|
+
complete from their fixed choices, `--output` from the output formats, and
|
|
37
|
+
`--profile` from configured profile names (`vsc/gen/complete.py`). Completing a
|
|
38
|
+
live resource id would require a connection and is deferred to a later release.
|
|
39
|
+
|
|
40
|
+
### Per-field filter flags
|
|
41
|
+
|
|
42
|
+
A `list` operation takes a single `filter` parameter that is a struct
|
|
43
|
+
(`VM.FilterSpec` etc.). The generator flattens that struct into typed
|
|
44
|
+
`--<field>` options (repeatable for list/set fields; enum fields validate and
|
|
45
|
+
complete their choices). The raw `--filter '<json>'` blob remains as a base
|
|
46
|
+
layer that per-field flags merge **over** (`vsc/gen/filters.py`).
|
|
47
|
+
|
|
48
|
+
### Pagination
|
|
49
|
+
|
|
50
|
+
`list` commands gain `--all` (follow the NSX cursor across pages), `--max-items`
|
|
51
|
+
(cap the total), and `--limit` (client-side cap for non-paginated vSphere
|
|
52
|
+
lists). Without `--all`, a paginated list returns one page and surfaces its
|
|
53
|
+
`cursor` for manual paging; on non-cursor backends `--all` is a no-op and the
|
|
54
|
+
output stays a plain array (`vsc/gen/paginate.py`).
|
|
55
|
+
|
|
56
|
+
## pyVmomi fallback (v0.3)
|
|
57
|
+
|
|
58
|
+
A few inventory/performance areas are only reachable through the older pyVmomi
|
|
59
|
+
SOAP API. Those are **hand-written read-only commands** mounted under
|
|
60
|
+
`vsc vsphere` alongside the generated ones, sharing the same output contract:
|
|
61
|
+
|
|
62
|
+
- a separate connection path, `connect_vmomi()` via `SmartConnect`, reusing the
|
|
63
|
+
resolved vSphere credentials and honouring `--insecure` like the REST path
|
|
64
|
+
(`vsc/connect/vmomi.py`);
|
|
65
|
+
- `vmomi_jsonable()` collapses managed objects to their moref and data objects to
|
|
66
|
+
dicts so results render like any other;
|
|
67
|
+
- a shared `run_read()` runner maps pyVmomi faults onto the same error envelope
|
|
68
|
+
and exit codes (`vsc/pyvmomi/runner.py`).
|
|
69
|
+
|
|
70
|
+
Commands: `perf` (PerformanceManager counters), `events` / `tasks`
|
|
71
|
+
(Event/Task managers), and `inventory` (a PropertyCollector property walk). All
|
|
72
|
+
are reads — no `--apply`.
|
|
73
|
+
|
|
74
|
+
## Design specs
|
|
75
|
+
|
|
76
|
+
The authoritative, milestone-by-milestone designs live in the repository:
|
|
77
|
+
|
|
78
|
+
- [v0.1 — dynamic read-only CLI](https://github.com/thomaschristory/vcf-super-cli/blob/main/docs/superpowers/specs/2026-06-05-vcf-super-cli-design.md)
|
|
79
|
+
- [v0.2 — writes](https://github.com/thomaschristory/vcf-super-cli/blob/main/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.2-writes-design.md)
|
|
80
|
+
- [v0.3 — ergonomics](https://github.com/thomaschristory/vcf-super-cli/blob/main/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.3-ergonomics-design.md)
|
|
81
|
+
|
|
82
|
+
## Contracts
|
|
83
|
+
|
|
84
|
+
- **Output:** JSON by default; `--output table` for humans.
|
|
85
|
+
- **Errors:** stable envelope `{ "error": { code, message, kind, details } }`.
|
|
86
|
+
- **Exit codes:** documented `IntEnum` (`0` ok, `1` generic, `2` usage, `3` auth,
|
|
87
|
+
`4` not-found, `5` connection, `6` config, `7` conflict, `8` unavailable).
|
|
88
|
+
- **Writes:** dry-run by default; `--apply` is the only gate and a dry-run opens
|
|
89
|
+
no connection.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# vcf-super-cli
|
|
2
|
+
|
|
3
|
+
A modern, agent-friendly CLI for **VMware Cloud Foundation 9** whose command tree
|
|
4
|
+
is **generated dynamically** from the official [`vcf-sdk`](https://pypi.org/project/vcf-sdk/)
|
|
5
|
+
vAPI bindings.
|
|
6
|
+
|
|
7
|
+
```console
|
|
8
|
+
$ vsc vsphere vm list --power-states POWERED_ON --profile prod
|
|
9
|
+
$ vsc nsx segments list --all --output table
|
|
10
|
+
$ vsc vsphere perf vm vm-42 --metric cpu.usage
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
!!! warning "Pre-1.0"
|
|
14
|
+
Reads and writes are both available. **Writes are dry-run by default** —
|
|
15
|
+
nothing changes without `--apply`. See [Writes](writes.md). While on `0.x`,
|
|
16
|
+
minor versions may include breaking changes.
|
|
17
|
+
|
|
18
|
+
## Highlights
|
|
19
|
+
|
|
20
|
+
- **Mirrors the real API** — commands come from the SDK's vAPI metadata, covering
|
|
21
|
+
vCenter and NSX from one generator.
|
|
22
|
+
- **Ergonomic** — offline tab-completion (enums, formats, profiles, filter
|
|
23
|
+
choices), per-field `--<field>` filter flags, and paging (`--all` /
|
|
24
|
+
`--max-items` / `--limit`).
|
|
25
|
+
- **pyVmomi fallback** — read-only `perf`, `events`, `tasks`, and `inventory`
|
|
26
|
+
commands for areas the REST/vAPI surface doesn't cover.
|
|
27
|
+
- **Safe by default** — writes are dry-run unless `--apply`; a dry-run never connects.
|
|
28
|
+
- **Agent-friendly** — JSON output, stable error envelope, documented exit codes,
|
|
29
|
+
bundled agent Skill.
|
|
30
|
+
|
|
31
|
+
See the [Design](design.md) for how dynamic generation works, and
|
|
32
|
+
[Commands](commands.md) for the full surface.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
uv tool install vcf-super-cli # or: pip install vcf-super-cli
|
|
38
|
+
vsc --install-completion # optional: offline shell completion
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
From source:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
uv sync
|
|
45
|
+
uv run vsc --help
|
|
46
|
+
```
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# vcf-super-cli v0.3 — Ergonomics (design)
|
|
2
|
+
|
|
3
|
+
**Status:** approved 2026-06-05. Milestone: `v0.3 — ergonomics` (#3).
|
|
4
|
+
**Predecessors:** [v0.1 design](2026-06-05-vcf-super-cli-design.md), [v0.2 writes design](2026-06-05-vcf-super-cli-v0.2-writes-design.md).
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
|
|
8
|
+
Make the dynamically-generated CLI pleasant to drive by hand and still trivially
|
|
9
|
+
scriptable by an agent. Three independent feature areas, each shipped as its own
|
|
10
|
+
issue/PR under milestone #3:
|
|
11
|
+
|
|
12
|
+
1. **Static shell completion** — offline tab-completion of enums, output formats,
|
|
13
|
+
profile names, and per-field filter choices.
|
|
14
|
+
2. **Filter & pagination helpers** — per-field filter flags (augmenting today's
|
|
15
|
+
`--filter '<json>'` blob) and friendlier paging (`--all`, `--max-items`,
|
|
16
|
+
`--limit`).
|
|
17
|
+
3. **pyVmomi fallback commands** — hand-written read commands (perf, events/tasks,
|
|
18
|
+
inventory walk) for gaps the vAPI/REST surface doesn't cover.
|
|
19
|
+
|
|
20
|
+
The agent contract is unchanged and non-negotiable: stable JSON output, the
|
|
21
|
+
documented error envelope, the frozen exit codes, and **writes stay dry-run by
|
|
22
|
+
default**. The pyVmomi additions are all reads, so they need no `--apply` gate.
|
|
23
|
+
|
|
24
|
+
## Locked decisions (from brainstorm, 2026-06-05)
|
|
25
|
+
|
|
26
|
+
- **Completion is static/offline only.** No feature in v0.3 hits the API during
|
|
27
|
+
`<TAB>`. Live resource-ID completion (e.g. completing `<vm>` from a live list)
|
|
28
|
+
is explicitly **deferred to v0.4**; the param model already carries
|
|
29
|
+
`resource_types` so it remains a clean future extension.
|
|
30
|
+
- **Filter flags augment, never replace, `--filter`.** Raw `--filter '<json>'`
|
|
31
|
+
stays as the base layer / escape hatch; generated per-field flags merge over it.
|
|
32
|
+
- **Pagination:** `--all` auto-follows the NSX cursor; `--max-items` caps total;
|
|
33
|
+
`--limit` is a client-side cap for non-cursor (vSphere) lists. NSX
|
|
34
|
+
`--page-size`/`--cursor` already exist as generated query options.
|
|
35
|
+
- **pyVmomi covers all three families** (perf, events/tasks, inventory walk),
|
|
36
|
+
built on a small shared `SmartConnect` foundation.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Feature 1 — Static shell completion
|
|
41
|
+
|
|
42
|
+
### Behaviour
|
|
43
|
+
|
|
44
|
+
Completion values are derived entirely from the introspected `Param` model plus
|
|
45
|
+
local config — never from a network call. `--help` and completion both stay fully
|
|
46
|
+
offline.
|
|
47
|
+
|
|
48
|
+
Completed surfaces:
|
|
49
|
+
|
|
50
|
+
| Surface | Source | Example |
|
|
51
|
+
|---------|--------|---------|
|
|
52
|
+
| enum option | `param.enum_values` | `--power-state POWERED_<TAB>` → `POWERED_ON`, `POWERED_OFF` |
|
|
53
|
+
| `--output` / `-o` | `OutputFormat` members | `-o <TAB>` → `json`, `table` |
|
|
54
|
+
| global `--profile` / `-p` | profile names from `load_config()` (offline file read) | `-p <TAB>` → `prod`, `lab` |
|
|
55
|
+
| per-field filter enum flags | reuse enum completer (feature 2) | `--filter-power-state <TAB>` |
|
|
56
|
+
|
|
57
|
+
Resource-ID args (the `<vm>` positionals) are **not** completed in v0.3.
|
|
58
|
+
|
|
59
|
+
### Implementation
|
|
60
|
+
|
|
61
|
+
- New module **`vsc/gen/complete.py`** — pure completer factories:
|
|
62
|
+
- `enum_completer(values: list[str]) -> Callable[[str], list[str]]`
|
|
63
|
+
- `profile_completer() -> Callable[[str], list[str]]`
|
|
64
|
+
Each takes the Click `incomplete` string and returns the prefix-filtered
|
|
65
|
+
candidate list. No `ctx`/`param` dependency → unit-testable without a shell.
|
|
66
|
+
- **`vsc/gen/builder.py`** `_build_signature`: when a non-path option is built for
|
|
67
|
+
an `ENUM` param, pass `autocompletion=enum_completer(param.enum_values)` to
|
|
68
|
+
`typer.Option`. When building `--output`, attach an `OutputFormat` completer.
|
|
69
|
+
- **`vsc/cli/app.py`** main callback: attach `profile_completer()` to `--profile`.
|
|
70
|
+
- Root app already sets `add_completion=True`; document `vsc --install-completion`
|
|
71
|
+
and `vsc --show-completion` in `docs/usage.md`.
|
|
72
|
+
|
|
73
|
+
### Tests
|
|
74
|
+
|
|
75
|
+
`tests/test_complete.py` — completer factories return correctly prefix-filtered
|
|
76
|
+
lists, empty `incomplete` returns all, no match returns `[]`. A smoke test asserts
|
|
77
|
+
generated enum options carry an `autocompletion` callback.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Feature 2 — Filter & pagination helpers
|
|
82
|
+
|
|
83
|
+
### Per-field filter flags
|
|
84
|
+
|
|
85
|
+
vCenter list operations take a single `filter` parameter that is a `STRUCT`
|
|
86
|
+
(`VM.FilterSpec` etc.). Today the user must pass it as `--filter '<json>'`. v0.3
|
|
87
|
+
additionally flattens that struct's fields into typed options.
|
|
88
|
+
|
|
89
|
+
- **Detection:** on a read/list op, a parameter that is `ParamKind.STRUCT` **and
|
|
90
|
+
named `filter`** is flattened. Write-body structs are *not* flattened — they
|
|
91
|
+
stay JSON (`--spec`, `--segment`, …).
|
|
92
|
+
- **Generated flags:** each struct field → `--<field>` (kebab-cased). `list`/`set`
|
|
93
|
+
fields are **repeatable** (`list[str]` annotation, multiple uses accumulate).
|
|
94
|
+
`enum` fields carry choices in help + the enum completer.
|
|
95
|
+
- **Precedence / merge:** raw `--filter '<json>'` provides the base dict; per-field
|
|
96
|
+
flags merge **over** it (a flag wins over the same key in the blob). The merged
|
|
97
|
+
dict is coerced into the FilterSpec via the existing
|
|
98
|
+
`coerce_struct`/`coerce_value` path — no new coercion logic.
|
|
99
|
+
- **Collisions:** a flattened field whose flag would collide with a reserved option
|
|
100
|
+
(`--output`, `--apply`) or another option is suffixed using the existing
|
|
101
|
+
`_sig_name` mechanism.
|
|
102
|
+
|
|
103
|
+
New helper **`vsc/gen/filters.py`**:
|
|
104
|
+
- `flatten_filter(param: Param) -> list[Param]` — child params for each struct field
|
|
105
|
+
(built with `param_from_type` on `struct_type.get_field(name)`).
|
|
106
|
+
- `assemble_filter(base_json, field_values, param) -> Any` — merge base + per-field
|
|
107
|
+
values and coerce into the struct. Pure; unit-testable.
|
|
108
|
+
|
|
109
|
+
`builder.py` consumes these: `_build_signature` emits the child options (tracking
|
|
110
|
+
them so `_collect_kwargs` knows to reassemble rather than pass them through), and
|
|
111
|
+
`_collect_kwargs` calls `assemble_filter` to rebuild the single `filter` kwarg.
|
|
112
|
+
|
|
113
|
+
### Pagination
|
|
114
|
+
|
|
115
|
+
NSX Policy list ops return a `*ListResult` struct (`.results`, `.cursor`,
|
|
116
|
+
`.result_count`) and already expose `cursor`/`page_size` as generated query
|
|
117
|
+
options. vSphere REST list ops return a plain list and do not paginate.
|
|
118
|
+
|
|
119
|
+
Injected options on **list verbs** (`op.cli_verb == "list"`):
|
|
120
|
+
|
|
121
|
+
| Flag | Applies to | Effect |
|
|
122
|
+
|------|-----------|--------|
|
|
123
|
+
| `--all` | cursor lists (NSX) | follow `.cursor` across pages, concatenate `.results`, stop at `--max-items` |
|
|
124
|
+
| `--max-items N` | all lists | hard cap on returned items (post-fetch for vSphere, loop-stop for `--all`) |
|
|
125
|
+
| `--limit N` | non-cursor lists (vSphere) | client-side slice of the returned list |
|
|
126
|
+
|
|
127
|
+
Without `--all`, a cursor list returns one page **including** its `cursor`, so an
|
|
128
|
+
agent can paginate manually. `--all` and `--max-items` interact: the follow loop
|
|
129
|
+
stops once `--max-items` items are collected.
|
|
130
|
+
|
|
131
|
+
New helper **`vsc/gen/paginate.py`**:
|
|
132
|
+
- `follow_cursor(fetch_page, *, max_items) -> list` — `fetch_page(cursor) ->
|
|
133
|
+
(results, next_cursor)`; loops until `next_cursor` is empty/repeated or the cap
|
|
134
|
+
is hit. Pure given the `fetch_page` callable (the callable closes over the SDK
|
|
135
|
+
method in `builder.py`); unit-testable with a fake pager.
|
|
136
|
+
|
|
137
|
+
`builder.py` `make_command` detects a cursor-bearing result and, when `--all` is
|
|
138
|
+
set, drives `follow_cursor`; otherwise applies `--limit`/`--max-items` slicing
|
|
139
|
+
before `emit()`.
|
|
140
|
+
|
|
141
|
+
### Tests
|
|
142
|
+
|
|
143
|
+
`tests/test_filters.py` — flatten produces one child per field with correct
|
|
144
|
+
kind/required/repeatable; assemble merges blob + flags with flags winning; enum
|
|
145
|
+
field carries choices. `tests/test_paginate.py` — `follow_cursor` concatenates,
|
|
146
|
+
respects `max_items`, terminates on empty/duplicate cursor. Builder integration
|
|
147
|
+
tests assert a vCenter list op grows `--<field>` options and an NSX list op grows
|
|
148
|
+
`--all`/`--max-items`.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Feature 3 — pyVmomi fallback commands
|
|
153
|
+
|
|
154
|
+
Hand-written read commands for gaps the vAPI/REST surface doesn't cover, mounted
|
|
155
|
+
into the existing `vsc vsphere` tree and emitted through the existing
|
|
156
|
+
`emit()`/error-envelope contract.
|
|
157
|
+
|
|
158
|
+
### Foundation
|
|
159
|
+
|
|
160
|
+
- New module **`vsc/connect/vmomi.py`**:
|
|
161
|
+
- `connect_vmomi() -> ServiceInstance` via `pyVim.connect.SmartConnect`, reusing
|
|
162
|
+
`resolve_target("vsphere")` for server/user/password and an `ssl` context that
|
|
163
|
+
honours `verify` (unverified context when `insecure`). Cached like the vAPI
|
|
164
|
+
connections (`reset_cache()` clears it); `Disconnect` registered at process
|
|
165
|
+
exit.
|
|
166
|
+
- `vmomi_jsonable(obj) -> Any` — convert managed-object / data-object trees into
|
|
167
|
+
plain JSON-able dicts (`moref` → `{"type", "value"}`, data objects → field
|
|
168
|
+
dicts, leave scalars/datetimes alone) so `emit()` renders them like any other
|
|
169
|
+
result.
|
|
170
|
+
- New package **`vsc/pyvmomi/`**, one module per family, each exposing a
|
|
171
|
+
`typer.Typer`. `vsc/cli/app.py` adds them onto the vsphere group
|
|
172
|
+
(`vsphere_group.add_typer(perf_app, name="perf")`, …). pyVmomi errors
|
|
173
|
+
(`vim.fault.*`, connection failures) map into the existing envelope/exit codes
|
|
174
|
+
(auth→3, not-found→4, connection→5) via a small adapter reusing
|
|
175
|
+
`vsc/output/errors.py` patterns.
|
|
176
|
+
|
|
177
|
+
### Commands
|
|
178
|
+
|
|
179
|
+
- **`vsc vsphere perf`** — real-time/historical counters via `PerformanceManager`.
|
|
180
|
+
e.g. `perf vm <vm> --metric cpu.usage [--interval 20s]`,
|
|
181
|
+
`perf host <host> --metric mem.usage`. Resolves the counter id, queries
|
|
182
|
+
`QueryPerf`, emits timestamped samples.
|
|
183
|
+
- **`vsc vsphere events`** — recent events via `EventManager` with `--since`/entity
|
|
184
|
+
filters. **`vsc vsphere tasks`** — running + recent tasks via `TaskManager`.
|
|
185
|
+
- **`vsc vsphere inventory`** — `PropertyCollector` walk for properties the REST
|
|
186
|
+
list ops omit (device tree, custom attributes, relationships), e.g.
|
|
187
|
+
`inventory vm <vm> --props config.hardware`.
|
|
188
|
+
|
|
189
|
+
All are reads → no `--apply`. They accept the same `--output json|table` and obey
|
|
190
|
+
`--profile`.
|
|
191
|
+
|
|
192
|
+
### Tests
|
|
193
|
+
|
|
194
|
+
pyVmomi has no offline-introspection trick like the vAPI bindings, so tests mock
|
|
195
|
+
`SmartConnect`/`ServiceInstance` and the relevant managers. `tests/test_vmomi.py`
|
|
196
|
+
covers `vmomi_jsonable` conversion (moref/data-object/scalar) purely;
|
|
197
|
+
`tests/test_pyvmomi_*.py` drive each command against a fake `ServiceInstance`
|
|
198
|
+
asserting the emitted JSON shape and error mapping. No live vCenter required.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Cross-cutting
|
|
203
|
+
|
|
204
|
+
- Each feature PR updates the relevant `docs/` page(s) and the bundled
|
|
205
|
+
`vsc/skill/assets/SKILL.md` for any new surface.
|
|
206
|
+
- `ruff`, `mypy --strict`, `pytest`, and `mkdocs --strict` stay green; CI green
|
|
207
|
+
before merge.
|
|
208
|
+
- Every PR gets an adversarial **refute-before-accept** review pass; findings are
|
|
209
|
+
fixed before merge.
|
|
210
|
+
|
|
211
|
+
## Issues (milestone #3)
|
|
212
|
+
|
|
213
|
+
| Issue | Title | Depends on |
|
|
214
|
+
|-------|-------|-----------|
|
|
215
|
+
| Epic | v0.3 — ergonomics | — |
|
|
216
|
+
| A | Static shell completion (enums, `--output`, `--profile`, filter choices) | — |
|
|
217
|
+
| B | Filter & pagination helpers (per-field flags + `--all`/`--max-items`/`--limit`) | — |
|
|
218
|
+
| C | pyVmomi fallback foundation + `vsc vsphere perf` | — |
|
|
219
|
+
| D | pyVmomi events & tasks | C |
|
|
220
|
+
| E | pyVmomi inventory walk | C |
|
|
221
|
+
| F | v0.3 docs/SKILL roundup + release prep | A–E |
|
|
222
|
+
|
|
223
|
+
Build order: A and B in parallel; C → (D, E); F last. The release tag (`vX.Y.Z`)
|
|
224
|
+
is pushed **manually by the maintainer** after F merges — `release.yml` keeps **no
|
|
225
|
+
`environment:` block** (the PyPI trusted publisher is registered with a blank
|
|
226
|
+
environment; they must stay matched).
|
|
227
|
+
|
|
228
|
+
## Out of scope (v0.3)
|
|
229
|
+
|
|
230
|
+
- Live resource-ID completion (hits the API) — v0.4 candidate.
|
|
231
|
+
- pyVmomi *writes* — the fallback surface is read-only this milestone.
|
|
232
|
+
- NSX Manager / Global-Manager APIs (still Policy-only).
|