01 · START HERE
Install one executable
Choose the Parto executable for your operating system and CPU architecture, put it where you keep command-line tools, and run it.
chmod +x parto_linux_amd64
sudo mv parto_linux_amd64 /usr/local/bin/parto
parto --versionchmod +x parto_darwin_arm64
sudo mv parto_darwin_arm64 /usr/local/bin/parto
parto --versionNew-Item -ItemType Directory -Force "$HOME\bin"
Move-Item .\parto_windows_amd64.exe "$HOME\bin\parto.exe"
& "$HOME\bin\parto.exe" --versionUse the arm64 file on ARM64 systems and amd64 on x86-64. On Windows, add $HOME\bin to PATH only if you want to run parto.exe from any directory.
Start with read-only inspection. Then run a size-specific dry run. Remove --dry-run only after both outputs look correct.
parto inspect [options]
parto grow [options] --dry-run
parto grow [options]02 · READY CHECK
What Parto needs
Parto connects three parts of the operation: the machine running the command, the OpenStack control plane, and the target VM. Have each one ready before the first inspection.
Run the Parto binary
A Linux, macOS, or Windows Parto executable matching the CPU architecture, OpenStack API access, and SSH access directly or through PAS.
Authenticate to OpenStack
Permission to read servers and attachments, read volumes, create and attach a volume, or extend an existing volume.
Reach the target VM
A mounted non-root XFS data filesystem, sudo access, and the required Linux storage utilities including df.
Centralize environments
Add PostgreSQL only when you use --environment. Standalone --cloud and OS_* workflows stay database-free.
TARGET UTILITIES
Common packages include util-linux, parted, lvm2,xfsprogs, and cloud-guest-utils. growpart is only required for partition-backed XFS; smartmontools is optional for best-effort SMART inspection.
03 · CONNECT
Configure OpenStack
Use --cloud to select a profile explicitly. When it is omitted, Parto checks OS_CLOUD, selects the only profile in the first discovered clouds.yaml, and uses OS_* credentials when no config file exists.
--cloud example-cloud selects the example-cloud key under clouds:. It is not an OpenStack project name. The selected profile supplies authentication, project scope, region, and TLS settings.
A single profile is selected automatically. If clouds.yaml contains multiple profiles, Parto stops and asks for --cloud instead of guessing.
clouds:
example-cloud:
auth_type: password
auth:
auth_url: https://identity.example.com/v3
username: example-user
project_name: example-project
user_domain_name: Default
project_domain_name: Default
region_name: RegionOne
interface: public
identity_api_version: 3
verify: trueclouds:
example-cloud:
auth:
password: replace-with-your-openstack-passwordchmod 600 ~/.config/openstack/clouds.yaml ~/.config/openstack/secure.yaml
openstack --os-cloud example-cloud token issue
openstack --os-cloud example-cloud server show my-vm
openstack --os-cloud example-cloud server volume list my-vmFor a private CA, add cacert: /path/to/company-ca.pem. Do not solve certificate errors with verify: false.
Parto requests Cinder API microversion 3.42 for online extension of attached volumes. The cloud policy and storage backend must also allow it.
04 · MANAGED CONNECTIONS
Import encrypted OpenStack environments
Managed environments let Parto resolve an OpenStack target from PostgreSQL instead of reading a local profile on every run. An environment binds the source cloud profile, immutable project ID, project metadata, region, and encrypted credential payload.
| Selector | What it selects | Credential source |
|---|---|---|
--cloud example-cloud | A profile named example-cloud | Local clouds.yaml, secure.yaml, or OS_* |
--environment production-search | A Parto environment record | Encrypted PostgreSQL payload, decrypted only in memory |
--cloud and --environment are mutually exclusive.
Start PostgreSQL for local development
docker volume create parto-postgres-data
docker run -d \
--name parto-postgres \
--restart unless-stopped \
-e POSTGRES_USER=parto \
-e POSTGRES_PASSWORD=parto-local-password \
-e POSTGRES_DB=parto \
-v parto-postgres-data:/var/lib/postgresql/data \
-p 127.0.0.1:55432:5432 \
postgres:17-alpineexport PARTO_DATABASE_URL='postgres://parto:parto-local-password@127.0.0.1:55432/parto?sslmode=disable'
openssl rand -base64 32 # Generate once and save the value.
export PARTO_MASTER_KEY='paste-the-saved-value-here'Parto creates its environment table on first use. Keep the same master key for every later run; losing or replacing it makes existing credential records unreadable.
Import one project-scoped profile
parto environment import \
--file ~/.config/openstack/clouds.yaml \
--cloud example-cloud \
--name-prefix production-search
parto environment listHere example-cloud is the profile name in the YAML file, while production-search becomes the Parto environment name. OpenStack application credentials are project-scoped, so import one profile for each project.
Discover projects available to a user
parto environment import \
--file ~/.config/openstack/clouds.yaml \
--cloud company \
--discover-projects \
--name-prefix productionProject discovery is explicit and works only with username/password credentials that can be re-scoped. Parto imports every enabled accessible project using its immutable project ID. It does not use project names as OpenStack identity.
Parto uses AES-256-GCM before writing credential payloads to PostgreSQL, never returns them from environment list, and never writes a temporary managed clouds.yaml. Keep PARTO_MASTER_KEY outside the database.
parto inspect \
--environment production-search \
--vm-name my-vm \
--mountpoint /srv/data \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa05 · READ ONLY
Inspect before changing anything
inspect discovers eligible data targets and runs preflight checks. It does not accept --size, run mutations, ask for approvals, or write an operation journal.
parto inspect \
--cloud example-cloud \
--vm-name my-vm \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsaparto inspect \
--cloud example-cloud \
--vm-name my-vm \
--mountpoint /srv/data \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa06 · OBSERVE AND INTEGRATE
Filesystem health and JSON output
Every inspected target includes filesystem capacity and inode pressure. Parto also follows the target to its physical disks and reads SMART health when the VM exposes it.
| Signal | Reported data | Behavior |
|---|---|---|
| Filesystem | Total, used, available bytes, and used percentage | healthy below 80%, warning at 80%, critical at 90% |
| Inodes | Total, used, available count, and used percentage | Uses the same informational thresholds |
| SMART | Pass/fail, model, serial, temperature, and power-on hours when available | unavailable is non-fatal on virtual disks |
parto inspect \
--cloud example-cloud \
--vm-name my-vm \
--mountpoint /srv/data \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa \
--json | jq '.targets[].health'--json writes one JSON document to stdout. Prompts and operational diagnostics use stderr. Structured errors preserve the normal Parto exit codes.
SMART is best-effort. Parto uses smartctl when installed, supports older text-only output, and reports each underlying LVM disk separately. Missing or hidden SMART data never makes inspection fail.
07 · PLAN, THEN APPLY
Dry-run and grow
A dry run performs real read-only discovery and prints the planned OpenStack and guest commands without applying them.
parto grow \
--cloud example-cloud \
--vm-name my-vm \
--mountpoint /srv/data \
--size 110 \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa \
--dry-runHere 110 is the new total Cinder volume size in GB.
parto grow \
--cloud example-cloud \
--vm-name my-vm \
--mountpoint /data \
--size 50 \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa \
--dry-runHere 50 is the size of a new Cinder volume added to the volume group.
Remove --dry-run after reviewing the plan. Parto asks for initial approval and confirms every mutating remote command.
08 · PICK THE RIGHT PATH
Filesystem and target selection
XFS is the filesystem. LVM, a partition, or a whole disk is the storage layout underneath it. The recommended selector is always the mounted filesystem: --mountpoint /data.
- Recommended:
--mountpoint /dataworks for all supported layouts. - LVM alternative:
--lv-name data-lvaccepts the plain LV name. - Auto-detect: omit both only when exactly one eligible non-root target exists.
The root filesystem and protected operating-system or runtime trees are never eligible. A target sharing the physical root disk is also rejected.
//bin/boot/dev/efi/etc/lib/lib64/proc/root/run/sbin/snap/sys/usr/var/lib/snapd/snap/var/lock/var/runEvery descendant of these paths is protected too.
Data locations such as /data, /home, /mnt/storage, /opt/data, /srv, /tmp, and /var remain eligible when every other safety check passes.
09 · IMPORTANT
Understand --size
The meaning changes with the detected storage layout.
| XFS placement | Action | Meaning of --size |
|---|---|---|
| On LVM | Add a new Cinder volume | New volume size in GB |
| On a partition | Grow volume and partition | New total volume size in GB |
| On a whole disk | Grow existing volume | New total volume size in GB |
On LVM, --size 50 adds a new 50 GB disk. For an existing 50 GB whole-disk volume that needs 20 GB more, use --size 70.
10 · ACCESS
SSH, sudo, and PAS
Direct SSH with a key
parto inspect \
--cloud example-cloud \
--vm-name my-vm \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsaOmit --ssh-key and --ssh-password to prompt for the SSH password. Add --ask-sudo-password when sudo is not passwordless.
PAS gateway
--ssh-gateway is the host you connect to. --ssh-gateway-login is the username template sent to that gateway; it must contain {target}.
parto grow \
--cloud example-cloud \
--vm-name my-vm \
--mountpoint /srv/data \
--size 110 \
--ssh-gateway pas.example.com \
--ssh-gateway-login 'centrify@{target}@user@example.com' \
--dry-run11 · GUARDRAILS
Safety and recovery
- The root disk and protected OS/runtime mount trees are never touched.
- Only mounted XFS filesystems are supported; other filesystems stop in preflight.
- Every mutating command requires operator confirmation.
- New guest disks are matched to exact Cinder volume IDs by serial.
- Timeouts and ambiguous outcomes stop the workflow instead of guessing.
- There is no automatic rollback, detach, delete, or LVM cleanup.
Operation journal
Every grow attempt that selects a target writes a private JSON record with mode 0600. It contains checkpoints and non-secret recovery context.
~/.local/state/parto/runs/
# or ${XDG_STATE_HOME}/parto/runs/Ctrl-C before mutation is a normal cancellation. After mutation begins, the result may be unknown; Parto stops and prints read-only inspection commands and manual recovery guidance. With --json, the same recovery context, failed step, inspection commands, and guidance are returned as structured fields.
12 · REFERENCE
CLI reference
| Option | Purpose |
|---|---|
--vm-name | OpenStack instance name. Required for every command. |
--size | Grow only. Positive integer in GB; meaning depends on layout. |
--mountpoint | Recommended target selector, such as /data. |
--lv-name | Alternative LVM selector using a plain LV name. |
--cloud | Explicit profile selector. Omit to use OS_CLOUD, one automatically discovered profile, or OS_*. |
--environment | Managed OpenStack environment stored in PostgreSQL. Mutually exclusive with --cloud. |
--ssh-user | Required for direct SSH. |
--ssh-key | SSH private-key path. |
--ssh-password | Non-interactive SSH password; interactive prompt is safer. |
--ask-sudo-password | Prompt interactively for the remote sudo password. |
--ssh-gateway | PAS gateway hostname. |
--ssh-gateway-login | PAS username template containing {target}. |
--dry-run | Read the real state and print mutations without applying them. |
--json | Write one machine-readable result document to stdout. |
parto inspect --help
parto grow --help
parto environment --helpNEED A HAND?