CoreOps/Volumes/Get-SDPVolume.ps1

<#
    SDPVolume — typed wrapper for a Silk SDP volume.

    Lives co-located with Get-SDPVolume so the type, the default rendering
    setup, and the cmdlet that emits instances are all one click apart.

    Three jobs:

      1. Real .NET-backed object (IDE IntelliSense, predictable
         serialization, strongly typed properties).
      2. Operational verbs as instance methods so a script can do
              $vol.Resize(500) / $vol.Map('host01') / $vol.Delete()
         without rebuilding the cmdlet surface.
      3. Carry a default property set so Get-SDPVolume | Format-Table
         shows a useful narrow column list by default — but every
         property is still there when you ask for it.

    Default table view (name, id, scsi_sn, sizeInGB, creationTime) is
    registered via Update-TypeData below. Format-List * still shows
    everything; direct property access still sees everything.

    sizeInGB and creationTime are computed in the constructor. The raw
    API values (size, creation_time) are retained on the object too in
    case anything downstream still wants them.
#>


class SDPVolume {

    # --- Properties shown in the default table view ---
    [string]   $name
    [string]   $id
    [string]   $scsi_sn
    [int]      $sizeInGB
    [datetime] $creationTime

    # --- Identity (additional) ---
    [int]      $scsi_suffix
    [string]   $iscsi_tgt_converted_name
    [string]   $description

    # --- Sizing (raw + derived) ---
    [long]     $size
    [long]     $logical_capacity
    [long]     $snapshots_logical_capacity

    # --- Volume group ---
    # Stored as the original nested ref object (e.g. @{ref="/volume_groups/4"})
    # so `| Update-SDPRefObjects` can walk it and attach a
    # `volume_group_name` NoteProperty at runtime. Do not flatten.
    [psobject] $volume_group

    # --- Flags ---
    [bool]     $vmware_support
    [bool]     $is_dedup
    [bool]     $is_new
    [bool]     $marked_for_deletion

    # --- Lifecycle ---
    [int]      $creation_time

    # --- Replication ---
    # Same nested-ref shape as volume_group; preserved for Update-SDPRefObjects.
    [psobject] $replication_peer_volume

    # Hidden — used by instance methods to stay on the same SDP context
    # the object came from.
    hidden [string] $context

    SDPVolume() {}

    SDPVolume([psobject] $apiHit, [string] $context) {
        $this.id                       = $apiHit.id
        $this.name                     = $apiHit.name
        $this.scsi_sn                  = $apiHit.scsi_sn
        $this.scsi_suffix              = $apiHit.scsi_suffix
        $this.iscsi_tgt_converted_name = $apiHit.iscsi_tgt_converted_name
        $this.description              = $apiHit.description
        $this.size                     = $apiHit.size
        $this.logical_capacity         = $apiHit.logical_capacity
        $this.snapshots_logical_capacity = $apiHit.snapshots_logical_capacity
        $this.vmware_support           = [bool] $apiHit.vmware_support
        $this.is_dedup                 = [bool] $apiHit.is_dedup
        $this.is_new                   = [bool] $apiHit.is_new
        $this.marked_for_deletion      = [bool] $apiHit.marked_for_deletion
        $this.creation_time            = $apiHit.creation_time
        $this.context                = $context

        # Computed: GB from the API's KB-block size.
        if ($apiHit.size) {
            $this.sizeInGB = [int] ($apiHit.size / 1024 / 1024)
        }

        # Computed: DateTime from the Unix timestamp.
        if ($apiHit.creation_time) {
            $this.creationTime = Convert-SDPTimeStampFrom -timestamp $apiHit.creation_time
        }

        # Refs preserved in their nested-object form so a downstream
        # `| Update-SDPRefObjects` can detect them.
        if ($apiHit.volume_group) {
            $this.volume_group = $apiHit.volume_group
        }
        if ($apiHit.replication_peer_volume) {
            $this.replication_peer_volume = $apiHit.replication_peer_volume
        }
    }

    # ---- Operational methods --------------------------------------------

    [SDPVolume] Resize([int] $newSizeInGB) {
        Set-SDPVolume -id $this.id -sizeInGB $newSizeInGB -context $this.context | Out-Null
        $this.sizeInGB = $newSizeInGB
        $this.size     = $newSizeInGB * 1024 * 1024
        return $this
    }

    [void] Map([string] $hostName) {
        New-SDPHostMapping -hostName $hostName -volumeName $this.name -context $this.context | Out-Null
    }

    [void] Unmap([string] $hostName) {
        Get-SDPHostMapping -hostName $hostName -volumeName $this.name -context $this.context |
            Remove-SDPHostMapping -context $this.context | Out-Null
    }

