Skip to content

'Proxy' object has no attribute 'lsvolumesnapshot' on CreateVolume-from-snapshot when SVC firmware lacks the snapshot-2.0 framework #873

Description

@bluecmd

'Proxy' object has no attribute 'lsvolumesnapshot' on CreateVolume-from-snapshot when SVC firmware lacks the snapshot-2.0 framework

Disclosure: this report was drafted by an AI coding assistant (Claude) working under my direction. I ran the reproductions on my own cluster, reviewed the analysis, and am posting it from my account; the prose, code citations, and suggested patches were AI-generated and have not been validated against IBM's internal test suite. Treat the patch diffs as suggestions, not as a tested fix.

TL;DR

On a Spectrum Virtualize backend whose CLI schema does not expose addsnapshot / lsvolumesnapshot, the driver behaves inconsistently when a StorageClass sets virt_snap_func: "true":

Path Behavior Expected
CreateSnapshot from PVC Silently falls back to legacy FlashCopy (mkfcmap) ✅ OK
CreateVolume from PVC clone (dataSourceRef: PersistentVolumeClaim) Fails with misleading error Snapshot function is enabled but not supported with object : csi_pvc-<destination-uid> Should fall back to legacy FlashCopy clone, OR error must blame the source, not the destination
CreateVolume from VolumeSnapshot (dataSourceRef: VolumeSnapshot) Uncaught AttributeError: 'Proxy' object has no attribute 'lsvolumesnapshot' — controller returns Internal to the CSI external-provisioner, which retries forever Should detect missing capability and either fall back to legacy FlashCopy lookup or fail cleanly with VirtSnapshotFunctionNotSupportedMessage

The CreateSnapshot path correctly gates on _is_vdisk_support_addsnapshot() before using the snapshot-2.0 API; the CreateVolume-from-source paths do not perform the equivalent check, so a single virt_snap_func: "true" on the StorageClass produces an environment where snapshots can be created but never consumed.

Closest existing report: #844 ("1.13 create a VM", open, no follow-up) — that one shows the related AttributeError: volume_group_name traceback under the same conditions. Filing this separately because the underlying defect class (no firmware-capability gating on the CreateVolume-from-source code paths) has at least three distinct manifestations.

Environment

  • Driver: quay.io/ibmcsiblock/ibm-block-csi-driver-controller:1.13.1
  • Sidecars: csi-provisioner v4.0.1, csi-attacher v4.9.0, csi-snapshotter v8.3.0, csi-resizer v1.14.0
  • Kubernetes: 1.35.2 (Talos)
  • Backend: IBM Spectrum Virtualize (SVC). Symptom-defined: CLI schema does not include addsnapshot / lsvolumesnapshot; legacy FlashCopy commands (mkfcmap, lsvdisk, etc.) are present. _is_addsnapshot_supported() returns False.
  • CDI: v1.65.0 (KubeVirt 1.8.2) — but the bug reproduces without CDI; see the bare kubectl apply recipes below.

StorageClass under test

Mirrors the documented Snapshot-2.0 setup:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: tera-compressed
provisioner: block.csi.ibm.com
parameters:
  pool: Pool0_CSI
  SpaceEfficiency: dedup_compressed
  volume_name_prefix: csi
  virt_snap_func: "true"
  csi.storage.k8s.io/fstype: ext4
  csi.storage.k8s.io/secret-name: tera-secret
  csi.storage.k8s.io/secret-namespace: ibm-block-csi
allowVolumeExpansion: true

VolumeSnapshotClass:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata: {name: ibm-block}
driver: block.csi.ibm.com
deletionPolicy: Delete
parameters:
  csi.storage.k8s.io/snapshotter-secret-name: tera-secret
  csi.storage.k8s.io/snapshotter-secret-namespace: ibm-block-csi

Reproduction

Step 1 — source PVC + snapshot (these succeed)

apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: src, namespace: vdi}
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: tera-compressed
  volumeMode: Filesystem
  resources: {requests: {storage: 2Gi}}
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata: {name: snap, namespace: vdi}
spec:
  volumeSnapshotClassName: ibm-block
  source: {persistentVolumeClaimName: src}

kubectl get vs -n vdi snap:

NAME   READYTOUSE   ...
snap   true         (created in ~2s)

VolumeSnapshotContent snapshotHandle is SVC:<id>;<wwn> — a regular volume handle, not a snapshot-2.0 handle, confirming the driver took the legacy _create_snapshot path (mkfcmap).

Controller log excerpt:

INFO  array_mediator_svc.py:create_snapshot:1165 - creating snapshot 'CSI_snapshot-...' from volume '<wwn>'
INFO  array_mediator_svc.py:create_snapshot:1189 - finished creating snapshot 'CSI_snapshot-...' from volume '<wwn>'

Step 2 — clone PVC from source (Bug A)

apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: clone, namespace: vdi}
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: tera-compressed
  volumeMode: Filesystem
  resources: {requests: {storage: 2Gi}}   # exact source size, intentionally
  dataSourceRef:
    kind: PersistentVolumeClaim
    name: src

PVC stays Pending, event:

Warning  ProvisioningFailed  ...  failed to provision volume with StorageClass "tera-compressed":
  rpc error: code = InvalidArgument
  desc = Snapshot function is enabled but not supported with object : csi_pvc-<destination-uid>

Controller log:

DEBUG csi_controller_server.py:CreateVolume:107 - volume was not found. creating a new volume with parameters: {... 'virt_snap_func': 'true' ...}
ERROR exception_handler.py:handle_exception:35 - Snapshot function is enabled but not supported with object : csi_pvc-<destination-uid>
controllers.array_action.errors.VirtSnapshotFunctionNotSupportedMessage: Snapshot function is enabled but not supported with object : csi_pvc-<destination-uid>

Source of the check is in create_volume (array_mediator_svc.py ~L934):

if is_virt_snap_func and source_ids:
    if self._is_vdisk_support_addsnapshot(source_ids.uid):
        self._create_cli_volume_from_source(...)
    else:
        raise array_errors.VirtSnapshotFunctionNotSupportedMessage(name)

Two problems:

  1. Misleading message. The unsupported object is the source (source_ids.uid), not the destination name being created. VirtSnapshotFunctionNotSupportedMessage(source_ids.uid) would be accurate.
  2. No fallback. A legacy FlashCopy clone (the same mkfcmap path used by CreateSnapshot) would satisfy the request. Falling back when _is_addsnapshot_supported() is False would let virt_snap_func: "true" StorageClasses keep working on older firmware instead of silently breaking all clone operations.

Step 3 — restore PVC from snapshot (Bug B — the AttributeError)

apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: restore, namespace: vdi}
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: tera-compressed
  volumeMode: Filesystem
  resources: {requests: {storage: 2Gi}}
  dataSourceRef:
    apiGroup: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: snap

PVC stays Pending, event:

Warning  ProvisioningFailed  ...  failed to provision volume with StorageClass "tera-compressed":
  rpc error: code = Internal
  desc = 'Proxy' object has no attribute 'lsvolumesnapshot'

Controller log:

ERROR exception_handler.py:handle_exception:35 - 'Proxy' object has no attribute 'lsvolumesnapshot'
Traceback (most recent call last):
  File "/driver/controllers/array_action/array_mediator_svc.py", line 2039, in _lsvolumesnapshot
    return self.client.svcinfo.lsvolumesnapshot(**kwargs).as_single_element
AttributeError: 'Proxy' object has no attribute 'lsvolumesnapshot'

Call chain (HEAD = 1ebeb4a):

  1. csi_controller_server.CreateVolume computes use_snap_object = bool(virt_snap_func and not partition_name).

  2. With use_snap_object=True and source_type == "snapshot", calls array_mediator.get_object_by_id(source_id, source_type, use_snap_object).

  3. get_object_by_id (array_mediator_svc.py ~L994):

    if is_virt_snap_func and object_type == controller_settings.SNAPSHOT_TYPE_NAME:
        cli_snapshot = self._get_cli_snapshot_by_id(object_id)        # ← no capability gate
        ...
  4. _get_cli_snapshot_by_id_lsvolumesnapshotself.client.svcinfo.lsvolumesnapshot(...)AttributeError.

Contrast with get_snapshot (array_mediator_svc.py ~L975) which does gate:

def get_snapshot(self, volume_id, snapshot_name, pool, is_virt_snap_func):
    if is_virt_snap_func:
        if self._is_addsnapshot_supported():
            cli_snapshot = self._get_cli_snapshot_by_name(snapshot_name)
            ...
        raise array_errors.VirtSnapshotFunctionNotSupportedMessage(volume_id)

get_object_by_id and create_snapshot's branch are missing the same guard.

Suggested fix (proposed patch, untested upstream)

--- a/controllers/array_action/array_mediator_svc.py
+++ b/controllers/array_action/array_mediator_svc.py
@@ -994,6 +994,8 @@ class SVCArrayMediator(ArrayMediatorAbstract, VolumeGroupInterface):

     def get_object_by_id(self, object_id, object_type, is_virt_snap_func=False):
         if is_virt_snap_func and object_type == controller_settings.SNAPSHOT_TYPE_NAME:
+            if not self._is_addsnapshot_supported():
+                raise array_errors.VirtSnapshotFunctionNotSupportedMessage(object_id)
             cli_snapshot = self._get_cli_snapshot_by_id(object_id)
             if not cli_snapshot:
                 return None

That converts the uncaught AttributeError (gRPC Internal, retry storm) into a deterministic InvalidArgument, matching how get_snapshot already handles the same case.

A more user-friendly fix is to fall back to the legacy lookup path (_get_cli_volume_by_wwn) when _is_addsnapshot_supported() is False, since the snapshot that exists on the array is a legacy FlashCopy target (created by the same driver, same release, against the same SC). That would let users keep virt_snap_func: "true" on the SC and have clones / snapshot restores actually work via mkfcmap.

A small auxiliary fix for Bug A:

--- a/controllers/array_action/array_mediator_svc.py
+++ b/controllers/array_action/array_mediator_svc.py
@@ -940,7 +940,7 @@ class SVCArrayMediator(ArrayMediatorAbstract, VolumeGroupInterface):
             if self._is_vdisk_support_addsnapshot(source_ids.uid):
                 self._create_cli_volume_from_source(name, pool, io_group, volume_group, source_ids,
                                                     source_type)
             else:
-                raise array_errors.VirtSnapshotFunctionNotSupportedMessage(name)
+                raise array_errors.VirtSnapshotFunctionNotSupportedMessage(source_ids.uid)

(Cosmetic — accurately blames the source.)

Workaround (in case anyone else hits this)

Provision a parallel StorageClass without virt_snap_func: "true". PVC→PVC clones via dataSourceRef: PersistentVolumeClaim then take the legacy FlashCopy path and bind in ~5–7 s. VolumeSnapshot creation still works on the virt_snap_func: "true" SC; only CreateVolume-from-snapshot is broken on this firmware.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions