Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
091e5ab
feat(vmop): add migration progress status and reasons
danilrwx Apr 2, 2026
0146943
feat(vmop): adapt migration progress strategy to kubevirt fields
danilrwx Apr 2, 2026
ea31a60
feat(core): estimate migration progress without transfer metrics
danilrwx Apr 2, 2026
9bfedf0
test(core): add migration progress test coverage
danilrwx Apr 2, 2026
84dd100
refactor(core): centralize migration sync progress range
danilrwx Apr 2, 2026
27ee884
feat(core, kubevirt): use migration transfer counters for vmop progress
danilrwx Apr 2, 2026
9ac043e
fix(core): reduce repeated vmop migration stall bumps
danilrwx Apr 2, 2026
5326ef6
chore(core): add 3p-kubevirt branch info
danilrwx Apr 2, 2026
1d9581a
chore(core): add corrent kubevirt replace
danilrwx Apr 2, 2026
b8eb134
fix(core): update embedded kubevirt migration counters schema
danilrwx Apr 2, 2026
a22ecca
feat(core): improve migration progress tracking
danilrwx Apr 3, 2026
0b84476
chore(core): remove unused migration helper
danilrwx Apr 3, 2026
d0fc6ab
chore(core): update kubevirt api replace
danilrwx Apr 3, 2026
2a4dee0
refactor(core): use ptr helper for migration progress
danilrwx Apr 3, 2026
001bb0b
fix(core): align migration progress iterative entry
danilrwx Apr 3, 2026
a54cb5c
fix(core): slow down migration progress resync jumps
danilrwx Apr 3, 2026
8fba32e
fix(core): pass state by pointer to persist metric tracking between s…
danilrwx Apr 3, 2026
68708ce
refactor(core): replace manual sync map with LRUExpireCache in progre…
danilrwx Apr 3, 2026
f8ee47e
refactor(core): rewrite migration progress engine with EMA smoothing …
danilrwx Apr 3, 2026
b6b9622
feat(core): detect not converging migration from remaining data stall
danilrwx Apr 3, 2026
b0d1d33
fix(api, vmop): fix migration reason and progress logic
danilrwx Apr 3, 2026
be7c47e
fix(api, vmop): fix getTargetPodDiskError to check Pending pod directly
danilrwx Apr 3, 2026
5619cf3
Revert "chore(core): add 3p-kubevirt branch info"
danilrwx Apr 3, 2026
aa916e7
feat(vmop): support nested migration transfer status
danilrwx Apr 3, 2026
ab23371
chore(vmop): update kubevirt api replace revision
danilrwx Apr 3, 2026
0d83b46
fix(vmop): check target disk errors only in container creating
danilrwx Apr 3, 2026
602ee46
fix(core, kubevirt): move migration transfer fields under transferStatus
danilrwx Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions api/client/kubeclient/async.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (aws *asyncWSRoundTripper) WebsocketCallback(ws *websocket.Conn, resp *http
if resp != nil && resp.StatusCode != http.StatusOK {
return enrichError(err, resp)
}
return fmt.Errorf("Can't connect to websocket: %s\n", err.Error())
return fmt.Errorf("can't connect to websocket: %w", err)
}
aws.Connection <- ws

Expand Down Expand Up @@ -105,7 +105,9 @@ func asyncSubresourceHelper(
}

if response != nil {
defer response.Body.Close()
defer func() {
_ = response.Body.Close()
}()
switch response.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
Expand Down Expand Up @@ -165,7 +167,7 @@ func enrichError(httpErr error, resp *http.Response) error {
if resp == nil {
return httpErr
}
httpErr = fmt.Errorf("Can't connect to websocket (%d): %s\n", resp.StatusCode, httpErr.Error())
httpErr = fmt.Errorf("can't connect to websocket (%d): %w", resp.StatusCode, httpErr)
status := &metav1.Status{}

if resp.Header.Get("Content-Type") != "application/json" {
Expand Down Expand Up @@ -201,7 +203,9 @@ type WebsocketRoundTripper struct {
func (d *WebsocketRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
conn, resp, err := d.Dialer.Dial(r.URL.String(), r.Header)
if err == nil {
defer conn.Close()
defer func() {
_ = conn.Close()
}()
}
return resp, d.Do(conn, resp, err)
}
4 changes: 2 additions & 2 deletions api/client/kubeclient/streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ type wsConn struct {
}

func (c *wsConn) SetDeadline(t time.Time) error {
if err := c.Conn.SetWriteDeadline(t); err != nil {
if err := c.SetWriteDeadline(t); err != nil {
return err
}
return c.Conn.SetReadDeadline(t)
return c.SetReadDeadline(t)
}

func NewWebsocketStreamer(conn *websocket.Conn, done chan struct{}) *wsStreamer {
Expand Down
4 changes: 3 additions & 1 deletion api/client/kubeclient/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ func (s *binaryWriter) Write(p []byte) (int, error) {
if err != nil {
return 0, convert(err)
}
defer w.Close()
defer func() {
_ = w.Close()
}()
n, err := w.Write(p)
return n, err
}
Expand Down
5 changes: 5 additions & 0 deletions api/core/v1alpha2/virtual_machine_operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
// +kubebuilder:subresource:status
// +kubebuilder:resource:categories={virtualization},scope=Namespaced,shortName={vmop},singular=virtualmachineoperation
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="VirtualMachineOperation phase."
// +kubebuilder:printcolumn:name="Progress",type="integer",JSONPath=".status.progress",description="VirtualMachineOperation progress in percent."
// +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".spec.type",description="VirtualMachineOperation type."
// +kubebuilder:printcolumn:name="VirtualMachine",type="string",JSONPath=".spec.virtualMachineName",description="VirtualMachine name."
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time of resource creation."
Expand Down Expand Up @@ -109,6 +110,10 @@ type VirtualMachineOperationCloneCustomization struct {

type VirtualMachineOperationStatus struct {
Phase VMOPPhase `json:"phase"`
// Progress reports operation completion percentage for migration-related VMOPs (Evict/Migrate).
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=100
Progress *int32 `json:"progress,omitempty"`
// The latest detailed observations of the VirtualMachineOperation resource.
Conditions []metav1.Condition `json:"conditions,omitempty"`
// Resource generation last processed by the controller.
Expand Down
36 changes: 36 additions & 0 deletions api/core/v1alpha2/vmopcondition/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,42 @@ const (
// ReasonMigrationRunning is a ReasonCompleted indicating that the migration process is currently in progress.
ReasonMigrationRunning ReasonCompleted = "MigrationRunning"

// ReasonDisksPreparing indicates that migration-related disk preparation is in progress.
ReasonDisksPreparing ReasonCompleted = "DisksPreparing"

// ReasonTargetScheduling indicates that the target pod is being scheduled.
ReasonTargetScheduling ReasonCompleted = "TargetScheduling"

// ReasonTargetUnschedulable indicates that the target pod cannot be scheduled.
ReasonTargetUnschedulable ReasonCompleted = "TargetUnschedulable"

// ReasonTargetDiskError indicates that target disk attachment failed.
ReasonTargetDiskError ReasonCompleted = "TargetDiskError"

// ReasonTargetPreparing indicates that target pod is being prepared.
ReasonTargetPreparing ReasonCompleted = "TargetPreparing"

// ReasonSyncing indicates that source and target are synchronizing migration data.
ReasonSyncing ReasonCompleted = "Syncing"

// ReasonNotConverging indicates that migration cannot converge even with maximum throttling.
ReasonNotConverging ReasonCompleted = "NotConverging"

// ReasonSourceSuspended indicates that source VM has been suspended.
ReasonSourceSuspended ReasonCompleted = "SourceSuspended"

// ReasonTargetResumed indicates that target VM has resumed.
ReasonTargetResumed ReasonCompleted = "TargetResumed"

// ReasonMigrationCompleted indicates that migration has completed successfully.
ReasonMigrationCompleted ReasonCompleted = "Completed"

// ReasonAborted indicates that migration has been aborted.
ReasonAborted ReasonCompleted = "Aborted"

// ReasonFailed indicates that migration failed for an unspecified reason.
ReasonFailed ReasonCompleted = "Failed"

// ReasonOtherMigrationInProgress is a ReasonCompleted indicating that there are other migrations in progress.
ReasonOtherMigrationInProgress ReasonCompleted = "OtherMigrationInProgress"

Expand Down
5 changes: 5 additions & 0 deletions api/core/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build/components/versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ firmware:
libvirt: v10.9.0
edk2: stable202411
core:
3p-kubevirt: v1.6.2-v12n.21
3p-kubevirt: feat/vm/migration-progress
3p-containerized-data-importer: v1.60.3-v12n.17
distribution: 2.8.3
package:
Expand Down
3 changes: 3 additions & 0 deletions crds/doc-ru-virtualmachineoperations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ spec:
* `Completed` — операция прошла успешно;
* `Failed` — операция завершилась неудачно. За подробностями обратитесь к полю `conditions` и событиям;
* `Terminating` — операция удаляется.
progress:
description: |
Прогресс выполнения операции в процентах для миграционных VMOP (`Evict`/`Migrate`).
observedGeneration:
description: |
Поколение ресурса, которое в последний раз обрабатывалось контроллером.
Expand Down
30 changes: 30 additions & 0 deletions crds/embedded/virtualmachineinstances.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4100,6 +4100,36 @@ spec:
description: Lets us know if the vmi is currently running pre
or post copy migration
type: string
transferStatus:
description: TransferStatus contains migration transfer details
reported by the source runtime.
properties:
autoConvergeThrottle:
description: AutoConvergeThrottle is the current auto-converge throttle
reported by the source runtime.
format: int32
type: integer
dataProcessedBytes:
description: DataProcessedBytes is the amount of migration data already
processed by the source runtime.
format: int64
type: integer
dataRemainingBytes:
description: DataRemainingBytes is the amount of migration data still
remaining on the source runtime.
format: int64
type: integer
dataTotalBytes:
description: DataTotalBytes is the total amount of migration data reported
by the source runtime.
format: int64
type: integer
iteration:
description: Iteration is the current migration iteration reported by
the source runtime.
format: int32
type: integer
type: object
sourceNode:
description: The source node that the VMI originated on
type: string
Expand Down
12 changes: 12 additions & 0 deletions crds/virtualmachineoperations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ spec:
jsonPath: .status.phase
name: Phase
type: string
- description: VirtualMachineOperation progress in percent.
jsonPath: .status.progress
name: Progress
type: integer
- description: VirtualMachineOperation type.
jsonPath: .spec.type
name: Type
Expand Down Expand Up @@ -319,6 +323,14 @@ spec:
- Failed
- Terminating
type: string
progress:
description:
Progress reports operation completion percentage for
migration-related VMOPs (Evict/Migrate).
format: int32
maximum: 100
minimum: 0
type: integer
resources:
description:
Resources contains the list of resources that are affected
Expand Down
2 changes: 1 addition & 1 deletion images/virtualization-artifact/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,4 @@ replace (
)

// Kubevirt API replaces
replace kubevirt.io/api => github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v1.6.2-v12n.21
replace kubevirt.io/api => github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v0.0.0-20260403154920-301347b413ce
6 changes: 4 additions & 2 deletions images/virtualization-artifact/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v1.6.2-v12n.21 h1:W0nSf7fOTTLn5lVqR4JR3KctrABzyqb/sCkmSPx2fEY=
github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v1.6.2-v12n.21/go.mod h1:wGZLfRa/b4w/V/hakmfcK0CmrAZGfpj+jN7BMt0s19E=
github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v0.0.0-20260403095053-aefa74c02fee h1:FL3Sn9OL9HZZX01vWiO6t6ps8nkxH+AOilBp+Rdp6iU=
github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v0.0.0-20260403095053-aefa74c02fee/go.mod h1:wGZLfRa/b4w/V/hakmfcK0CmrAZGfpj+jN7BMt0s19E=
github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v0.0.0-20260403154920-301347b413ce h1:b6I/SUcA30j2wcOBBERbN20cKARaDAHNtSzP4XB6kgg=
github.com/deckhouse/3p-kubevirt/staging/src/kubevirt.io/api v0.0.0-20260403154920-301347b413ce/go.mod h1:wGZLfRa/b4w/V/hakmfcK0CmrAZGfpj+jN7BMt0s19E=
github.com/deckhouse/deckhouse/pkg/log v0.0.0-20250226105106-176cd3afcdd5 h1:PsN1E0oxC/+4zdA977txrqUCuObFL3HAuu5Xnud8m8c=
github.com/deckhouse/deckhouse/pkg/log v0.0.0-20250226105106-176cd3afcdd5/go.mod h1:Mk5HRzkc5pIcDIZ2JJ6DPuuqnwhXVkb3you8M8Mg+4w=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,16 @@ func (h *MigratingHandler) syncMigrating(ctx context.Context, s state.VirtualMac

completed, _ := conditions.GetCondition(vmopcondition.TypeCompleted, vmop.Status.Conditions)
switch completed.Reason {
case vmopcondition.ReasonMigrationPending.String():
case vmopcondition.ReasonMigrationPending.String(), vmopcondition.ReasonTargetScheduling.String():
cb.Message("Migration is awaiting start.")

case vmopcondition.ReasonQuotaExceeded.String():
cb.Message(fmt.Sprintf("Migration is pending: %s.", completed.Message))

case vmopcondition.ReasonMigrationPrepareTarget.String():
case vmopcondition.ReasonMigrationPrepareTarget.String(), vmopcondition.ReasonTargetPreparing.String(), vmopcondition.ReasonDisksPreparing.String():
cb.Message("Migration is awaiting target preparation.")

case vmopcondition.ReasonMigrationTargetReady.String():
case vmopcondition.ReasonMigrationTargetReady.String(), vmopcondition.ReasonSyncing.String(), vmopcondition.ReasonSourceSuspended.String(), vmopcondition.ReasonTargetResumed.String():
cb.Message("Migration is awaiting execution.")

case vmopcondition.ReasonWaitingForVirtualMachineToBeReadyToMigrate.String():
Expand All @@ -183,7 +183,7 @@ func (h *MigratingHandler) syncMigrating(ctx context.Context, s state.VirtualMac
case vmopcondition.ReasonMigrationRunning.String():
cb.Status(metav1.ConditionTrue).Reason(vmcondition.ReasonMigratingInProgress)

case vmopcondition.ReasonOperationCompleted.String():
case vmopcondition.ReasonOperationCompleted.String(), vmopcondition.ReasonMigrationCompleted.String():
conditions.RemoveCondition(vmcondition.TypeMigrating, &vm.Status.Conditions)
return nil

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (s MigrationVolumesService) SyncVolumes(ctx context.Context, vmState state.
if vmop != nil {
completed, _ := conditions.GetCondition(vmopcondition.TypeCompleted, vmop.Status.Conditions)
switch completed.Reason {
case vmopcondition.ReasonMigrationPrepareTarget.String(), vmopcondition.ReasonMigrationTargetReady.String(), vmopcondition.ReasonMigrationRunning.String():
case vmopcondition.ReasonMigrationPrepareTarget.String(), vmopcondition.ReasonMigrationTargetReady.String(), vmopcondition.ReasonMigrationRunning.String(), vmopcondition.ReasonTargetPreparing.String(), vmopcondition.ReasonSyncing.String(), vmopcondition.ReasonSourceSuspended.String(), vmopcondition.ReasonTargetResumed.String():
return reconcile.Result{}, nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ var _ = Describe("DeletionHandler", func() {
},
Entry("VMOP Evict 1",
newVmop(v1alpha2.VMOPPhaseInProgress, vmopbuilder.WithType(v1alpha2.VMOPTypeEvict), vmopbuilder.WithVirtualMachine("test-vm")),
newSimpleMigration("vmop-"+name, namespace, "test-vm"), true,
newSimpleMigration("vmop-"+name, "test-vm"), true,
),
Entry("VMOP Evict 2",
newVmop(v1alpha2.VMOPPhaseCompleted, vmopbuilder.WithType(v1alpha2.VMOPTypeEvict), vmopbuilder.WithVirtualMachine("test-vm")),
newSimpleMigration("vmop-"+name, namespace, "test-vm"), false,
newSimpleMigration("vmop-"+name, "test-vm"), false,
),
)
})
Expand Down
Loading
Loading