    [SDPVolume] Refresh() {
        # Mutate $this in place so callers' existing references stay current.
        # Copy declared properties via assignment; carry over Update-SDPRefObjects
        # NoteProperties (volume_group_name, etc.) via Add-Member -Force.
        $fresh = Get-SDPVolume -id $this.id -context $this.context
        foreach ($p in $fresh.PSObject.Properties) {
            if ($this.PSObject.Properties[$p.Name]) {
                $this.($p.Name) = $p.Value
            } else {
                Add-Member -InputObject $this -NotePropertyName $p.Name -NotePropertyValue $p.Value -Force
            }
        }
        return $this
    }

    [void] Delete() {
        Remove-SDPVolume -id $this.id -context $this.context | Out-Null
    }

    [string] ToString() {
        return "$($this.name) ($($this.sizeInGB) GB)"
    }
}

# Default rendering registration. Runs once when this file is dot-sourced
# at module load. -Force lets the file be re-sourced without throwing
# "type already exists."
Update-TypeData -TypeName 'SDPVolume' `
                -DefaultDisplayPropertySet 'name','id','scsi_sn','sizeInGB','creationTime' `
                -Force


<#
    .SYNOPSIS
    Retrieves volume information from the SDP.

    .DESCRIPTION
    Queries for existing volumes on the Silk Data Pod. Can filter by name,
    ID, volume group, or other properties. Returns SDPVolume instances
    that render as a narrow table by default and expose Resize / Map /
    Unmap / Delete methods.

    .PARAMETER description
    Filter volumes by description text.

    .PARAMETER id
    The unique identifier of the volume.

    .PARAMETER name
    The name of the volume to retrieve.

    .PARAMETER vmware_support
    Filter volumes by VMware support flag.

    .PARAMETER volume_group
    Filter volumes by volume group name or ID. Accepts piped input from
    Get-SDPVolumeGroup.

    .PARAMETER context
    Specifies the K2 context to use for authentication. Defaults to
    'sdpconnection'.

    .EXAMPLE
    Get-SDPVolume
    Retrieves all volumes from the SDP.

    .EXAMPLE
    Get-SDPVolume -name "Vol01"
    Retrieves the volume named "Vol01".

    .EXAMPLE
    Get-SDPVolumeGroup -name "VG01" | Get-SDPVolume
    Retrieves all volumes in the volume group "VG01".

    .NOTES
    Authored by J.R. Phillips (GitHub: JayAreP)

    .LINK
    https://github.com/silk-us/silk-sdp-powershell-sdk
#>


function Get-SDPVolume {
    [CmdletBinding()]
    [OutputType([SDPVolume])]
    param(
        # Typed as [object] to dodge module load-order coupling on the
        # SDPVolumeGroup class. Validated to [SDPVolumeGroup] at the top of process.
        [parameter(ValueFromPipeline)]
        [object] $InputObject,
        [parameter()]
        [string] $description,
        [parameter()]
        [int] $id,
        [parameter(Position=1)]
        [ValidateLength(0, 42)]
        [string] $name,
        [parameter()]
        [Alias("VmwareSupport")]
        [bool] $vmware_support,
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias('pipeId')]
        [Alias("VolumeGroup")]
        [string] $volume_group,
        [parameter()]
        [switch] $doNotResolve,
        [parameter()]
        [string] $context = "sdpconnection"
    )

    begin {
        $endpoint = "volumes"
    }

    process {

        if ($InputObject -and $InputObject -isnot [SDPVolumeGroup]) {
            throw "Get-SDPVolume accepts pipeline input only from SDPVolumeGroup; got [$($InputObject.GetType().FullName)]."
        }
        # When piped an SDPVolumeGroup, derive volume_group filter + inherit context.
        if ($InputObject) {
            $volume_group = $InputObject.id
            if (-not $PSBoundParameters.ContainsKey('context')) {
                $context = $InputObject.context
            }
        }
        $PSBoundParameters.Remove('InputObject') | Out-Null

        # Special Ops

        if ($volume_group) {
            Write-Verbose "volume_group specified, parsing SDP object reference"
            $PSBoundParameters.volume_group = ConvertTo-SDPObjectPrefix -ObjectPath volume_groups -ObjectID $volume_group -nestedObject
        }

        # Strip internal-only switches before passing to Invoke-SDPRestCall —
        # they shouldn't end up as URI query parameters.
        $PSBoundParameters.Remove('doNotResolve') | Out-Null

        # Query

        $results = Invoke-SDPRestCall -endpoint $endpoint -method GET -parameterList $PSBoundParameters -context $context -strictURI

        # Emit typed objects so the default view + class methods kick in.

        $instances = foreach ($hit in $results) {
            [SDPVolume]::new($hit, $context)
        }

        # Auto-resolve refs unless the caller opted out. Ref resolution
        # adds `_name` NoteProperties (volume_group_name etc.) which the
        # default table view picks up.
        if ($doNotResolve) {
            $instances
        } else {
            $instances | Update-SDPRefObjects -context $context
        }
    }
}