Venom.psm1

# Module Constants

  # Global/Private constants are available to all nested modules
  # These constants will be required by other nested modules & the Venom module
  Set-Variable -Name AIDir -Value "ArrayInfo"           -Option Constant -Scope Global -Visibility Private -Description "XMLDB folder name";
  Set-Variable -Name CRDir -Value "CredInfo"            -Option Constant -Scope Global -Visibility Private -Description "Credential folder name";
  Set-Variable -Name AIPath -Value "$PWD\$Global:AIDir" -Option Constant -Scope Global -Visibility Private -Description "XMLDB path";
  Set-Variable -Name CRPath -Value "$PWD\$Global:CRDir"  -Option Constant -Scope Global -Visibility Private -Description "Credential path";
  
  $Script:usr = $null
  $Script:pw  = $null
  $Script:org = $null

#

# Non-Exported Functions

  # Dot Sourced Functions

    . $PSScriptRoot\Local\Set-Ignore-SelfSignedCerts.ps1
    . $PSScriptRoot\Local\Export-PSCredentials.ps1
    . $PSScriptRoot\Local\Import-PSCredentials.ps1
    . $PSScriptRoot\Local\Get-ArrayInfoXML.ps1

  #

  # Get-VeArrayInfo functions
    function Select-Report {

      $reports = @();
      $reports += [pscustomobject]@{Array="All-Arrays";
          Description = " Capacity Summary of all block arrays"}
      $reports += [pscustomobject]@{Array="Symmetrix";
          Description = " Capacity Summary of all Symmetrix arrays"};
      $reports += [pscustomobject]@{Array="VPLEX";
          Description = " Summary of all VPLEX arrays"};
      $reports += [pscustomobject]@{Array="Pure";
          Description = " Capacity Summary of all PureSystem arrays"};
      $reports += [pscustomobject]@{Array="IBM";
          Description = " Capacity Summary of all IBM arrays"};
      $reports += [pscustomobject]@{Array="VNX";
          Description = " Capacity Summary of all VNX arrays"};
      $reports += [pscustomobject]@{Array="Trend";
          Description = " Historical Capacity Trend of one or more VMAX arrays"};

      $title = "Select the desired Array Capacity Report"
      $selectA_report = $reports | Out-GridView -Title $title -OutputMode Single;

      if ($selectA_report -eq $null) {
          return $null
      }

      return $selectA_report.Array

    }

    function Convert-ArrayXMLDB {
      begin {$PSArrays = @();}
      process {
        $PSArray = New-Object -typename PSObject
        $PSArray | Add-Member -MemberType NoteProperty -Name "Array" -Value $_.Name
        $PSArray | Add-Member -MemberType NoteProperty -Name "Sid" -Value $_.sid
        $PSArray | Add-Member -MemberType NoteProperty -Name "Org" -Value $_.Org
        $PSArray | Add-Member -MemberType NoteProperty -Name "Class" -Value $_.Class
        $PSArray | Add-Member -MemberType NoteProperty -Name "Model" -Value $_.Model
        $PSArray | Add-Member -MemberType NoteProperty -Name "Usage" -Value $_.usage
        $PSArray | Add-Member -MemberType NoteProperty -Name "Data Center" -Value $_.DataCenter
        #$PSArray | Add-Member -MemberType NoteProperty -Name "Remote" -Value $_.Remote
        $useable = $_.total_usable_tb
        $PSArray | Add-Member -MemberType NoteProperty -Name "Usable (TB)" -Value $useable
        $PSArray | Add-Member -MemberType NoteProperty -Name "Used (TB)" -Value $_.total_used_tb
        $PSArray | Add-Member -MemberType NoteProperty -Name "Free (TB)" -Value $_.total_free_tb
        $PSArray | Add-Member -MemberType NoteProperty -Name "Full%" -Value $_.percent_full
        $PSArray | Add-Member -MemberType NoteProperty -Name "Subs%" -Value $_.subs_percent
        $PSArray | Add-Member -MemberType NoteProperty -Name "T0 Full%" -Value $_.t0_percent_full
        $PSArray | Add-Member -MemberType NoteProperty -Name "T0 Subs%" -Value $_.t0_subs_percent
        $PSArray | Add-Member -MemberType NoteProperty -Name "T1 Full%" -Value $_.t1_percent_full
        $PSArray | Add-Member -MemberType NoteProperty -Name "T1 Subs%" -Value $_.t1_subs_percent
        $PSArray | Add-Member -MemberType NoteProperty -Name "T2 Full%" -Value $_.t2_percent_full
        $PSArray | Add-Member -MemberType NoteProperty -Name "T2 Subs%" -Value $_.t2_subs_percent
        $PSArray | Add-Member -MemberType NoteProperty -Name "Updated" -Value $_.LastUpdated
        $PSArray | Add-Member -MemberType NoteProperty -Name "RestAPI" -Value $_.restapi
        $PSArrays += $PSArray
      }
      # return an array of storage arrays
      end { $PSArrays }
    }

    function Convert-ArrayXMLDB-Trend {
      begin {$PSArrays = @();}
      process {
        $PSArray = New-Object -typename PSObject
        $PSArray | Add-Member -MemberType NoteProperty -Name "Array" -Value $_.Name
        $PSArray | Add-Member -MemberType NoteProperty -Name "Sid" -Value $_.sid
        $PSArray | Add-Member -MemberType NoteProperty -Name "Org" -Value $_.Org
        $PSArray | Add-Member -MemberType NoteProperty -Name "Class" -Value $_.Class
        $PSArray | Add-Member -MemberType NoteProperty -Name "Model" -Value $_.Model
        $PSArray | Add-Member -MemberType NoteProperty -Name "Usage" -Value $_.usage
        $PSArray | Add-Member -MemberType NoteProperty -Name "Data Center" -Value $_.DataCenter

        <#
            $PSArray | Add-Member -MemberType NoteProperty -Name "Remote" -Value $_.Remote
            $PSArray | Add-Member -MemberType NoteProperty -Name "Usable (TB)" -Value $_.total_usable_tb
            $PSArray | Add-Member -MemberType NoteProperty -Name "Used (TB)" -Value $_.total_used_tb
            $PSArray | Add-Member -MemberType NoteProperty -Name "Free (TB)" -Value $_.total_free_tb
            $PSArray | Add-Member -MemberType NoteProperty -Name "Full%" -Value $_.percent_full
            $PSArray | Add-Member -MemberType NoteProperty -Name "Subs%" -Value $_.subs_percent
            $PSArray | Add-Member -MemberType NoteProperty -Name "Updated" -Value $_.LastUpdated
        #>


        $PSArrays += $PSArray
      }
      # return an array of storage arrays
      end { $PSArrays }
    }

    function Convert-TrendXML {
      begin {$PSTrends = @() }
      process {
        $PSTrend = New-Object -typename PSObject
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Array" -Value $_.Name
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Sid" -Value $_.sid
        $PSArray | Add-Member -MemberType NoteProperty -Name "Org" -Value $_.Org
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Class" -Value $_.Class
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Model" -Value $_.Model
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Usage" -Value $_.usage
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Data Center" -Value $_.DataCenter
        #$PSTrend | Add-Member -MemberType NoteProperty -Name "Remote" -Value $_.Remote
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Usable (TB)" -Value $_.total_usable_tb
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Used (TB)" -Value $_.total_used_tb
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Free (TB)" -Value $_.total_free_tb
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Full%" -Value $_.percent_full
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Subs%" -Value $_.subs_percent
        $PSTrend | Add-Member -MemberType NoteProperty -Name "T0 Full%" -Value $_.t0_percent_full
        $PSTrend | Add-Member -MemberType NoteProperty -Name "T0 Subs%" -Value $_.t0_subs_percent
        $PSTrend | Add-Member -MemberType NoteProperty -Name "T1 Full%" -Value $_.t1_percent_full
        $PSTrend | Add-Member -MemberType NoteProperty -Name "T1 Subs%" -Value $_.t1_subs_percent
        $PSTrend | Add-Member -MemberType NoteProperty -Name "T2 Full%" -Value $_.t2_percent_full
        $PSTrend | Add-Member -MemberType NoteProperty -Name "T2 Subs%" -Value $_.t2_subs_percent
        $PSTrend | Add-Member -MemberType NoteProperty -Name "Updated" -Value $_.LastUpdated
        $PSTrends += $PSTrend
      }
      # return an array of storage arrays
      end { $PSTrends }
    }

    function Test-Trend {
        $title = "Select a Storage Array for the Trend Report";
        $doc.SelectNodes("//Array") |
            Where-Object {$_.Model -match "VMAX"} |
            Convert-ArrayXMLDB-Trend | Out-GridView -OutputMode Single -Title $title |
            ForEach-Object { $sid = $_.Sid; $name = $_.Name };
            #ForEach-Object { Write-Host "You selected:" $_.Sid}

        $trd = new-object "System.Xml.XmlDocument"
        $AIFile.Fullname | ForEach-Object {
            $trd.Load($_); $trd.SelectNodes("//Array") |
            Where-Object {$_.sid -match $sid} | Convert-TrendXML
        } | Out-GridView -Title "VMAX Trend Report";

    }

    function Show-VMAX-TrendReport {

      $trd = new-object "System.Xml.XmlDocument"
      $title = "Select one or more VMAX arrays for the Trend Report";
      $doc.SelectNodes("//Array") |
        Where-Object {$_.Model -match "VMAX"} |
        Convert-ArrayXMLDB-Trend | Out-GridView -OutputMode Multiple -Title $title |
        ForEach-Object { $SidSelected = $_.Sid
            $AIFile.Fullname | ForEach-Object {
                $trd.Load($_); $trd.SelectNodes("//Array") |
                Where-Object {$_.sid -match $SidSelected} | Convert-TrendXML
            } # | Out-GridView -Title "VMAX Trend Report" # Grid per SID
        } | Out-GridView -Title "VMAX Trend Report";      # One Grid for all arrays
    }

    function Add-ArrayInfoDB {
      New-Item -Path $PWD\$Global:AIDir -ItemType Directory | Out-Null;
      $tod = Get-Date -Format "MM-dd-yyyy"
      $yed = (Get-Date).AddDays(-1)
      $yo1 = $yed.ToString("MM-dd-yyyy")

      $basenm   = $Global:AIDir
      $newfile  = $Global:AIPath + "\" + $basenm + "-" + $tod + ".xml"
      $oldfile  = $Global:AIPath + "\" + $basenm + "-" + $yo1 + ".xml"

      Copy-Item -Path $PSScriptRoot\Data\ArrayInfo-Example.xml -Destination $newfile;
      Copy-Item -Path $PSScriptRoot\Data\ArrayInfo-Example.xml -Destination $oldfile;

    }
  #

  # Update-VeArrayInfo Functions

    function ConvertSize {
      param(
          [validateset("Bytes","KB","MB","VMAXTK","GB","TB","PB")]
          [string]$From,
          [validateset("Bytes","KB","MB","GB","TB","PB")]
          [string]$To,
          [Parameter(Mandatory=$true)]
          [double]$Value,
          [int]$Precision = 0
      )
      switch($From) {
          "Bytes" {$value = $Value }
          "KB" {$value = $Value * 1024 }
          "MB" {$value = $Value * 1024 * 1024}
        "VMAXTK" {$value = ($Value * $vmaxtk)}
          "GB" {$value = $Value * 1024 * 1024 * 1024}
          "TB" {$value = $Value * 1024 * 1024 * 1024 * 1024}
        "PB" {$value = $Value * 1024 * 1024 * 1024 * 1024 * 1024}
      }
      switch ($To) {
          "Bytes" {return $value}
          "KB" {$Value = $Value/1KB}
          "MB" {$Value = $Value/1MB}
          "GB" {$Value = $Value/1GB}
          "TB" {$Value = $Value/1TB}
          "PB" {$Value = $Value/1PB}

      }

      $rslt = [Math]::Round($value,$Precision,[MidPointRounding]::AwayFromZero)
      [string]$srslt = $rslt
      return $srslt
    }

    function Set-Symcli-Connect {
      param ([string]$connectName)

      if ($env:SYMCLI_CONNECT -eq $null) {
        New-Item -Path env:SYMCLI_CONNECT -Value $connectName | Out-Null
        New-Item -Path env:SYMCLI_CONNECT_TYPE -Value "REMOTE" | Out-Null
        Write-Verbose "New-Item $env:SYMCLI_CONNECT & $env:SYMCLI_CONNECT_TYPE";
        #symcfg discover
      } elseif ($env:SYMCLI_CONNECT -match $connectName) {
      } else {
        Set-Item -Path env:SYMCLI_CONNECT -Value $connectName | Out-Null
        Write-Verbose "Set-Item $env:SYMCLI_CONNECT";
        #symcfg discover
      }
    }

    function Update-Symm-ArrayInfo {
      begin { }

      process {
        $remote = $_.remote;
        $symm = $_.Model
        $symmSid = $_.sid;

        if ($remote -match "none") {
          Write-Host "Skipping $symm : $symmSid no symcli server available" -ForegroundColor Red;
        } else {
          Set-Symcli-Connect $remote
          Write-Host "Processing $symm : $symmSid on $remote" -ForegroundColor Green;

          if ($symm -match "VMAX") {
            #Write-Host "$symm"
            [xml]$xml = symcfg -sid $_.sid list -pool -thin -detail -out xml_element
            if ($LastExitCode -eq 0) { # last command executed without error
              [int]$value = $xml.SymCLI_ML.Symmetrix.Totals.total_usable_tracks_gb
              $_.total_usable_tb = ConvertSize -From GB -TO TB -value $value

              [int]$value = $xml.SymCLI_ML.Symmetrix.Totals.total_used_tracks_gb
              $_.total_used_tb = ConvertSize -From GB -TO TB -value $value

              [int]$value = $xml.SymCLI_ML.Symmetrix.Totals.total_free_tracks_gb
              $_.total_free_tb = ConvertSize -From GB -TO TB -value $value

              $_.percent_full = [string]$xml.SymCLI_ML.Symmetrix.Totals.percent_full
              $_.subs_percent = [string]$xml.SymCLI_ML.Symmetrix.Totals.subs_percent
              $_.LastUpdated = [string]$dt = Get-Date -Format "MM/dd/yyy HH:mm"

              [string]$t0full, [string]$t0subs = " ";
              [string]$t1full, [string]$t1subs = " ";
              [string]$t2full, [string]$t2subs = " ";

              $xml.SymCLI_ML.Symmetrix.DevicePool | ForEach-Object {
                if ($_.technology -eq "EFD") {
                  $t0full = $_.percent_full
                  $t0subs = $_.subs_percent
                }
                if ($_.technology -eq "FC") {
                  $t1full = $_.percent_full
                  $t1subs = $_.subs_percent
                }
                if ($_.technology -eq "SATA") {
                  $t2full = $_.percent_full
                  $t2subs = $_.subs_percent
                }
              }

              $_.t0_percent_full = [string]$t0full
              $_.t0_subs_percent = [string]$t0subs
              $_.t1_percent_full = [string]$t1full
              $_.t1_subs_percent = [string]$t1subs
              $_.t2_percent_full = [string]$t2full
              $_.t2_subs_percent = [string]$t2subs
            }
          } elseif ($symm -match "DMX") {
            #Write-Host "DMX: $symm"
            [xml]$xml = symdisk -sid $_.sid list -dskgrp_summary -out xml_element
            if ($LastExitCode -eq 0) { # last command executed without error
              [int]$value = $xml.SymCLI_ML.Symmetrix.Disk_Group_Summary_Totals.total
              $_.total_usable_tb = ConvertSize -From MB -TO TB -value $value
              [int]$usable = $_.total_usable_tb

              [int]$value = $xml.SymCLI_ML.Symmetrix.Disk_Group_Summary_Totals.free
              $_.total_free_tb = ConvertSize -From MB -TO TB -value $value
              [int]$free = $_.total_free_tb

              [int]$used = $usable - $free
              [string]$sused = $used
              $_.total_used_tb = $sused

              [int]$fullp = [Math]::Round(($used / $usable) * 100, 0)
              [string]$sfullp = $fullp
              $_.percent_full = $sfullp

              $_.subs_percent = "NA"
              $_.LastUpdated = [string]$dt = Get-Date -Format "MM/dd/yyy HH:mm"
            }
          } else {
            Write-Host "Error Processing: $symmSid - $LastExitCode"
          }
        }
      }

      end { }
    }

    function Update-Symm-ArrayInfo1 {

      begin {
        # set requests, to use TLSv1.2 protocol
        [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
        # tell Windows to ignore self-signed certs
        Set-Ignore-SelfSignedCerts;
        $cred = $null;
      }

      process {
        $AIobj            = $_;
        $symm             = $_.Model
        $symmSid         = $_.sid;
        $org        = $_.org
        $symmsn         = $_.sn;
        $URIserver     = $_.restapi;
        $usrname        = $_.username;
        $URIport        = ":8443";
        $userver        = $URIserver + $URIport;

        Write-Host "Processing $symm : $symmSid via RESTAPI to $URIServer" -ForegroundColor Green;

        if ([string]::IsNullOrEmpty($cred)) {  # assume same credentials for all Unisphere servers in Org

          $credBase = "-" + $Script:org + "-" + $env:COMPUTERNAME + $credExt;
          $Script:credfilename = $Global:CRPath + "\" + $usrname + $credBase;
          
          if (!(Test-Path $Script:credfilename)) { # create cache credential
            Write-Host "Unisphere Credentials require for $Script:org Storage" -ForegroundColor Green
            Export-PSCredential $usrname $credBase
            Start-Sleep 3;
          }

          $cred = Import-PSCredential $Script:credfilename
          $Script:usr = $cred.UserName;
          $Script:pw  = $cred.GetNetworkCredential().Password;
          
        }

        $EUP = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $Script:usr,$Script:pw)))
        $Headers=@{'Authorization'="Basic $($EUP)";'Content-type'='application/xml';'Accept'='application/xml'}

        $ierr = $null
        if ($_.Model -match "VMAX2") {
          # https://admassan45:8443/univmax/restapi/provisioning/symmetrix/000292600479/thinpool
          $URI = "https://$userver/univmax/restapi/provisioning/symmetrix/$symmsn/thinpool"
          try { $response = Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers }
          catch {
            Write-Host $_ -ForegroundColor "Red";
            $ierr = $_;
            Write-Verbose "userver: $userver symmsn: $symmsn"
            Write-Verbose $URI
          }
          if ($ierr -eq $null) {
            [int]$total_usable_gb = 0;
            [int]$total_used_gb = 0;
            [int]$total_free_gb = 0;
            [string]$t0full, [string]$t0subs = " ";
            [string]$t1full, [string]$t1subs = " ";
            [string]$t2full, [string]$t2subs = " ";

            $response.listThinPoolResult.poolId | ForEach-Object {
              $tpool = $_;
              # "https://admassan45:8443/univmax/restapi/provisioning/symmetrix/000292600479/thinpool/$tpool"
              $URI = "https://$userver/univmax/restapi/provisioning/symmetrix/$symmsn/thinpool/$tpool"
              try    { $poolResp = Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers }
              catch { Write-Host $_ -ForegroundColor Magenta; $ierr = $_; }

              if ($ierr -eq $null) {
                if ($poolResp.getThinPoolResult.thinPool.diskTechnology -eq "EFD") {
                  $t0full = $poolResp.getThinPoolResult.thinPool.percent_allocated
                  $t0subs = $poolResp.getThinPoolResult.thinPool.percent_subscription
                }
                if ($poolResp.getThinPoolResult.thinPool.diskTechnology -eq "FC") {
                  $t1full = $poolResp.getThinPoolResult.thinPool.percent_allocated
                  $t1subs = $poolResp.getThinPoolResult.thinPool.percent_subscription
                }
                if ($poolResp.getThinPoolResult.thinPool.diskTechnology -eq "SATA") {
                  $t2full = $poolResp.getThinPoolResult.thinPool.percent_allocated
                  $t2subs = $poolResp.getThinPoolResult.thinPool.percent_subscription
                }

                $total_usable_gb    += $poolResp.getThinPoolResult.thinPool.total_gb;
                $total_used_gb         += $poolResp.getThinPoolResult.thinPool.used_gb;
                $total_free_gb        += $poolResp.getThinPoolResult.thinPool.free_gb;

                $AIobj.t0_percent_full    = [string]$t0full
                $AIobj.t0_subs_percent    = [string]$t0subs
                $AIobj.t1_percent_full    = [string]$t1full
                $AIobj.t1_subs_percent    = [string]$t1subs
                $AIobj.t2_percent_full    = [string]$t2full
                $AIobj.t2_subs_percent    = [string]$t2subs
                $AIobj.total_usable_tb    = ConvertSize -From GB -TO TB -value $total_usable_gb
                $AIobj.total_used_tb        =    ConvertSize -From GB -TO TB -value $total_used_gb
                $AIobj.total_free_tb        = ConvertSize -From GB -TO TB -value $total_free_gb

                [double]$full                        = [Math]::Round(($total_used_gb / $total_usable_gb) * 100, 0)
                $AIobj.percent_full            = [string]$full
                #$AIobj.subs_percent = " ";
                [string]$dt                         = Get-Date -Format "MM/dd/yyyy HH:mm"
                $AIobj.LastUpdated             = $dt;
              }
            }
          }
        }

        $ierr = $null
        if ($_.Model -match "VMAX3") {
          # https://pxvmx0316:8443/univmax/restapi/sloprovisioning/symmetrix/000197000316/srp
          $URI = "https://$userver/univmax/restapi/sloprovisioning/symmetrix/$symmsn/srp";
          try { $response = Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers }
          catch { Write-Host $_ -ForegroundColor "Red"; $ierr = $_; }

          if ($ierr -eq $null) {
            [int]$total_usable_gb = 0;
            [int]$total_used_gb = 0;
            [int]$total_free_gb = 0;
            [string]$t0full, [string]$t0subs = " ";
            [string]$t1full, [string]$t1subs = " ";
            [string]$t2full, [string]$t2subs = " ";

            $response.listSRPResult.srpId | ForEach-Object {
              $srpId = $_;
              # https://pxvmx0316:8443/univmax/restapi/sloprovisioning/symmetrix/000197000316/srp/SRP_EFD_R5
              $URI = "https://$userver/univmax/restapi/sloprovisioning/symmetrix/$symmsn/srp/$srpId"
              try    { $srpResp = Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers }
              catch { Write-Host $_ -ForegroundColor Magenta; $ierr = $_; }

              if ($ierr -eq $null) {
                $total_usable_gb    = $srpResp.getSRPResult.srp.total_usable_cap_gb;
                $total_used_gb         = $srpResp.getSRPResult.srp.total_allocated_cap_gb;
                $total_sub_gb            = $srpResp.getSRPResult.srp.total_subscribed_cap_gb;
                $total_free_gb        = $total_usable_gb - $total_used_gb;

                [double]$full            = [Math]::Round(($total_used_gb / $total_usable_gb) * 100, 0)
                $t0full                     = $full
                [double]$sub            = [Math]::Round(($total_sub_gb / $total_usable_gb) * 100, 0)
                $t0subs                        = $sub

                $AIobj.t0_percent_full    = [string]$t0full
                $AIobj.t0_subs_percent    = [string]$t0subs
                $AIobj.t1_percent_full    = [string]$t1full
                $AIobj.t1_subs_percent    = [string]$t1subs
                $AIobj.t2_percent_full    = [string]$t2full
                $AIobj.t2_subs_percent    = [string]$t2subs
                $AIobj.total_usable_tb    = ConvertSize -From GB -TO TB -value $total_usable_gb
                $AIobj.total_used_tb        =    ConvertSize -From GB -TO TB -value $total_used_gb
                $AIobj.total_free_tb        = ConvertSize -From GB -TO TB -value $total_free_gb

                $AIobj.percent_full            = " ";
                $AIobj.subs_percent            = " ";
                [string]$dt                         = Get-Date -Format "MM/dd/yyyy HH:mm"
                $AIobj.LastUpdated             = $dt
              }
            }
          }
        }
      }

      end { }

    }

    function Update-Pure-ArrayInfo {
      begin {

        $xmlDB = Get-ArrayInfoXML;

        if ($Script:org -eq $null) { # org value from first record since none specified
          $unique = $xmlDB.SelectNodes("//Array") |
            Where-Object { $_.Class -match "Pure"} |
            Select-Object -Property Org, username, domainname -Unique;
            $Script:org = $unique[0].Org;
          
        } else {
          $unique = $xmlDB.SelectNodes("//Array") |
            Where-Object { $_.Class -match "Pure" -and $_.Org -match $Script:org} |
            Select-Object -Property Org, username, domainname -Unique;
        }
        
        $usrname = $unique[0].username;
        $credBase = "-" + $Script:org + "-" + $env:COMPUTERNAME + $credExt;
        $Script:credfilename = $Global:CRPath + "\" + $usrname + $credBase;
        Write-Verbose "Pure Credentials : $Script:credfilename";

        if (!(Test-Path $Script:credfilename)) { # create cache credential
          Export-PSCredential $usrname $credBase
          Start-Sleep 3;
        }
        $cred = Import-PSCredential $Script:credfilename
      }

      process {
        $pureIP = $_.remote;
        $pureName = $_.Name;
        Write-Host "Processing Pure Array : $pureName via RESTAPI" -ForegroundColor Green;
        $pure = New-PfaArray -EndPoint $pureIP -Credentials $cred -IgnoreCertificateError;
        $rslt = Get-PfaArraySpaceMetrics -array $pure;

        [double]$usable = $rslt.capacity;
        $_.total_usable_tb = ConvertSize -From Bytes -TO TB -value $usable;

        [double]$vols = $rslt.volumes;
        [double]$shared = $rslt.shared_space;
        [double]$used = $vols + $shared;
        $_.total_used_tb = ConvertSize -From Bytes -TO TB -value $used;

        [double]$free = $usable - $used;
        $_.total_free_tb = ConvertSize -From Bytes -TO TB -value $free;

        [double]$full = ($used/$usable) * 100;
        $value = [Math]::Round($full)
        $_.percent_full = [string]$value

        [int]$sub = $rslt.total_reduction;
        $value = [Math]::Round($sub,1);
        $_.subs_percent = [string]$value;
        [string]$dt = Get-Date -Format "MM/dd/yyy HH:mm"
        $_.LastUpdated = $dt;

      }

      end {

      }
    }

  #

#

# Exported Functions

  <#
      .SYNOPSIS
          Report of capacity metrics of specified storage arrays
          presented in an Excel like column and row table.
 
          Filters and sorting supported.
 
      .DESCRIPTION
          Generates an array of PSObjects based on xml input content
          and presents the metrics in a Out-Gridview table.
 
      .PARAMETER symm
          Switch for reporting on symmetrix arrays only.
 
      .PARAMETER vmax
          Switch for reporting on VMAX arrays only.
 
      .PARAMETER dmx
          Switch for reporting on DMX arrays only
 
      .PARAMETER vnx
          Switch for reporting on VNX arrays only.
 
      .PARAMETER trend
          Processes all the XML files in the sub-directory, 'ArrayInfo'.
          Out-GridView is presented for selection of specific VMAX arrays.
 
      .INPUTS
          Uses the most current file named, ArrayInfo-MM-DD-YYYY.xml in
          the sub-folder called, 'ArrayInfo'.
 
          For trending reports, Out-Gridview is presented for selection
          of one or more VMX arrays.
 
      .OUTPUTS
          Displays a Out-Gridview of the array capacity metrics.
 
      .EXAMPLE
        Get-VeArrayInfo # Out-GridView of all arrays
 
        If multiple options are specified, only first one is processed.
 
      .EXAMPLE
        Get-VeArrayInfo -vnx # Out-GridView of all VNX arrays
 
      .EXAMPLE
          Get-VeArrayInfo -vnx -vmax # 2nd option is ignored.
 
      .EXAMPLE
          Get-VeArrayInfo -trend # Trend Report for selected VMAX arrays.
 
          An Out-Gridview of all VMAX arrays is presented for selection of one or more VMAX arrays.
 
      .NOTES
          Author: Craig Dayton
          0.0.2.0: 07/07/2017 : cadayton : converted to Venom module.
          Updated: 11/31/2016 Added integration with Get-Pure-Metrics.ps1
          Updated: 10/17/2016 added integration with Get-Array-Metrics.ps1
 
      .LINK
          https://github.com/cadayton/Venom
       
      .LINK
          http://venom.readthedocs.io
  #>


  function Get-VeArrayInfo  {
    # Get-ArrayInfo
      [cmdletbinding()]
        Param(
          [Parameter(Position=0,
            Mandatory=$false,
            ValueFromPipeline=$True)]
            [switch]$symm,
          [Parameter(Position=2,
            Mandatory=$false,
            ValueFromPipeline=$True)]
            [switch]$vmax,
          [Parameter(Position=3,
            Mandatory=$false,
            ValueFromPipeline=$True)]
            [switch]$dmx,
          [Parameter(Position=4,
            Mandatory=$false,
            ValueFromPipeline=$True)]
            [switch]$vnx,
          [Parameter(Position=4,
            Mandatory=$false,
            ValueFromPipeline=$True)]
            [switch]$trend
        )

    #

    # Main Routine

      Write-Host "Get-VeArrayInfo version 0.0.2.0" -ForegroundColor Green

      if (!(Test-Path $Global:AIDir)) { Add-ArrayInfoDB }

      $AIFile = Get-ChildItem -Path $Global:AIPath |
          Where-Object {$_.PSChildName -like "ArrayInfo*.xml"} |
          Sort-object -property @{Expression={$_.LastWriteTime}; Ascending=$false};

      $FileIn = $Global:AIPath + "\" + $AIFile.PSChildName[0];

      $doc = new-object "System.Xml.XmlDocument"
      $doc.Load($FileIn)

      if ($symm) {
        $title = "List of Symmetrix storage arrays";
        $doc.SelectNodes("//Array") | Where-Object {$_.Class -match "Symmetrix"} | Convert-ArrayXMLDB | Out-GridView -Title $title
      } elseif ($vmax) {
        $title = "List of VMAX storage arrays";
        $doc.SelectNodes("//Array") | Where-Object {$_.Model -match "VMAX"} | Convert-ArrayXMLDB | Out-GridView -Title $title
      } elseif ($dmx) {
        $title = "List of DMX storage arrays";
        $doc.SelectNodes("//Array") | Where-Object {$_.Model -match "DMX"} | Convert-ArrayXMLDB | Out-GridView -Title $title
      } elseif ($vnx) {
        $title = "List of VNX storage arrays";
        $doc.SelectNodes("//Array") | Where-Object {$_.Class -match "VNX"} | Convert-ArrayXMLDB | Out-GridView -Title $title
      } elseif ($trend) {
        Show-VMAX-TrendReport
      } else {
        $rptloop = $true;
        While ($rptloop) {
          $rptType = Select-Report;

          switch ($rptType) {
            "All-Arrays" {
              $title = "List of All storage arrays";
              $report = $doc.SelectNodes("//Array") |
                  Convert-ArrayXMLDB |
                  Out-GridView -Title $title -OutputMode Single
            }
            "Symmetrix" {
              $title = "List of Symmetrix storage arrays";
              $report = $doc.SelectNodes("//Array") |
                  Where-Object {$_.Class -match "Symmetrix"} |
                  Convert-ArrayXMLDB |
                  Out-GridView -Title $title -OutputMode Single
              if ($report -ne $null) {
                  $selsid = $report.sid;
                  $unisphere = $report.restapi
                  Write-Host "Display Performance Reports for VMAX $selsid (Y or N)?" -NoNewline -ForegroundColor Green
                  [string]$resp = Read-Host;
                  if ($resp -match "Y") {
                      Get-VeSymmMetrics $unisphere $selsid
                  };
              }
            }
            "VPLEX" {
              $title = "List of VPLEX storage arrays";
              $report = $doc.SelectNodes("//Array") |
                  Where-Object {$_.Class -match "VPLEX"} |
                  Convert-ArrayXMLDB |
                  Out-GridView -Title $title -OutputMode Single
              $vplex = $report.Array
              Write-Host "Display $rptType Performance metrics for $vplex (Y or N)?" -NoNewline -ForegroundColor Green
              [string]$resp = Read-Host;
              if (($resp -match "Y") -and ($report -ne $null)) { Get-VeVPlexMetrics $vplex  }
            }
            "Pure" {
              $title = "List of PureSystem storage arrays";
              $report = $doc.SelectNodes("//Array") |
                  Where-Object {$_.Class -match "Pure"} |
                  Convert-ArrayXMLDB |
                  Out-GridView -Title $title -OutputMode Single
              $pure = $report.Array
              Write-Host "Display $rptType Performance metrics(Y or N)?" -NoNewline -ForegroundColor Green
              [string]$resp = Read-Host;
              if (($resp -match "Y") -and ($report -ne $null)) { Get-VePureMetrics }
            }
            "IBM" {
              $title = "List of Fraud IBM storage arrays";
              $report = $doc.SelectNodes("//Array") |
                  Where-Object {$_.Class -match "IBM"} |
                  Convert-ArrayXMLDB |
                  Out-GridView -Title $title -OutputMode Single
            }
            "VNX" {
              $title = "List of VNX storage arrays";
              $report = $doc.SelectNodes("//Array") |
                  Where-Object {$_.Class -match "VNX"} |
                  Convert-ArrayXMLDB |
                  Out-GridView -Title $title -OutputMode Single
            }
            "Trend" {
              Show-VMAX-TrendReport
              $report = "none"
            }
            Default {
                Write-Host "Report operation cancelled" -ForegroundColor Red
            }
          }

          Write-Host "Select a different Array report (Y or N)?" -NoNewline -ForegroundColor Green
          [string]$resp = Read-Host;
          if ($resp -match "N") { $rptloop = $false };

          # if ($report -ne $null) { Performance-Report $report }
          #if ($report -ne $null) { Get-VeVPlexMetrics }
        }
      }

    #

  }

  <#
    .SYNOPSIS
      By default, the Unisphere RESTAPI is called to collect capacity
      metrics for each of the symmetrix arrays.
 
      PureSystem's PureStoragePowerShellSDK is used update
      Pure Storage arrays.
 
    .DESCRIPTION
      The most recent XMLDB file titled, ArrayInfo-MM-DD-YYYY.xml within the
      sub-directory, ArrayInfo is loaded as input.
 
    .PARAMETER hostnm
      Host name of where the symcli commands will be executed.
 
      If offline is true, the assumption is the host is a unix
      host and SSH will be used to get an offline copy of the
      symapi database.
 
      If offline is false, the assumption is the host is a Windows
      host with the symapi server implemented for processing symcli
      remote commands.
 
    .PARAMETER netcfgnm
      By default, the value is 'restapi' which invokes the Unisphere
      RESTAPI.
 
      If it is desired to use the symcli, then the value should be a
      name listed in the netcfg file to reference the symcli hostnm.
 
    .PARAMETER org
      Limit the update to a specific organization value in the XMLDB.
 
      By default, the update will be limited organization of the first
      record in the XMLDB.
 
    .INPUTS
      /ArrayInfo/ArrayInfo-MM-DD-YYYY.xml
 
    .OUTPUTS
      A new file as /ArrayInfo/ArrayInfo-MM-DD-YYYY.xml
 
    .EXAMPLE
      Update-VeArrayInfo
 
      The RESTAPI is used to update capacity metrics in the new ArrayInfo file.
 
      Get-VeArrayInfo.ps1 is used to display the array information using the
      Out-GridView feature within PowerShell.
 
      By default the updates are limited to organization of the first record
      found in the XMLDB.
 
    .EXAMPLE
      Update-VeArrayInfo -org Mojo
 
      Limits updates to the records in the Mojo organization.
 
    .NOTES
 
      Must have PureStoragePowerShellSDK installed to support Pure arrays.
 
      Execute the following to install PureStorage PowerShell module.
      Install-Module -Name PureStoragePowerShellSDK
 
      Author: Craig Dayton
      0.0.2.1: 07/09/2017 : cadayton : Added 'org' parameter for selection of specific records.
      0.0.2.0: 07/07/2017 : cadayton : converted to Venom module.
      Updated: 01/05/2016 - Added support for Unisphere RESTAPI
      Updated: 10/29/2016 - Added Support for Pure Arrays
      Updated: 08/24/2015 - Initial Release
 
    .LINK
      https://github.com/cadayton/Venom
     
    .LINK
      http://venom.readthedocs.io
 
     
 
  #>

  function Update-VeArrayInfo {
    #.TODO
        # Backup provisioning groups: symaccess -sid 2394 backup -f backup_2394.grps
    #

    # Update-VeArrayInfo
      [cmdletbinding()]
        Param(
          [Parameter(Position=0,
            Mandatory=$false,
            ValueFromPipeline=$True)]
          [string]$hostnm = "smcmgmt01",
          [Parameter(Position=1,
            Mandatory=$false,
            ValueFromPipeline=$True)]
          [string]$netcfgnm = "restapi",
          [Parameter(Position=2,
            Mandatory=$false,
            ValueFromPipeline=$True)]
          [string]$org= $null
        )

    #

    # Declarations

      New-Variable -Name vmaxtk -Value 64KB -Option Constant
      Set-Variable credExt ".enc.xml" -Option Constant

    #

    # Main Routine

      <# Logic for moving data to share location
        #todo: may need to add credentials to access share
 
        $cShare = "\\my.share.org\scratch"
        $cName = "AI"
        New-PSDrive -name $cName -psprovider filesystem -Root $cShare | Out-Null
        $NetPath = $cName + ":\$Global:AIDir";
      #>


      Write-Host "Update-VeArrayInfo version 0.0.2.1" -ForegroundColor Green

      $AIFile = Get-ChildItem -Path $Global:AIPath |
        Where-Object {$_.PSChildName -like "ArrayInfo*.xml"} |
        Sort-object -property @{Expression={$_.LastWriteTime}; Ascending=$false};

      $FileIn = $AIFile.FullName[0]
      $basenm,$x = $FileIn.split("-")
      $tod = Get-Date -Format "MM-dd-yyyy"
      $newfile = $basenm + "-" + $tod + ".xml"

      $Script:org = $org;

      if ((Test-Path $FileIn)) { # input file exist?
        $doc = new-object "System.Xml.XmlDocument"
        $doc.Load($FileIn)

        # Set or Validate the 'org' value to use for the updates.

          if ([string]::IsNullOrEmpty($org)) { # org value not specified, so set it
            $unique = $doc.SelectNodes("//Array") |
              Where-Object { $_.Model -match "VMAX"} |
              Select-Object -Property Org, username, domainname -Unique;
              $Script:org = $unique[0].Org
          } else {
            $unique = $doc.SelectNodes("//Array") |
              Where-Object { $_.Model -match "VMAX" -and $_.Org -match $org} |
              Select-Object -Property Org, username, domainname -Unique;

            if ($unique -is [Object]) { # Valid org value specified
              $Script:org = $org
            } else {
              Write-Host "The org value " -ForegroundColor Blue -NoNewline
              Write-Host $org -ForegroundColor Red -NoNewline
              Write-Host " was not found in the XMLDB" -ForegroundColor Blue;
              Write-Host "Check your spelling. The valid org values are:" -ForegroundColor Green;
              $unique = $doc.SelectNodes("//Array") |
                Where-Object { $_.Model -match "VMAX"} |
                Select-Object -Property Org -Unique;
              $unique;
              return $null;
            }
          }
        #

        if ($netcfgnm -match "restapi") {
          $doc.SelectNodes("//Array") | Where-Object {$_.Class -match "Symmetrix" -and $_.Org -match $Script:org} | Update-Symm-ArrayInfo1
        } else {
          $doc.SelectNodes("//Array") | Where-Object {$_.Class -match "Symmetrix" -and $_.Org -match $Script:org} | Update-Symm-ArrayInfo
        }

        $doc.SelectNodes("//Array") | Where-Object {$_.Class -match "Pure" -and $_.Org -match $Script:org} | Update-Pure-ArrayInfo

        # need to generate new xml file ArrayInfo-dd-mm-yyyy.xml
        $doc.Save($newfile);

        <# copy to share location
          Copy-Item $newfile -Destination $NetPath -Force | Out-Null
          Remove-PSDrive -name $cName | Out-Null
        #>


      } else {
        Write-Host "$FileIn not found" -ForegroundColor Red
      }

    #

  }

#