proxctl 0.2.0__tar.gz → 0.2.2__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.
Files changed (57) hide show
  1. {proxctl-0.2.0 → proxctl-0.2.2}/.github/workflows/ci.yml +15 -0
  2. {proxctl-0.2.0 → proxctl-0.2.2}/.github/workflows/release.yml +80 -0
  3. {proxctl-0.2.0 → proxctl-0.2.2}/CHANGELOG.md +15 -0
  4. {proxctl-0.2.0 → proxctl-0.2.2}/Cargo.lock +1 -1
  5. {proxctl-0.2.0 → proxctl-0.2.2}/Cargo.toml +1 -1
  6. {proxctl-0.2.0 → proxctl-0.2.2}/Makefile +10 -1
  7. {proxctl-0.2.0 → proxctl-0.2.2}/PKG-INFO +65 -2
  8. {proxctl-0.2.0 → proxctl-0.2.2}/README.md +64 -1
  9. proxctl-0.2.2/prek.toml +36 -0
  10. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/container/config.rs +4 -0
  11. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/container/mod.rs +5 -0
  12. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/firewall.rs +34 -2
  13. {proxctl-0.2.0 → proxctl-0.2.2}/src/schema.rs +8 -0
  14. proxctl-0.2.0/.pre-commit-config.yaml +0 -31
  15. {proxctl-0.2.0 → proxctl-0.2.2}/.gitignore +0 -0
  16. {proxctl-0.2.0 → proxctl-0.2.2}/LICENSE +0 -0
  17. {proxctl-0.2.0 → proxctl-0.2.2}/proxctl_py/__init__.py +0 -0
  18. {proxctl-0.2.0 → proxctl-0.2.2}/proxctl_py/__main__.py +0 -0
  19. {proxctl-0.2.0 → proxctl-0.2.2}/pyproject.toml +0 -0
  20. {proxctl-0.2.0 → proxctl-0.2.2}/src/api/client.rs +0 -0
  21. {proxctl-0.2.0 → proxctl-0.2.2}/src/api/error.rs +0 -0
  22. {proxctl-0.2.0 → proxctl-0.2.2}/src/api/mod.rs +0 -0
  23. {proxctl-0.2.0 → proxctl-0.2.2}/src/api/token.rs +0 -0
  24. {proxctl-0.2.0 → proxctl-0.2.2}/src/api/types.rs +0 -0
  25. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/access.rs +0 -0
  26. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/api.rs +0 -0
  27. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/container.rs +0 -0
  28. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/diff.rs +0 -0
  29. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/firewall.rs +0 -0
  30. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/manifest.rs +0 -0
  31. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/mod.rs +0 -0
  32. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/reconciler.rs +0 -0
  33. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/apply/vm.rs +0 -0
  34. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/backup.rs +0 -0
  35. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/ceph.rs +0 -0
  36. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/cluster.rs +0 -0
  37. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/container/firewall.rs +0 -0
  38. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/container/lifecycle.rs +0 -0
  39. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/container/migrate.rs +0 -0
  40. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/container/snapshot.rs +0 -0
  41. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/export.rs +0 -0
  42. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/mod.rs +0 -0
  43. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/node.rs +0 -0
  44. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/pool.rs +0 -0
  45. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/storage.rs +0 -0
  46. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/task.rs +0 -0
  47. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/agent.rs +0 -0
  48. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/cloudinit.rs +0 -0
  49. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/config.rs +0 -0
  50. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/firewall.rs +0 -0
  51. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/lifecycle.rs +0 -0
  52. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/migrate.rs +0 -0
  53. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/mod.rs +0 -0
  54. {proxctl-0.2.0 → proxctl-0.2.2}/src/commands/vm/snapshot.rs +0 -0
  55. {proxctl-0.2.0 → proxctl-0.2.2}/src/lib.rs +0 -0
  56. {proxctl-0.2.0 → proxctl-0.2.2}/src/main.rs +0 -0
  57. {proxctl-0.2.0 → proxctl-0.2.2}/src/output.rs +0 -0
@@ -35,6 +35,21 @@ jobs:
35
35
  - name: Run tests
36
36
  run: make test
37
37
 
38
+ coverage:
39
+ name: Coverage
40
+ runs-on: ubuntu-latest
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: dtolnay/rust-toolchain@stable
44
+ - uses: taiki-e/install-action@cargo-tarpaulin
45
+ - name: Generate coverage
46
+ run: cargo tarpaulin --out xml
47
+ - name: Upload to Codecov
48
+ uses: codecov/codecov-action@v5
49
+ with:
50
+ files: cobertura.xml
51
+ token: ${{ secrets.CODECOV_TOKEN }}
52
+
38
53
  all-checks-passed:
39
54
  name: All checks passed
40
55
  runs-on: ubuntu-latest
@@ -232,3 +232,83 @@ jobs:
232
232
  echo "Dry run complete. Artifacts built but nothing published."
233
233
  echo "Archives:"
234
234
  find artifacts/release-* -type f | sort
235
+
236
+ update-homebrew:
237
+ needs: release
238
+ if: ${{ !inputs.dry_run }}
239
+ runs-on: ubuntu-latest
240
+ steps:
241
+ - uses: actions/download-artifact@v4
242
+ with:
243
+ path: /tmp/artifacts
244
+
245
+ - name: Compute SHA256 hashes
246
+ id: hashes
247
+ run: |
248
+ for target in x86_64-apple-darwin aarch64-apple-darwin x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
249
+ sha=$(cat /tmp/artifacts/release-${target}/proxctl-${{ github.ref_name }}-${target}.tar.gz.sha256 | awk '{print $1}')
250
+ key=$(echo "${target}" | tr '-' '_')
251
+ echo "${key}=${sha}" >> "$GITHUB_OUTPUT"
252
+ done
253
+
254
+ - name: Update Homebrew formula
255
+ env:
256
+ GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
257
+ run: |
258
+ VERSION="${{ github.ref_name }}"
259
+ VERSION_NUM="${VERSION#v}"
260
+
261
+ cat > /tmp/proxctl.rb << 'FORMULA'
262
+ class Proxctl < Formula
263
+ desc "CLI for Proxmox VE"
264
+ homepage "https://github.com/rvben/proxctl"
265
+ version "VERSION_NUM"
266
+ license "MIT"
267
+
268
+ on_macos do
269
+ if Hardware::CPU.arm?
270
+ url "https://github.com/rvben/proxctl/releases/download/VERSION/proxctl-VERSION-aarch64-apple-darwin.tar.gz"
271
+ sha256 "SHA_AARCH64_APPLE_DARWIN"
272
+ else
273
+ url "https://github.com/rvben/proxctl/releases/download/VERSION/proxctl-VERSION-x86_64-apple-darwin.tar.gz"
274
+ sha256 "SHA_X86_64_APPLE_DARWIN"
275
+ end
276
+ end
277
+
278
+ on_linux do
279
+ if Hardware::CPU.arm?
280
+ url "https://github.com/rvben/proxctl/releases/download/VERSION/proxctl-VERSION-aarch64-unknown-linux-gnu.tar.gz"
281
+ sha256 "SHA_AARCH64_UNKNOWN_LINUX_GNU"
282
+ else
283
+ url "https://github.com/rvben/proxctl/releases/download/VERSION/proxctl-VERSION-x86_64-unknown-linux-gnu.tar.gz"
284
+ sha256 "SHA_X86_64_UNKNOWN_LINUX_GNU"
285
+ end
286
+ end
287
+
288
+ def install
289
+ bin.install "proxctl"
290
+ end
291
+
292
+ test do
293
+ system "#{bin}/proxctl", "--version"
294
+ end
295
+ end
296
+ FORMULA
297
+
298
+ sed -i "s/VERSION_NUM/${VERSION_NUM}/g" /tmp/proxctl.rb
299
+ sed -i "s/VERSION/${VERSION}/g" /tmp/proxctl.rb
300
+ sed -i "s/SHA_AARCH64_APPLE_DARWIN/${{ steps.hashes.outputs.aarch64_apple_darwin }}/g" /tmp/proxctl.rb
301
+ sed -i "s/SHA_X86_64_APPLE_DARWIN/${{ steps.hashes.outputs.x86_64_apple_darwin }}/g" /tmp/proxctl.rb
302
+ sed -i "s/SHA_AARCH64_UNKNOWN_LINUX_GNU/${{ steps.hashes.outputs.aarch64_unknown_linux_gnu }}/g" /tmp/proxctl.rb
303
+ sed -i "s/SHA_X86_64_UNKNOWN_LINUX_GNU/${{ steps.hashes.outputs.x86_64_unknown_linux_gnu }}/g" /tmp/proxctl.rb
304
+
305
+ # Clone tap repo, update formula, push
306
+ git clone https://x-access-token:${GH_TOKEN}@github.com/rvben/homebrew-tap.git /tmp/tap
307
+ mkdir -p /tmp/tap/Formula
308
+ cp /tmp/proxctl.rb /tmp/tap/Formula/proxctl.rb
309
+ cd /tmp/tap
310
+ git config user.name "github-actions[bot]"
311
+ git config user.email "github-actions[bot]@users.noreply.github.com"
312
+ git add Formula/proxctl.rb
313
+ git diff --cached --quiet || git commit -m "Update proxctl to ${VERSION}"
314
+ git push
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+
6
+
7
+ ## [0.2.2](https://github.com/rvben/proxctl/compare/v0.2.1...v0.2.2) - 2026-04-03
8
+
9
+ ### Added
10
+
11
+ - **firewall**: add --iface and --macro flags to firewall add commands ([7533533](https://github.com/rvben/proxctl/commit/7533533da3bcc1e808cf3af5ffab6ca150d7a05b))
12
+
13
+ ## [0.2.1](https://github.com/rvben/proxctl/compare/v0.2.0...v0.2.1) - 2026-03-28
14
+
15
+ ### Added
16
+
17
+ - **container**: add --nameserver flag to container set command ([2cbdc9a](https://github.com/rvben/proxctl/commit/2cbdc9a421b3281731a48506cd3afb1f010752dd))
18
+ - **schema**: add metadata for apply and export commands ([b408baa](https://github.com/rvben/proxctl/commit/b408baa1c496af992bc4dd3d2da4611af5ce6b72))
19
+
5
20
  ## [0.2.0] - 2026-03-27
6
21
 
7
22
  ### Added
@@ -955,7 +955,7 @@ dependencies = [
955
955
 
956
956
  [[package]]
957
957
  name = "proxctl"
958
- version = "0.2.0"
958
+ version = "0.2.2"
959
959
  dependencies = [
960
960
  "clap",
961
961
  "clap_complete",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "proxctl"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  edition = "2024"
5
5
  rust-version = "1.90"
6
6
  description = "CLI for Proxmox VE"
@@ -1,4 +1,4 @@
1
- .PHONY: build release test lint fmt check clean install
1
+ .PHONY: build release test lint fmt check clean install release-patch release-minor release-major
2
2
 
3
3
  build:
4
4
  cargo build
@@ -23,3 +23,12 @@ clean:
23
23
 
24
24
  install: release
25
25
  cp target/release/proxctl ~/.local/bin/proxctl
26
+
27
+ release-patch:
28
+ vership bump patch
29
+
30
+ release-minor:
31
+ vership bump minor
32
+
33
+ release-major:
34
+ vership bump major
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxctl
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: System Administrators
@@ -24,8 +24,9 @@ Project-URL: Repository, https://github.com/rvben/proxctl.git
24
24
  [![crates.io](https://img.shields.io/crates/v/proxctl.svg)](https://crates.io/crates/proxctl)
25
25
  [![CI](https://github.com/rvben/proxctl/actions/workflows/ci.yml/badge.svg)](https://github.com/rvben/proxctl/actions/workflows/ci.yml)
26
26
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
27
+ [![codecov](https://codecov.io/gh/rvben/proxctl/graph/badge.svg)](https://codecov.io/gh/rvben/proxctl)
27
28
 
28
- A command-line interface for [Proxmox VE](https://www.proxmox.com/en/proxmox-virtual-environment/overview) -- manage VMs, containers, nodes, storage, and more from your terminal.
29
+ A command-line interface for [Proxmox VE](https://www.proxmox.com/en/proxmox-virtual-environment/overview) -- manage VMs, containers, nodes, storage, and more from your terminal. Includes declarative infrastructure management with `apply` and `export`.
29
30
 
30
31
  ## Install
31
32
 
@@ -65,9 +66,70 @@ proxctl vm snapshot list 100
65
66
  proxctl api get /nodes
66
67
  ```
67
68
 
69
+ ## Declarative Infrastructure (IaC)
70
+
71
+ Manage Proxmox resources declaratively with YAML manifests, similar to `kubectl apply`.
72
+
73
+ ### Export existing resources
74
+
75
+ ```bash
76
+ # Export a single VM
77
+ proxctl export vm 101 > haos.yaml
78
+
79
+ # Export all containers
80
+ proxctl export container --all > containers.yaml
81
+
82
+ # Export cluster firewall rules
83
+ proxctl export firewall cluster > firewall.yaml
84
+ ```
85
+
86
+ ### Apply desired state
87
+
88
+ ```yaml
89
+ # infra/web.yaml
90
+ kind: vm
91
+ name: web-01
92
+ vmid: 100
93
+ config:
94
+ memory: 4096
95
+ cores: 2
96
+ onboot: true
97
+ ---
98
+ kind: firewall-rule
99
+ scope: cluster
100
+ config:
101
+ action: ACCEPT
102
+ type: in
103
+ proto: tcp
104
+ dport: "443"
105
+ comment: "Allow HTTPS"
106
+ ```
107
+
108
+ ```bash
109
+ # Preview changes
110
+ proxctl apply -f infra/ --dry-run
111
+
112
+ # Apply changes
113
+ proxctl apply -f infra/
114
+
115
+ # Round-trip: export, then verify nothing drifted
116
+ proxctl export vm --all > current.yaml
117
+ proxctl apply -f current.yaml --dry-run # should show "noop" for all
118
+ ```
119
+
120
+ ### Key behaviors
121
+
122
+ - **Idempotent** -- running apply twice produces "up to date" on the second run
123
+ - **Patch semantics** -- only specified config keys are changed, others left untouched
124
+ - **Name or VMID** -- resources can be identified by name (auto-resolves VMID) or pinned by ID
125
+ - **Multi-document** -- multiple resources in one file with `---` separators, or a directory of files
126
+ - **Optional power state** -- add `state: running` or `state: stopped` to manage power, or omit to leave it alone
127
+ - **Safe** -- shows a diff before applying, destructive changes prompt for confirmation
128
+
68
129
  ## Features
69
130
 
70
131
  - **145+ commands** covering VMs, containers, nodes, storage, backups, cluster, firewall, access control, pools, and Ceph
132
+ - **Declarative IaC** -- `apply` and `export` for infrastructure-as-code workflows
71
133
  - **Auto-detection** -- resolves which node a VM lives on automatically
72
134
  - **Agent-friendly** -- `--json` output, `schema` command for introspection, structured exit codes
73
135
  - **Async task handling** -- waits for operations to complete with progress spinner
@@ -144,6 +206,7 @@ This enables AI agents and automation tools to discover available operations, re
144
206
 
145
207
  | Feature | proxctl | pvesh (built-in) | proxmoxer (Python) |
146
208
  |---|---|---|---|
209
+ | Declarative IaC (apply/export) | Yes | No | No |
147
210
  | Typed CLI with completions | Yes | No | N/A |
148
211
  | Cross-platform binaries | Yes | No (PVE only) | pip install |
149
212
  | VMID auto-resolution | Yes | No | Manual |
@@ -3,8 +3,9 @@
3
3
  [![crates.io](https://img.shields.io/crates/v/proxctl.svg)](https://crates.io/crates/proxctl)
4
4
  [![CI](https://github.com/rvben/proxctl/actions/workflows/ci.yml/badge.svg)](https://github.com/rvben/proxctl/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![codecov](https://codecov.io/gh/rvben/proxctl/graph/badge.svg)](https://codecov.io/gh/rvben/proxctl)
6
7
 
7
- A command-line interface for [Proxmox VE](https://www.proxmox.com/en/proxmox-virtual-environment/overview) -- manage VMs, containers, nodes, storage, and more from your terminal.
8
+ A command-line interface for [Proxmox VE](https://www.proxmox.com/en/proxmox-virtual-environment/overview) -- manage VMs, containers, nodes, storage, and more from your terminal. Includes declarative infrastructure management with `apply` and `export`.
8
9
 
9
10
  ## Install
10
11
 
@@ -44,9 +45,70 @@ proxctl vm snapshot list 100
44
45
  proxctl api get /nodes
45
46
  ```
46
47
 
48
+ ## Declarative Infrastructure (IaC)
49
+
50
+ Manage Proxmox resources declaratively with YAML manifests, similar to `kubectl apply`.
51
+
52
+ ### Export existing resources
53
+
54
+ ```bash
55
+ # Export a single VM
56
+ proxctl export vm 101 > haos.yaml
57
+
58
+ # Export all containers
59
+ proxctl export container --all > containers.yaml
60
+
61
+ # Export cluster firewall rules
62
+ proxctl export firewall cluster > firewall.yaml
63
+ ```
64
+
65
+ ### Apply desired state
66
+
67
+ ```yaml
68
+ # infra/web.yaml
69
+ kind: vm
70
+ name: web-01
71
+ vmid: 100
72
+ config:
73
+ memory: 4096
74
+ cores: 2
75
+ onboot: true
76
+ ---
77
+ kind: firewall-rule
78
+ scope: cluster
79
+ config:
80
+ action: ACCEPT
81
+ type: in
82
+ proto: tcp
83
+ dport: "443"
84
+ comment: "Allow HTTPS"
85
+ ```
86
+
87
+ ```bash
88
+ # Preview changes
89
+ proxctl apply -f infra/ --dry-run
90
+
91
+ # Apply changes
92
+ proxctl apply -f infra/
93
+
94
+ # Round-trip: export, then verify nothing drifted
95
+ proxctl export vm --all > current.yaml
96
+ proxctl apply -f current.yaml --dry-run # should show "noop" for all
97
+ ```
98
+
99
+ ### Key behaviors
100
+
101
+ - **Idempotent** -- running apply twice produces "up to date" on the second run
102
+ - **Patch semantics** -- only specified config keys are changed, others left untouched
103
+ - **Name or VMID** -- resources can be identified by name (auto-resolves VMID) or pinned by ID
104
+ - **Multi-document** -- multiple resources in one file with `---` separators, or a directory of files
105
+ - **Optional power state** -- add `state: running` or `state: stopped` to manage power, or omit to leave it alone
106
+ - **Safe** -- shows a diff before applying, destructive changes prompt for confirmation
107
+
47
108
  ## Features
48
109
 
49
110
  - **145+ commands** covering VMs, containers, nodes, storage, backups, cluster, firewall, access control, pools, and Ceph
111
+ - **Declarative IaC** -- `apply` and `export` for infrastructure-as-code workflows
50
112
  - **Auto-detection** -- resolves which node a VM lives on automatically
51
113
  - **Agent-friendly** -- `--json` output, `schema` command for introspection, structured exit codes
52
114
  - **Async task handling** -- waits for operations to complete with progress spinner
@@ -123,6 +185,7 @@ This enables AI agents and automation tools to discover available operations, re
123
185
 
124
186
  | Feature | proxctl | pvesh (built-in) | proxmoxer (Python) |
125
187
  |---|---|---|---|
188
+ | Declarative IaC (apply/export) | Yes | No | No |
126
189
  | Typed CLI with completions | Yes | No | N/A |
127
190
  | Cross-platform binaries | Yes | No (PVE only) | pip install |
128
191
  | VMID auto-resolution | Yes | No | Manual |
@@ -0,0 +1,36 @@
1
+ #:schema: https://www.schemastore.org/prek.json
2
+
3
+ [[repos]]
4
+ repo = "builtin"
5
+ hooks = [
6
+ { id = "trailing-whitespace" },
7
+ { id = "end-of-file-fixer" },
8
+ { id = "check-added-large-files" },
9
+ ]
10
+
11
+ [[repos]]
12
+ repo = "local"
13
+ [[repos.hooks]]
14
+ id = "cargo-fmt"
15
+ name = "cargo fmt"
16
+ entry = "cargo fmt -- --check"
17
+ language = "system"
18
+ types = ["rust"]
19
+ pass_filenames = false
20
+
21
+ [[repos.hooks]]
22
+ id = "cargo-clippy"
23
+ name = "cargo clippy"
24
+ entry = "cargo clippy -- -D warnings"
25
+ language = "system"
26
+ types = ["rust"]
27
+ pass_filenames = false
28
+
29
+ [[repos.hooks]]
30
+ id = "cargo-test"
31
+ name = "cargo test"
32
+ entry = "cargo test"
33
+ language = "system"
34
+ types = ["rust"]
35
+ pass_filenames = false
36
+ stages = ["pre-push"]
@@ -66,6 +66,7 @@ pub async fn set(
66
66
  hostname: Option<String>,
67
67
  description: Option<String>,
68
68
  onboot: Option<bool>,
69
+ nameserver: Option<String>,
69
70
  ) -> Result<(), Error> {
70
71
  let node = client.resolve_node_for_vmid(vmid, node_override).await?;
71
72
  let path = format!("/nodes/{node}/lxc/{vmid}/config");
@@ -86,6 +87,9 @@ pub async fn set(
86
87
  if let Some(ob) = onboot {
87
88
  params.push(("onboot".to_string(), if ob { "1" } else { "0" }.to_string()));
88
89
  }
90
+ if let Some(ns) = nameserver {
91
+ params.push(("nameserver".to_string(), ns));
92
+ }
89
93
 
90
94
  if params.is_empty() {
91
95
  return Err(Error::Config(
@@ -192,6 +192,9 @@ pub enum ContainerCommand {
192
192
  /// Start at boot
193
193
  #[arg(long)]
194
194
  onboot: Option<bool>,
195
+ /// DNS nameserver(s) (e.g. "10.10.30.225 1.1.1.1")
196
+ #[arg(long)]
197
+ nameserver: Option<String>,
195
198
  },
196
199
  /// Create a new container
197
200
  Create {
@@ -362,6 +365,7 @@ pub async fn run(
362
365
  hostname,
363
366
  description,
364
367
  onboot,
368
+ nameserver,
365
369
  } => {
366
370
  config::set(
367
371
  client,
@@ -373,6 +377,7 @@ pub async fn run(
373
377
  hostname,
374
378
  description,
375
379
  onboot,
380
+ nameserver,
376
381
  )
377
382
  .await
378
383
  }
@@ -42,6 +42,9 @@ pub enum ClusterFirewallCommand {
42
42
  /// Enable the rule
43
43
  #[arg(long)]
44
44
  enable: Option<bool>,
45
+ /// Network interface (e.g. vmbr0, vmbr0v30)
46
+ #[arg(long)]
47
+ iface: Option<String>,
45
48
  /// Source address
46
49
  #[arg(long)]
47
50
  source: Option<String>,
@@ -54,6 +57,9 @@ pub enum ClusterFirewallCommand {
54
57
  /// Protocol
55
58
  #[arg(long)]
56
59
  proto: Option<String>,
60
+ /// Macro (e.g. SSH, HTTP, HTTPS)
61
+ #[arg(long, rename_all = "kebab-case")]
62
+ r#macro: Option<String>,
57
63
  /// Comment
58
64
  #[arg(long)]
59
65
  comment: Option<String>,
@@ -91,6 +97,9 @@ pub enum NodeFirewallCommand {
91
97
  /// Enable the rule
92
98
  #[arg(long)]
93
99
  enable: Option<bool>,
100
+ /// Network interface (e.g. vmbr0, vmbr0v30)
101
+ #[arg(long)]
102
+ iface: Option<String>,
94
103
  /// Source address
95
104
  #[arg(long)]
96
105
  source: Option<String>,
@@ -103,6 +112,9 @@ pub enum NodeFirewallCommand {
103
112
  /// Protocol
104
113
  #[arg(long)]
105
114
  proto: Option<String>,
115
+ /// Macro (e.g. SSH, HTTP, HTTPS)
116
+ #[arg(long, rename_all = "kebab-case")]
117
+ r#macro: Option<String>,
106
118
  /// Comment
107
119
  #[arg(long)]
108
120
  comment: Option<String>,
@@ -206,10 +218,12 @@ pub async fn run(
206
218
  action,
207
219
  r#type,
208
220
  enable,
221
+ iface,
209
222
  source,
210
223
  dest,
211
224
  dport,
212
225
  proto,
226
+ r#macro,
213
227
  comment,
214
228
  } => {
215
229
  cluster_add_rule(
@@ -218,10 +232,12 @@ pub async fn run(
218
232
  &action,
219
233
  &r#type,
220
234
  enable,
235
+ iface.as_deref(),
221
236
  source.as_deref(),
222
237
  dest.as_deref(),
223
238
  dport.as_deref(),
224
239
  proto.as_deref(),
240
+ r#macro.as_deref(),
225
241
  comment.as_deref(),
226
242
  )
227
243
  .await
@@ -240,10 +256,12 @@ pub async fn run(
240
256
  action,
241
257
  r#type,
242
258
  enable,
259
+ iface,
243
260
  source,
244
261
  dest,
245
262
  dport,
246
263
  proto,
264
+ r#macro,
247
265
  comment,
248
266
  } => {
249
267
  let n = require_node(node.as_deref(), global_node)?;
@@ -254,10 +272,12 @@ pub async fn run(
254
272
  &action,
255
273
  &r#type,
256
274
  enable,
275
+ iface.as_deref(),
257
276
  source.as_deref(),
258
277
  dest.as_deref(),
259
278
  dport.as_deref(),
260
279
  proto.as_deref(),
280
+ r#macro.as_deref(),
261
281
  comment.as_deref(),
262
282
  )
263
283
  .await
@@ -336,10 +356,12 @@ fn build_rule_params(
336
356
  action: &str,
337
357
  rule_type: &str,
338
358
  enable: Option<bool>,
359
+ iface: Option<&str>,
339
360
  source: Option<&str>,
340
361
  dest: Option<&str>,
341
362
  dport: Option<&str>,
342
363
  proto: Option<&str>,
364
+ r#macro: Option<&str>,
343
365
  comment: Option<&str>,
344
366
  ) -> Vec<(String, String)> {
345
367
  let mut params: Vec<(String, String)> = vec![
@@ -349,6 +371,9 @@ fn build_rule_params(
349
371
  if let Some(e) = enable {
350
372
  params.push(("enable".to_string(), if e { "1" } else { "0" }.to_string()));
351
373
  }
374
+ if let Some(i) = iface {
375
+ params.push(("iface".to_string(), i.to_string()));
376
+ }
352
377
  if let Some(s) = source {
353
378
  params.push(("source".to_string(), s.to_string()));
354
379
  }
@@ -361,6 +386,9 @@ fn build_rule_params(
361
386
  if let Some(p) = proto {
362
387
  params.push(("proto".to_string(), p.to_string()));
363
388
  }
389
+ if let Some(m) = r#macro {
390
+ params.push(("macro".to_string(), m.to_string()));
391
+ }
364
392
  if let Some(c) = comment {
365
393
  params.push(("comment".to_string(), c.to_string()));
366
394
  }
@@ -391,14 +419,16 @@ async fn cluster_add_rule(
391
419
  action: &str,
392
420
  rule_type: &str,
393
421
  enable: Option<bool>,
422
+ iface: Option<&str>,
394
423
  source: Option<&str>,
395
424
  dest: Option<&str>,
396
425
  dport: Option<&str>,
397
426
  proto: Option<&str>,
427
+ r#macro: Option<&str>,
398
428
  comment: Option<&str>,
399
429
  ) -> Result<(), Error> {
400
430
  let params = build_rule_params(
401
- action, rule_type, enable, source, dest, dport, proto, comment,
431
+ action, rule_type, enable, iface, source, dest, dport, proto, r#macro, comment,
402
432
  );
403
433
  let param_refs: Vec<(&str, &str)> = params
404
434
  .iter()
@@ -459,14 +489,16 @@ async fn node_add_rule(
459
489
  action: &str,
460
490
  rule_type: &str,
461
491
  enable: Option<bool>,
492
+ iface: Option<&str>,
462
493
  source: Option<&str>,
463
494
  dest: Option<&str>,
464
495
  dport: Option<&str>,
465
496
  proto: Option<&str>,
497
+ r#macro: Option<&str>,
466
498
  comment: Option<&str>,
467
499
  ) -> Result<(), Error> {
468
500
  let params = build_rule_params(
469
- action, rule_type, enable, source, dest, dport, proto, comment,
501
+ action, rule_type, enable, iface, source, dest, dport, proto, r#macro, comment,
470
502
  );
471
503
  let param_refs: Vec<(&str, &str)> = params
472
504
  .iter()
@@ -381,6 +381,14 @@ fn build_metadata() -> HashMap<&'static str, CommandMeta> {
381
381
  meta!("ceph pool create", mutating: true, idempotent: false);
382
382
  meta!("ceph mon list", output_fields: &["name", "host", "addr"]);
383
383
 
384
+ // Apply
385
+ meta!("apply", mutating: true, idempotent: true, output_fields: &["kind", "name", "vmid", "action", "changes", "status", "error"]);
386
+
387
+ // Export
388
+ meta!("export vm", output_fields: &["kind", "name", "vmid", "node", "state", "config"]);
389
+ meta!("export container", output_fields: &["kind", "name", "vmid", "node", "state", "config"]);
390
+ meta!("export firewall", output_fields: &["kind", "scope", "target", "config"]);
391
+
384
392
  // API passthrough
385
393
  meta!("api get", output_fields: &[]);
386
394
  meta!("api post", mutating: true, idempotent: false);
@@ -1,31 +0,0 @@
1
- repos:
2
- - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v4.5.0
4
- hooks:
5
- - id: trailing-whitespace
6
- - id: end-of-file-fixer
7
- - id: check-added-large-files
8
-
9
- - repo: local
10
- hooks:
11
- - id: cargo-fmt
12
- name: cargo fmt
13
- entry: cargo fmt -- --check
14
- language: system
15
- types: [rust]
16
- pass_filenames: false
17
-
18
- - id: cargo-clippy
19
- name: cargo clippy
20
- entry: cargo clippy -- -D warnings
21
- language: system
22
- types: [rust]
23
- pass_filenames: false
24
-
25
- - id: cargo-test
26
- name: cargo test
27
- entry: cargo test
28
- language: system
29
- types: [rust]
30
- pass_filenames: false
31
- stages: [pre-push]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes