Skip to content

feat(volume): batch Change Type, so a backend can be rebalanced in one action - #133

Draft
traviswu-bigstack wants to merge 1 commit into
masterfrom
travis.wu/batch-volume-retype
Draft

traviswu-bigstack wants to merge 1 commit into
masterfrom
travis.wu/batch-volume-retype

Conversation

@traviswu-bigstack

@traviswu-bigstack traviswu-bigstack commented Sep 15, 2026

Copy link
Copy Markdown

What type of PR is this?

/kind feature

What this PR does / why we need it

Skyline can already retype a volume — Change Type has been a row action on the volume list, and it already passes migration_policy: 'on-demand', so an in-use volume moves live. What it cannot do is retype more than one volume without opening the dialog once per row.

Two cases need that, and both arrived together:

  • An operator rebalancing a backend. Not a full evacuation — moving a subset of a backend's volumes off it to even out capacity. That is inherently a multi-select action.
  • A tenant moving a set of volumes onto a faster tier.

The volume list already renders row checkboxes (Delete is a batch action), so the work is making the existing action accept a selection rather than adding a new one.

items decides batch vs single. BatchActionButtons.updateConf passes items: selectedItems and isAllowed: selectedItems.length > 0 — the framework never consults an action's allowed for a batch button, so per-item eligibility has to be re-checked inside the action. disableSubmit does that, and the tip names the volumes to deselect. That matches how batch Delete refuses a mixed selection (ConfirmAction.perform"There are resources that cannot be deleted in the selected resources, such as:") rather than silently dropping the ones it cannot handle.

The type list adapts to the selection. The single-volume behaviour is unchanged: the volume's own type is dropped from the options. A mixed selection keeps every type on offer, and volumes already on the chosen type are skipped instead of being sent to cinder to be refused — the tip says so when that can happen.

The root-disk exclusion is dropped. allowed carried !isOsDisk since 4eb68e7f (99cloud's initial import), and it refuses what nova permits. swap_volume has no root-disk guard — compute/api.py gates on @check_instance_state([ACTIVE, PAUSED, RESIZED]), old-volume-attached-here, new-volume-detached, size, and >1 read/write attachment on a multiattach volume; the libvirt driver adds native LUKS and disk-present-in-domain. Every root_bdm / boot_index reference in that file is in server-create validation. CubeCOS's own preflight has no root blocker either, so the console was refusing what its CLI allows — and a rebalance that cannot touch boot disks only half-drains a backend. Status stays the gate.

Submission is allSettled. One failure then reports against its own volume instead of taking the rest of the batch down with it. This is the shape Create already uses for multi-volume creation, and BaseForm.onOk already unpacks an array response into per-instance notices keyed off instanceName.

Added to the admin volume list too — as a row action beside Migrate, and as a batch action — since the rebalance case is the operator's, and the admin list previously had no retype at all.

Which issue(s) this PR fixes

Refs bigstack-oss/cubecos#1492, bigstack-oss/cubecmp#1373 — the CubeCOS/CMP half of the same tenant storage-tier change.

Special notes for your reviewer

Not yet exercised on a cluster. The sky lab was repurposed mid-stream, so verification here is a clean production webpack build plus lint; the behaviour is reasoned from the framework code cited above, not observed. Worth a pass on a lab before it merges.

Two things a reviewer may want to weigh in on:

  • Blocking the whole batch when one volume is ineligible (not available/in-use) follows the Delete convention, but for a 30-volume rebalance it means deselect-and-retry. Skipping the ineligible ones with a note in the modal is the alternative.
  • defaultValue returns a volume_type key while the form field is named new_type, so the default select value has never applied and the required select opens empty. That predates this change and I left it alone — auto-selecting the first type is more dangerous for a batch than for one volume.

Two new strings, translated for zh-hans / zh-hant / ko-kr / ru; tr-tr skipped, matching #132.

Additional documentation


🤖 Generated with Claude Code

https://claude.ai/code/session_013cE5vKGkQQn3nAnn9TLKAU

@traviswu-bigstack
traviswu-bigstack requested a review from a team as a code owner September 15, 2026 03:31
@traviswu-bigstack
traviswu-bigstack marked this pull request as draft September 15, 2026 03:40
Change Type already existed per volume, but moving more than one volume
meant opening the dialog once per row. The two cases that need it are an
operator rebalancing a backend (a subset of its volumes, not a full
evacuation) and a tenant moving a set of volumes onto a faster tier.

The volume list already has row checkboxes -- Delete is a batch action --
so the work is making the existing action accept a selection:

  - `items` from the framework decides batch vs single. The framework
    enables a batch button on any selection and never consults `allowed`,
    so `disableSubmit` repeats the per-item check and the tip names the
    volumes to deselect, matching how batch Delete refuses a mixed set.
  - The type list drops the current type only when every selected volume
    shares one; a mixed selection keeps all of them on offer, and volumes
    already on the chosen type are skipped rather than sent to cinder to
    be refused. The tip says so.
  - Submission is allSettled, so one failure reports against its own
    volume instead of losing the rest -- the same shape Create uses for
    multi-volume creation, which BaseForm.onOk already unpacks per
    instance.
  - migration_policy stays on-demand, so an in-use volume moves live.

Also drops the !isOsDisk condition, which came from the initial upstream
import and refused what nova permits. swap_volume has no root-disk guard
in compute/api.py or the libvirt driver -- its gates are instance state,
size, multiattach and native LUKS -- so a running BFV instance's root
volume retypes like any other in-use volume. CubeCOS's own preflight has
no root blocker either, so the console was refusing what its CLI allows,
and a rebalance that cannot touch boot disks only half-drains a backend.
Status remains the gate.

Added to the admin list too, as a row action beside Migrate and as a
batch action, since the rebalance case is the operator's.

Verified by a production webpack build; not yet exercised on a cluster.

Signed-off-by: Travis Wu <travis.wu@bigstack.co>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013cE5vKGkQQn3nAnn9TLKAU
@traviswu-bigstack
traviswu-bigstack force-pushed the travis.wu/batch-volume-retype branch from d70bc83 to 4cc4925 Compare September 15, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant