SpectrumControlBase-Client.psm1
############################################################################## # # # Licensed Materials - Property of IBM # # # # IBM Storage Automation Plugin for PowerShell # # # # (C) COPYRIGHT International Business Machines Corp. 2013, 2016 # # All Rights Reserved # # # # US Government Users Restricted Rights: Use, duplication or # # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # # ############################################################################## # requires -Version 3 # # Module Definition for module 'SpectrumControlBase-Client' # # Generated by: Gino Lv (lvlch@cn.ibm.com) # # Generated on: Jun 20, 2016 # #region Variable Definition Set-Variable -Name API_ENTRY -Value "api/v1" Set-Variable -Name RT_CAPABILITY_VALUES -Value "capability-values" -Option ReadOnly Set-Variable -Name RT_CONTAINERS -Value "containers" -Option ReadOnly Set-Variable -Name RT_DISKS -Value "disks" -Option ReadOnly Set-Variable -Name RT_DOWNLOADS -Value "downloads" -Option ReadOnly Set-Variable -Name RT_HOSTS -Value "hosts" -Option ReadOnly Set-Variable -Name RT_HOST_INITIATORS -Value "host_initiators" -Option ReadOnly Set-Variable -Name RT_INTERFACES -Value "interfaces" -Option ReadOnly Set-Variable -Name RT_MAPPINGS -Value "mappings" -Option ReadOnly Set-Variable -Name RT_MODULES -Value "modules" -Option ReadOnly Set-Variable -Name RT_POOLS -Value "pools" -Option ReadOnly Set-Variable -Name RT_PORTS -Value "ports" -Option ReadOnly Set-Variable -Name RT_SERVICES -Value "services" -Option ReadOnly Set-Variable -Name RT_SETTINGS -Value "settings" -Option ReadOnly Set-Variable -Name RT_STORAGE_ATTRIBUTES -Value "storage-attributes" -Option ReadOnly Set-Variable -Name RT_STORAGE_RESOURCES -Value "storage-resources" -Option ReadOnly Set-Variable -Name RT_STORAGE_SYSTEMS -Value "arrays" -Option ReadOnly Set-Variable -Name RT_TASKS -Value "tasks" -Option ReadOnly Set-Variable -Name RT_AUTH_TOKEN -Value "users/get-auth-token" -Option ReadOnly Set-Variable -Name RT_USERS -Value "users" -Option ReadOnly Set-Variable -Name RT_VOLUMES -Value "volumes" -Option ReadOnly $Global:ModuleName="SpectrumControlBase-Client" $Global:DefaultConnection=$null #Handle to a most recently used SCBConnection; #endregion #region Classe Definition try{ Add-Type @" using System; using System.Net; using System.Security; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Management.Automation; using System.Runtime.InteropServices; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } public class SCBConnection{ private PSCredential _credential; public Uri ConnectionUri {get; set; } public string UserName {get{return _credential.UserName;} } public string Token {get;set;} private Dictionary<int,string> _capabilityValues= new Dictionary<int,string>(); public void SetCapability(IDictionary<int,string> capValues){ if(_capabilityValues==null) _capabilityValues= new Dictionary<int,string>(); _capabilityValues.Clear(); foreach(int key in capValues.Keys){ _capabilityValues.Add(key,capValues[key]); } } public IDictionary<int,string> GetCapability(){ return _capabilityValues; } public string GetTokenString(){ if(string.IsNullOrEmpty(Token)) return null; else return "Token " +Token; } public PSCredential GetCredential(){ return _credential; } public void SetCredential(PSCredential credential){ if(credential != null){ _credential=credential; } } public void SetCredential(string username,string password){ if(!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)){ var secureString=new SecureString(); foreach(char ch in password)secureString.AppendChar(ch); _credential= new PSCredential(username,secureString); } } public string ResolvePassword(){ if(_credential==null) return null; else{ var ptrPassword=Marshal.SecureStringToBSTR(_credential.Password); return Marshal.PtrToStringBSTR(ptrPassword); } } } public class SCBMappingInfor{ private Dictionary<string, string> _internalResourceType = new Dictionary<string, string>() { { "SCBToken","$RT_AUTH_TOKEN"}, { "SCBSpace","$RT_CONTAINERS" }, { "SCBService","$RT_SERVICES"}, { "SCBVolume","$RT_VOLUMES"}, { "SCBHost","$RT_HOSTS"}, { "SCBHostInitiator","$RT_HOST_INITIATORS"}, { "SCBCapabilityValue","$RT_CAPABILITY_VALUES"}, { "SCBHostVolMapping","$RT_MAPPINGS"}, }; public string ObjectName{get;set;} public string ResourceType{get{if(ObjectName==null)return null;else return _internalResourceType[ObjectName];}} public Dictionary<string,string> FieldsToRenamed = new Dictionary<string, string>(); public List<string> FieldsToRemoved = new List<string>(); public List<string> FieldsToPostprocessed = new List<string>(); } public class SCBRequest{ public SCBConnection SCBConnection { get; set; } public SCBMappingInfor MappingInfor = new SCBMappingInfor(); public string Method { get; set; } public string Key{get;set;} public Dictionary<string, string> Header = new Dictionary<string, string>(); public Dictionary<string, string> UrlParams = new Dictionary<string, string>(); public Dictionary<string, object> Body = new Dictionary<string, object>(); public string Uri { get { string str=""; if (!string.IsNullOrEmpty(Key)) { str=string.Format("{0}/{1}/{2}/{3}", SCBConnection.ConnectionUri.AbsoluteUri.Trim('/'), "$API_ENTRY", MappingInfor.ResourceType, Key); } else if (UrlParams.Count > 0) { string queryStr = ""; foreach (string query in UrlParams.Keys) { if(string.IsNullOrEmpty(UrlParams[query])) continue; queryStr += "&" + string.Format("{0}={1}", query, UrlParams[query]); } str=string.Format("{0}/{1}/{2}?{3}", SCBConnection.ConnectionUri.AbsoluteUri.Trim('/'), "$API_ENTRY", MappingInfor.ResourceType, queryStr.Trim('&')); } else { str=string.Format("{0}/{1}/{2}", SCBConnection.ConnectionUri.AbsoluteUri.Trim('/'), "$API_ENTRY", MappingInfor.ResourceType); } return (new Uri(str)).AbsoluteUri; } } } "@ } catch{ Write-Warning "The classes are partially registered!" } #endregion #region Initilization #Will not check the certificate policy [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy #Manually enable the high level protocols TLS1.1|TLS1.2 [System.Net.ServicePointManager]::SecurityProtocol=[System.Net.SecurityProtocolType]::Tls12,[System.Net.SecurityProtocolType]::Tls11,[System.Net.SecurityProtocolType]::Tls,[System.Net.SecurityProtocolType]::Ssl3 #endregion #region Function Definition function AnalyzeException{ param( [Object] $Exception, [ref] $SCBConnectionRef, [String] $ErrorInCmdlet ) $CmdletName="AnalyzeException" Write-Verbose "$CmdletName`: Exception: $($Exception.Message)" Write-Verbose "$CmdletName`: SCBConnection: $($SCBConnectionRef.Value|ConvertTo-Json -Compress)" $Message="" if($Exception -Is "System.Net.WebException"){ Write-Verbose "$CmdletName`: The exception is a WebException." if($Exception.Response -ne $null){ $statusCode=$Exception.Response.StatusCode $ErrorCode=[int]$Exception.Response.StatusCode $result = $Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $response = $reader.ReadToEnd() Write-Verbose "$CmdletName`: Response: $response" if(![String]::IsNullOrEmpty($response)){ $msgPattern="{.*non_field_errors.*:[\S\s]*?\[""(?<Message>.*)""\]}|^{""detail"":[\S\s]*?""(?<Message>.*)""}$" $volNamePattern="Volume is: (?<VolumeName>\w{1,})." $match=[System.Text.RegularExpressions.Regex]::Match($response,$msgPattern) if($match.Success){ $Message=$match.Groups["Message"].Value }else{ $Message=$response } $match=[System.Text.RegularExpressions.Regex]::Match($response,$volNamePattern) if($match.Success){ $volInfo=if([String]::IsNullOrEmpty($match.Groups["VolumeName"].Value)){""}else{" The volume is " + $match.Groups["VolumeName"].Value +"."} } switch($ErrorCode){ 401{ if($response.Contains("Invalid token") -Or $response.Contains("Token has expired")){ $Message="Cannot process this command because this connection is invalid or has expired. Please re-issue the ""New-SCBConnection"" command to get a new connection." } } 500{ if($response.Contains("CMMVC8698E")){ #Failed for the volume has host mappings $Message="The command failed for the volume has host mappings." + $volInfo+ " Please use $ErrorInCmdlet -Force." break } if($response.Contains("CMMVC7019E")){ $Message="The command failed because the volume size is not a multiple of 512 bytes." + $volInfo break } if($response.Contains("A volume with this name already exists")){ $Message="The command failed because a volume with this name already exists." + $volInfo break } } default{ Write-Verbose "Unhandled StatusCode: $StatusCode; Message: $response" } } } } if([String]::IsNullOrEmpty($Message)){$Message=$Exception.Message} $Exception=New-Object "System.Net.WebException" "$ErrorInCmdlet`: $Message",$Exception }else{ $Message=$Exception.Message $Exception=New-Object Exception ("$ErrorInCmdlet`: $Message",$Exception) } Write-Verbose "$CmdletName`: Message: $ErrorInCmdlet`: $Message" $Exception.HelpLink = (Get-Module $Global:ModuleName).HelpInfoUri $Exception.Source = $Global:ModuleName return $Exception } #Verify the SCBConnection is well configured function ValidateConnection([SCBConnection] $SCBConnection){ $preSetting=$WarningPreference $WarningPreference="SilentlyContinue" #Don't show the Warning information Write-Verbose "ValidateConnection: SCBConnection = $($SCBConnection|ConvertTo-Json -Compress)" if($SCBConnection -eq $null){ $message="SCBConnection: Cannot process the command because it is null." throw new-object System.ArgumentNullException $message } $connUri= $SCBConnection.ConnectionUri $cred= $SCBConnection.GetCredential() $token= $SCBConnection.Token if($connUri -ne $null -And $cred -ne $null -And ![String]::IsNullOrEmpty($token)){ Write-Verbose "ValidateConnection: No need to reconnect" } else { $parameter=if($connUri -eq $null){"ConnectionUri"}elseif($cred -eq $null){"Credential"}else{"Token"} $message="Cannot process the command because $parameter is null or empty. Please issue the ""New-SCBConnection"" command to get a new connection and retry." throw new-object [System.ArgumentNullException] $message } $Global:DefaultConnection=$SCBConnection $WarningPreference=$preSetting } function GetResponse([SCBRequest] $SCBRequest,[String]$CmdletName){ Write-Verbose "$CmdletName.Request.Uri=$($SCBRequest.Uri)" Write-Verbose "$CmdletName.Request.ObjectName=$($SCBRequest.MappingInfor.ObjectName)" Write-Verbose "$CmdletName.Request.Method=$($SCBRequest.Method)" Write-Verbose "$CmdletName.Request.Header=$($SCBRequest.Header|ConvertTo-Json -Compress)" Write-Verbose "$CmdletName.Request.Body=$($SCBRequest.Body|ConvertTo-Json -Compress)" $ret=$null try{ Write-Verbose("$CmdletName.BEGIN_REST_METHOD") if(!$request.Header.Keys.Contains("Authorization") -And ![String]::IsNullOrEmpty($request.SCBConnection.Token)){ $request.Header.Add("Authorization",$request.SCBConnection.GetTokenString()) } if($request.Body.Count -gt 0){ $ret=Invoke-RestMethod -Method $request.Method -Uri $request.Uri -ContentType "application/json" -Header $request.Header -Body ($request.Body|ConvertTo-Json) -ErrorAction Stop }else{ $ret=Invoke-RestMethod -Method $request.Method -Uri $request.Uri -ContentType "application/json" -Header $request.Header -ErrorAction Stop } #Write-Verbose "$CmdletName.Response=$($ret[0]|ConvertTo-Json -Compress)" } catch{ $ex=AnalyzeException -Exception $_.Exception -SCBConnectionRef ([ref]$request.SCBConnection) -ErrorInCmdlet $CmdletName throw $ex }finally{ Write-Verbose("$CmdletName.END_REST_METHOD") } $results=@() if($ret -ne $null){ if($request.MappingInfor.FieldsToPostprocessed.Contains("space_name")){ $spaces=Get-SCBSpace -SCBConnection $request.SCBConnection -Verbose:$False } if($request.MappingInfor.FieldsToPostprocessed.Contains("initiators")){ $initiators=Get-SCBHostInitiator -SCBConnection $SCBConnection -Verbose:$False } $isToRename=$request.MappingInfor.FieldsToRenamed.Count -gt 0 $isToRemove=$request.MappingInfor.FieldsToRemoved.Count -gt 0 $isToPostProcess=$request.MappingInfor.FieldsToPostprocessed.Count -gt 0 foreach($obj in $ret){ if($isToRename){ foreach($key in $request.MappingInfor.FieldsToRenamed.Keys){ #Write-Verbose "$CmdletName.PostProcess: Rename the field $key to $($request.MappingInfor.FieldsToRenamed[$key])" $obj|Add-Member -NotePropertyName $request.MappingInfor.FieldsToRenamed[$key] -NotePropertyValue $obj.$key -Force $obj.PSObject.Properties.Remove($key) } } if($isToRemove){ foreach($key in $request.MappingInfor.FieldsToRemoved){ #Write-Verbose "$CmdletName.PostProcess: Remove the field: $key" $obj.PSObject.Properties.Remove($key) } } if($isToPostProcess){ if($request.MappingInfor.FieldsToPostprocessed.Contains("space_name")){ $spname=($spaces|?{$_.ID -eq $obj.space_id})[0].Name $obj|Add-Member -NotePropertyName space_name -NotePropertyValue $spname -Force } if($request.MappingInfor.FieldsToPostprocessed.Contains("capability_values")){ $CapabilityValues=$request.SCBConnection.GetCapability() $caps=$obj.capability_values.Split(",") $obj.PSObject.Properties.Remove("capability_values") $obj|Add-Member -NotePropertyName capability_values -NotePropertyValue $CapabilityValues[$caps] -Force } if($request.MappingInfor.FieldsToPostprocessed.Contains("initiators")){ $ports=($initiators|?{$_.host -eq $obj.id}).identifier $obj|Add-Member -NotePropertyName initiators -NotePropertyValue $ports -Force } } $results+=$obj } } return $results } #endregion #region Cmdlet Definition function New-SCBConnection{ <# .SYNOPSIS Set up the connection between the SCB server and the IBM Storage PowerShell Client. Before using the PowerShell client, create a PowerShell interface user. .DESCRIPTION Setup the connection to the SCB server, and a SCBConnection object will be returned if successful. .PARAMETER ConnectionUri Alias L; The connection URI for SCB server in the https://SCBIPAddress:ServicePort(8440) format. .PARAMETER Credential User credentials for connection to the SCB server. .PARAMETER Username Alias U; User name and password for the SCB server connection must be specified. The user can be created on the Spectrum Control BaseGUI, when creating a PowerShell interface. Other non-PowerShell-interface users will be limited to access some SCB functions .PARAMETER Password Alias P; The corresponding password. .EXAMPLE $client=New-SCBConnection -ConnectionUri https://9.115.250.45:8440 If Credential or Username/Password is not provided, a Credential dialog will pop up to input username/password information. .EXAMPLE $client=New-SCBConnection -ConnectionUri https://9.115.250.45:8440 -UserName powershell -Password passw0rd! Or in short form using parameter aliases: $client=New-SCBConnection -L https://9.115.250.45:8440 -U powershell -P passw0rd! .EXAMPLE $username="powershell"; $password="passw0rd"; $ssPsw=ConvertTo-SecureString -AsPlainText -Force $password; $cred=New-Object pscredential $username,$ssPsw; $client=New-SCBConnection -ConnectionUri https://9.115.236.61:8440 -Credential $cred Create the PSCredential from cmdline, and pass it to New-SCBConnection #> [CmdletBinding(DefaultParameterSetname="Credential")] Param( [Parameter(Mandatory = $true)][ValidateNotNullorEmpty()][Alias("L")][String] $ConnectionUri, [Parameter(Mandatory = $true,ParameterSetName="Credential",Position=1)][PSCredential]$Credential, [Parameter(Mandatory = $true,ParameterSetName="Password",Position=1)][ValidateNotNullorEmpty()][Alias("U")][String] $UserName, [Parameter(Mandatory = $true,ParameterSetName="Password",Position=2)][ValidateNotNullorEmpty()][Alias("P")][string] $Password ) $CmdletName="New-SCBConnection" $SCBConnection=New-Object "SCBConnection" $SCBConnection.ConnectionUri=$ConnectionUri [System.Uri] $Uri=$ConnectionUri $strPassword="" if(![String]::IsNullOrEmpty($Username)){ $strPassword=$Password $SCBConnection.SetCredential($UserName,$Password) }elseif($Credential -ne $null){ $SCBConnection.SetCredential($Credential) $strPassword=$SCBConnection.ResolvePassword() } $request=New-Object SCBRequest; $request.Method="POST" $request.SCBConnection=$SCBConnection; $request.Header.Add("Referer",$Uri.AbsoluteUri) $request.Header.Add("Host",$Uri.Authority) $request.Body.Add("username",$SCBConnection.UserName) $request.Body.Add("password",$strPassword) $request.Body.Add("group","powershell") $request.MappingInfor.ObjectName="SCBToken" $Token=GetResponse $request $CmdletName $SCBConnection.Token=$Token.token if(![String]::IsNullOrEmpty($Token.token)){ $capValues=Get-SCBCapabilityValue -SCBConnection $SCBConnection $CapabilityValues=New-Object "System.Collections.Generic.Dictionary[int,string]" $null=$CapabilityValues.Add(0,"") foreach($cv in $capValues){ $null=$CapabilityValues.Add($cv.id,[String]::Format("{0}: {1}", $cv.capability, $cv.value)) } $SCBConnection.SetCapability($CapabilityValues) } $Global:DefaultConnection=$SCBConnection return $SCBConnection } function Get-SCBSpace{ <# .SYNOPSIS Retrieves all the spaces or show the detailed information via space ID or space name. .PARAMETER SpaceID Alias ID; Get the spaces via the space ID. .PARAMETER SpaceName Alias Name; Get the spaces via the space name. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Get-SCBSpace -SCBConnection $client |ft ID,Name id name -- ---- efbd54ac-70c8-4693-ad7f-3df1cf56c187 Default_Space 12fb661e-e9e6-45c7-a5f4-62b86d66e238 space_vwc 7af7432d-172d-495b-a287-a1df2d2f4b77 recovered_space_vwc Retrieve all the spaces on the SCB server specified by the SCBConnection $client; .EXAMPLE Get-SCBSpace -SCBConnection $client -SpaceID efbd54ac-70c8-4693-ad7f-3df1cf56c187 id : efbd54ac-70c8-4693-ad7f-3df1cf56c187 num_services : 3 name : Default_Space description : Space used as the default. storage_array_metadata : {} Get the detailed information via Space ID .EXAMPLE lsspace -SCBConnection $client -Name DemoSpace id : 1ba5a6eb-c652-436f-a90c-2bdba732fdb3 num_services : 2 name : DemoSpace description : storage_array_metadata : {} Use the aliases for short; For this cmdlet, it supports the alias for cmdlet and the alias for parameters #> [CmdletBinding(DefaultParameterSetname="ID")] param( [parameter(ParameterSetName="ID",Position=0)][Alias("ID")][String] $SpaceID, [parameter(ParameterSetName="Name",Position=0)][Alias("Name")][String] $SpaceName, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBSpace" Write-Verbose "$CmdletName.ENTER: SCBConnection=$($SCBConnection|ConvertTo-Json -Compress), SpaceID=$SpaceID, SpaceName=$SpaceName" ValidateConnection $SCBConnection if(![String]::IsNullOrWhiteSpace($SpaceName)){ Write-Verbose("$CmdletName.SpaceName:" + $SpaceName) $ret=Get-SCBSpace -SCBConnection $SCBConnection|?{$_.Name -eq $SpaceName} if([String]::IsNullOrEmpty($ret)){ Write-Warning ([String]::Format("$CmdletName`: Instance with {0} equal to {1} not found!","Name",$SpaceName)) } return $ret } $request=New-Object SCBRequest; $request.Method="GET" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBSpace" $request.MappingInfor.FieldsToRemoved.Add("unique_identifier") if(![String]::IsNullOrWhiteSpace($SpaceID)){ $request.Key=$SpaceID } $ret=GetResponse $request $CmdletName Write-Verbose "$CmdletName.EXIT" return $ret } function Get-SCBService{ <# .SYNOPSIS Retrieves the services from SCB server. Services are a set of storage capabilities and capacity for a tier or application. For PowerShell, all the services are attached to PowerShell interfaces. .PARAMETER ServiceID Alias ID; The service ID or Unique Identifier(id). .PARAMETER ServiceName Alias Name; The service name. .PARAMETER SpaceID Get the services via the space ID. .PARAMETER SpaceName Get the services via the space Name. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Get-SCBService -SCBConnection $client|ft id,description,type,physical_size id description type physical_size -- ----------- ---- ------------- 97b8ca86-a01e-4143-97c1-89d33003944a ThinProvisionService regular 214748364800 e7df033a-6367-4f51-a7b9-fb3bd04472e4 CompressionService regular 322122547200 Retrieves all the service instances .EXAMPLE Get-SCBService e7df033a-6367-4f51-a7b9-fb3bd04472e4 -SCBConnection $client id : e7df033a-6367-4f51-a7b9-fb3bd04472e4 name : CompressionService description : CompressionService type : regular physical_size : 322122547200 logical_size : 322122547200 physical_free : 317827579904 logical_free : 317827579904 total_capacity : 322122547200 used_capacity : 4294967296 max_resource_logical_free : 317827579904 max_resource_free_size_for_provisioning : 10594252663466 num_volumes : 4 has_admin : True qos_max_iops : 0 qos_max_mbps : 0 space_id : 1ba5a6eb-c652-436f-a90c-2bdba732fdb3 space_name : DemoSpace capability_values : {Compression: Enabled, Deduplication: Disabled} Retrieves the detailed information about a service via its ID .EXAMPLE Get-SCBService -SpaceName DemoSpace -SCBConnection $sc|ft name,id,space_id name id space_id ---- -- --------- ThinProvisionService 8d4b6c91-670f-40bd-8d69-901e4a1219f9 4cfc8298-965f-4f90-b23f-4d04fe70d9ee CompressionService d7cd2c6f-cb96-4c27-8c87-8dba3fb9820c 4cfc8298-965f-4f90-b23f-4d04fe70d9ee Retrieves all the services under a space #> [CmdletBinding(DefaultParameterSetname="ID")] param( [parameter(ParameterSetName="ID",Position=0)][Alias("ID")][String] $ServiceID, [parameter(ParameterSetName="Name")][Alias("Name")][String] $ServiceName, [parameter(ParameterSetName="SpaceID")][String] $SpaceID, [parameter(ParameterSetName="SpaceName")][String] $SpaceName, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBService" Write-Verbose "$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ServiceID=$ServiceID, ServiceName=$ServiceName, SpaceID=$SpaceID,SpaceName=$SpaceName" ValidateConnection $SCBConnection if(![String]::IsNullOrWhiteSpace($SpaceName)){ $spaces=Get-SCBSpace -SCBConnection $SCBConnection -Verbose:$False $_spaceID=($spaces|?{$_.Name -eq $SpaceName}).id Write-Verbose "$CmdletName`: Space found with ID $_spaceID" if([String]::IsNullOrEmpty($_spaceID)){ Write-Warning ([String]::Format("$CmdletName`: SCBSpace instance with {0} equal to {1} not found!","Name",$SpaceName)) Write-Verbose("$CmdletName.EXIT") return $null } return Get-SCBService -SCBConnection $SCBConnection -SpaceID $_spaceID } $request=New-Object SCBRequest; $request.MappingInfor.ObjectName="SCBService" $request.Method="GET" $request.SCBConnection=$SCBConnection; $request.MappingInfor.FieldsToRenamed.Add("container","space_id") $request.MappingInfor.FieldsToRemoved.Add("unique_identifier") $request.MappingInfor.FieldsToPostprocessed.Add("space_name") $request.MappingInfor.FieldsToPostprocessed.Add("capability_values") if(![String]::IsNullOrWhiteSpace($ServiceID)){ $request.Key=$ServiceID } if(![String]::IsNullOrWhiteSpace($ServiceName)){ $request.UrlParams.Add("name",$ServiceName) } if(![String]::IsNullOrWhiteSpace($SpaceID)){ $request.UrlParams.Add("container",$SpaceID) } $ret=GetResponse $request $CmdletName Write-Verbose("$CmdletName.EXIT") return $ret } function Get-SCBHostInitiator{ <# .SYNOPSIS Retrieves initiator ports information from the SCB server. .PARAMETER HostID The host ID from the SCB server. .PARAMETER ArrayID The ID of the managed storage array. .PARAMETER HostName Specifies on which host to retrieve its initiator ports. .PARAMETER ConnectionType Specifies the type of the connection. Acceptable values are: FibreChannel, and iSCSI. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Get-SCBHostInitiator -ConnectionType FibreChannel -SCBConnection $client|ft id identifier port_type host physical_initiator -- ---------- --------- ---- ------------------ 4 21000024FF2DC5EE FC 4 5 21000024FF2DA3F6 FC 3 6 21000024FF2FADC5 FC 2 7 21000024FF2FADC4 FC 1 8 21000024FF523307 FC 6 9 21000024FF523306 FC 4 Retrieves all the FC initiators from all managed storage arrays .EXAMPLE Get-SCBHostInitiator -ArrayID $array -HostName fakeFCHost -SCBConnection $client|ft d identifier port_type host physical_initiator - ---------- --------- ---- ------------------ 2 21000024FF2DAFF5 FC 6 1 21000024FF2DAFF6 FC 6 Shows all the initiators of a host on a managed array #> [CmdletBinding(DefaultParameterSetname="ID")] param( [parameter(ParameterSetName="ID",Position=0)][String] $HostID, [parameter(Mandatory=$true,ParameterSetName="Name",Position=0)][String] $ArrayID, [parameter(ParameterSetName="Name",Position=1)][String] $HostName, [ValidateSet("iSCSI","FibreChannel")][String] $ConnectionType, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBHostInitiator" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), HostID=$HostID, HostName=$HostName, ArrayID=$ArrayID ConnectionType=$ConnectionType, FromHostSide=$FromHostSide") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="GET" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBHostInitiator" if(![String]::IsNullOrEmpty($HostName) -And ![String]::IsNullOrEmpty($ArrayID)){ $HostID=(Get-SCBHost -SCBConnection $SCBConnection -Array $ArrayID -Name $HostName).id } if(![String]::IsNullOrEmpty($HostID)){ $request.UrlParams.Add("host",$HostID) } $portType="" if($ConnectionType -eq "FibreChannel"){ $portType="FC" }elseif($ConnectionType -eq "iSCSI"){ $portType="iSCSI" } $ret=GetResponse $request $CmdletName Write-Verbose("$CmdletName.EXIT") if([String]::IsNullOrEmpty($portType)){ return $ret }else{ return ($ret|where {$_.port_type -eq $portType}) } } function Get-SCBCapabilityValue{ <# .SYNOPSIS Retrieves the capabilities of services. .PARAMETER ID The ID or Unique Identifier(id) of the capabilities values. .EXAMPLE Get-SCBCapabilityValue -SCBConnection $Client|ft id capability capability_constraints value value_type -- ---------- ---------------------- ----- ---------- 1 Qos {} Max Independent Performance string 2 Qos {} Max Shared Performance string 3 Compression {1} Enabled string 4 Compression {} Disabled string ... Retrieves all the capability values .EXAMPLE Get-SCBCapabilityValue 3 -SCBConnection $client id : 3 capability : Compression capability_constraints : {1} value : Enabled value_type : string Retrieve the capability values via ID #> [CmdletBinding()] param( [String] $ID, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBCapabilityValue" Write-Verbose("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ID=$ID") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="GET" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBCapabilityValue" if(![String]::IsNullOrWhiteSpace($ID)){ $request.Key=$ID } $ret=GetResponse $request $CmdletName Write-Verbose("$CmdletName.EXIT") return $ret } function Get-SCBHost{ <# .SYNOPSIS Retrieves the information of host instances on the storage arrays managed by SCB Server. .PARAMETER HostID Alias ID; The host ID. .PARAMETER HostName Alias Name; The host name(case sensitive). The host name is not unique, especially when more storage arrays are managed by the same SCB server; To get the correct host, the ArrayID parameter must be specified. .PARAMETER ArrayID The ID of the managed storage array. .PARAMETER Initiators The host endpoints (either FC or iSCSI); System.Collections.ArrayList object or just a Object[] is accepted here for multiple host endpoints. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Get-SCBHost -SCBConnection $scbclient|ft id,name,array,initiators -AutoSize id name array initiators -- ---- ----- ---------- 1 win2012r2x215 00000200AE62F4E4 iqn.1991-05.com.microsoft:win2012r2x215.vmm.com 17 host_memeber5 00000200AE62F4E4 iqn.1991-05.com.microsoft:host5 7 WIN2012X66_FC 00000200AE62F4E4 21000024FF2DC5FB 2 win2012x73_ISCSI 00000200AE62F4E4 iqn.1991-05.com.microsoft:win2012x73.vmm.com ... 6 fakeFCHost 00000200AE62F4E4 {21000024FF2DAFF6, 21000024FF2DAFF5} Gets all the host information for the connected SCB Server. .EXAMPLE Get-SCBHost -HostID 6 -SCBConnection $scbclient(or Get-SCBHost 6, or Get-SCBHost -ID 6) id : 6 array_type : 2145 array : 00000200AE62F4E4 name : fakeFCHost port_count : 2 iogroup_count : 4 status : offline host_type : generic iogroups : {2, 3, 4, 1} initiators : {21000024FF2DAFF6, 21000024FF2DAFF5} Gets the information for the specified host with ID 6. .EXAMPLE Get-SCBHost -ArrayID $array -HostName fakeISCSIHost -SCBConnection $scbclient id : 5 array_type : 2145 array : 00000200AE62F4E4 name : fakeISCSIHost port_count : 1 iogroup_count : 4 status : offline host_type : generic iogroups : {2, 3, 4, 1} initiators : iqn.1991-05.com.microsoft:agino ... Retrieves the detailed information by host name. .EXAMPLE $ports="iqn.1991-05.com.microsoft:win2012x72.vmm.com","21000024FF2DC5FB","21000024FF2DAFF6"; Get-SCBHost -ArrayID $array -Initiators $ports|ft name,array,id name array id ---- ----- -- WIN2012X66_FC 00000200AE62F4E4 7 fakeFCHost 00000200AE62F4E4 6 win2012x72_ISCSI 00000200AE62F4E4 3 Retrieves the hosts via their initiators. .EXAMPLE $ports=New-Object "System.Collections.ArrayList"; $ports.Add("21000024FF2DC5FB"); $ports.Add("iqn.1991-05.com.microsoft:win2012x72.vmm.com"); Get-SCBHost -ArrayID $array -Initiators $ports |ft name,array,id name array id ---- ----- -- fakeFCHost 00000200AE62F4E4 6 win2012x72_ISCSI 00000200AE62F4E4 3 Retrieves the hosts via their initiators. #> [CmdletBinding(DefaultParameterSetname="ID")] param( [parameter(ParameterSetName="ID",Position=0)][Alias("ID")][String] $HostID, [parameter(ParameterSetName="ArrayID",Position=0,Mandatory= $True)][parameter(ParameterSetName="Initiators",Position=0,Mandatory= $True)][parameter(ParameterSetName="Name",Position=0,Mandatory= $True)][String] $ArrayID, [parameter(ParameterSetName="Name",Position=1,Mandatory= $True)][Alias("Name")][String] $HostName, [parameter(ParameterSetName="Initiators",Position=1,Mandatory= $True)][Object] $Initiators, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBHost" Write-Verbose("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ID=$HostID,Name=$HostName,ArrayID=$ArrayID, Initiators=$($Initiators|ConvertTo-Json -Compress)") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="GET" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBHost" $request.MappingInfor.FieldsToRemoved.Add("host_id") $request.MappingInfor.FieldsToRemoved.Add("physical_host") $request.MappingInfor.FieldsToRemoved.Add("storage_cluster") $request.MappingInfor.FieldsToPostprocessed.Add("initiators") if(![String]::IsNullOrWhiteSpace($HostID)){ $request.Key=$HostID } if (![String]::IsNullOrWhiteSpace($ArrayID)) { $request.UrlParams.Add("array",$ArrayID) } if (![String]::IsNullOrWhiteSpace($HostName)) { $request.UrlParams.Add("name",$HostName) } if ($Initiators -ne $null) { if($Initiators -Is "System.Collections.ArrayList" -Or $Initiators.GetType().IsArray){ # [1,2] =>"1,2" $request.UrlParams.Add("match_all_ports","$Initiators".Replace(" ",",")) }else{ $request.UrlParams.Add("match_all_ports",$Initiators) } } try{ $ret=GetResponse $request $CmdletName } catch{ if($_.Exception.Message.Contains("descriptor 'union' of 'set' object needs an argument")){ $ret=$null }else{ throw $_.Exception } } finally{ Write-Verbose("$CmdletName.EXIT") } return $ret } #region SCBVolume function Get-SCBVolume{ <# .SYNOPSIS Retrieves all the volumes managed by the PowerShell interfaces; Detailed information can be retrieved if the VolumeID/VolumeName parameter is specified. .PARAMETER VolumeID Alias ID; The volume ID or Unique Identifier(id). .PARAMETER VolumeName Alias Name; The volume Name; The volume name is not unique, especially when more storage arrays are managed by the same SCB server; To get the correct volume, the ArrayID parameter must be specified. .PARAMETER ArrayID The ID of the managed storage array. This parameter can be used seperately, or with VolumeName together. .PARAMETER ServiceName Get the volumes via the service name. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Get-SCBVolume -SCBConnection $client |?{$_.name -match ".*demo.*"}|ft name,id,pool_name,array name id pool_name array ---- -- --------- ----- SCBDemo004 6005076801A707416800000000000B85 DemoPool 0000020069C1D05A DemoThin04 6005076801A707416800000000000B76 DemoPool 0000020069C1D05A DemoThin05 6005076801A707416800000000000B75 DemoPool 0000020069C1D05A Gets the volumes whose names contain "demo" on the array(id=0000020069C1D05A) .EXAMPLE Get-SCBVolume -VolumeID 6005076802B98BD390000000000000AD -SCBConnection $client|fl name,id,logical_capacity name : SCBVOL-TCK2-636180625453455304 id : 6005076802B98BD390000000000000AD logical_capacity : 10737418240 Retrieves the detailed information via volume ID .EXAMPLE Get-SCBVolume -VolumeName SCBVOL304 -ArrayID 00000200AE62F4E4 -SCBConnection $client|fl id,name,array,service_name,space_name,logical_capacity id : 6005076802B98BD390000000000000AD array : 00000200AE62F4E4 name : SCBVOL304 service_name : ThinProvisionService space_name : DemoSpace logical_capacity : 10737418240 Retrieves the detailed information of the volume(SCBVOL304) on a managed array #> [CmdletBinding(DefaultParameterSetName="ID")] param( [parameter(ParameterSetName="ID",Position=0)][Alias("ID")][String] $VolumeID, [parameter(ParameterSetName="Name",Position=0,Mandatory=$true)][Alias("Name")][String] $VolumeName, [parameter(ParameterSetName="Name",Position=1)][parameter(ParameterSetName="Array",Position=0,Mandatory=$true)][String] $ArrayID, [parameter(ParameterSetName="Service",Position=0,Mandatory=$true)][String] $ServiceID, [parameter(ParameterSetName="ScsiID",Position=0,Mandatory=$true)][String] $ScsiID, [SCBConnection]$SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBVolume" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ID=$VolumeID,Name=$VolumeName,ServiceID=$ServiceID,ArrayID=$ArrayID,ScsiID=$ScsiID") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="GET" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBVolume" $request.MappingInfor.FieldsToRenamed.Add("container_name","space_name") $request.MappingInfor.FieldsToRenamed.Add("container_id","space_id") if (![String]::IsNullOrWhiteSpace($VolumeID)) { $request.Key=$VolumeID } if (![String]::IsNullOrWhiteSpace($ArrayID)) { $request.UrlParams.Add("array","$ArrayID") } if (![String]::IsNullOrWhiteSpace($VolumeName)){ $request.UrlParams.Add("name","$VolumeName") } if (![String]::IsNullOrWhiteSpace($ServiceID)) { $request.UrlParams.Add("service","$ServiceID") } if (![String]::IsNullOrWhiteSpace($ScsiID)) { $request.UrlParams.Add("scsi_identifier","$ScsiID") } $ret=GetResponse $request $CmdletName Write-Verbose ("$CmdletName.EXIT") return $ret } function New-SCBVolume{ <# .SYNOPSIS Create volumes via the services attached to the PowerShell interface user; If the Initiators parameter is specified, the volume will be automatically mapped; A SCBVolume instance will be returned if succeed. .PARAMETER VolumeName Alias Name; Specify the name for the volume. .PARAMETER ServiceName The service name. The service will determine what kind of volumes will be created based on its capabilities. .PARAMETER Size Specify the volume size. Its unit can be specified via the SizeUnit parameter. If the SizeUnit param is not specified, the default unit is GB(10^9Byte). .PARAMETER SizeUnit Specify the unit of Size. If not specified, the unit GB will be used by default. The following units are available: TiB = 1024^4 Byte GiB = 1024^3 Byte GB = 1000^3 Byte Byte .PARAMETER Initiators The host endpoints (either FC or iSCSI); System.Collections.ArrayList object or just a Object[] is accepted here for multiple host endpoints. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE New-SCBVolume -Name DemoThin04 -Size 1 -SizeUnit GiB -ServiceID $service.id |fl array_name,id,name,pool_name,service_name,space_name,logical_capacity array_name : cim75 id : 6005076801A707416800000000000B76 name : DemoThin04 pool_name : SCBReserved service_name : SVCThinService space_name : DemoSpace logical_capacity : 1073741824 Creates a new volume from service. .EXAMPLE $vol=New-SCBVolume -Name $volName -Size 1 -SizeUnit TiB -ServiceID $service.ID -Initiators 21000024FF2DA3F6,21000024FF2DD3FA; Get-SCBHostVolMapping -VolumeID $vol.id|ft id volume host lun_number -- ------ ---- ---------- 317 6005076802B98BD39000000000000177 6 3 318 6005076802B98BD39000000000000177 7 3 News a volume via Service, and register it to specified FC/iSCSI initiators. .EXAMPLE $service=(Get-SCBService -SCBConnection $sc|?{$_.name.Contains("Compress")})[0]; New-SCBVolume -VolumeName CompVol001 -Size 10 -ServiceID $service.id|fl Name,ID,logical_capacity name : CompVol001 id : 6005076802B98BD39000000000000136 logical_capacity : 10000000000 Specifies the SizeUnit when creating volume. #> [CmdletBinding()] param( [Parameter(Mandatory = $true,Position=0)][Alias("Name")][String] $VolumeName, [Parameter(Mandatory = $true,Position=1)][Long] $Size, [Parameter(Position=2)][ValidateSet("TiB","GiB","GB","Byte")][String] $SizeUnit="GB", [Parameter(Mandatory = $true,Position=3)][String] $ServiceID, [Object] $Initiators, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="New-SCBVolume" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), Name=$VolumeName,Size=$Size,SizeUnit=GB,ServiceID=$ServiceID,Initiators=$($Initiators|ConvertTo-Json -Compress)") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="Post" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBVolume" $request.MappingInfor.FieldsToRenamed.Add("container_name","space_name") $request.MappingInfor.FieldsToRenamed.Add("container_id","space_id") $request.Body.Add("name",$VolumeName) if($SizeUnit.Equals("GB")){ $request.Body.Add("size",$Size) }else{ [long]$finalSize=$Size switch($SizeUnit.ToUpper()){ "TIB"{$finalSize=1024*$Size; $SizeUnit="gib";} "GB"{$finalSize=$Size; $SizeUnit="gb";} "GiB"{$finalSize=$Size;$SizeUnit="gib";} "BYTE"{$finalSize=$Size;$SizeUnit="byte";} } $request.Body.Add("size",$finalSize) $request.Body.Add("size_unit",$SizeUnit) } if(![String]::IsNullOrWhiteSpace($ServiceID)){ $request.Body.Add("service",$ServiceID) } if($Initiators -ne $Null){ if($Initiators -Is "System.Collections.ArrayList" -Or $Initiators.GetType().IsArray){ $request.Body.Add("host_initiators","$Initiators".Replace(" ",",")) } else{ $request.Body.Add("host_initiators",$Initiators) } } $ret=GetResponse $request $CmdletName Write-Verbose ("$CmdletName.EXIT") return $ret } function Remove-SCBVolume{ <# .SYNOPSIS Removes the volume or multiple volumes; For the operation on single volume, it will return $True if the volume is successfully removed, otherwize it will return $False; For multiple volumes, a dictionary, with VolumeID as the key and operation result as the value, will be returned. .PARAMETER VolumeID Alias ID; The volume ID or Unique Identifier(id). .PARAMETER Force Indicates whether to remove the mapping relationships if exist. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Remove-SCBVolume -VolumeID 6005076801A707416800000000000B85 -SCBConnection $client Removes a volume via its ID. .EXAMPLE $ids=(Get-SCBVolume|?{$_.Name.StartsWith("SCBVOL")}).id; Remove-SCBVolume -VolumeID $ids -SCBConnection $client -Force Key Value --- ----- 6005076802B98BD390000000000019B0 True 6005076802B98BD39000000000001996 True 6005076802B98BD39000000000001A22 True .... 6005076802B98BD39000000000001A82 True Remove all the volumes whose name starts with "SCBVOL". #> [CmdletBinding()] param( [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName=$true,Position=0)][Alias("ID")][Object] $VolumeID, [switch] $Force, [SCBConnection] $SCBConnection=$Global:DefaultConnection ) $CmdletName="Remove-SCBVolume" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ID=$VolumeID, Force=$Force") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="Delete" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBVolume" if($VolumeID -Is "System.Collections.ArrayList" -Or $VolumeID.GetType().IsArray){ $rets=New-Object "System.Collections.Generic.Dictionary[String,String]" foreach($volID in $VolumeID){ try{ $ret=Remove-SCBVolume -SCBConnection $SCBConnection -VolumeID $volID -Force:$Force }catch{ Write-Warning $_.Exception.Message $ret=$False } $rets.Add($volID,$ret) } return $rets } $request.Key=$VolumeID if($Force){ $mappings=Get-SCBHostVolMapping -SCBConnection $SCBConnection -VolumeID $VolumeID if($mappings -ne $null){ foreach($mapping in $mappings){ Write-Verbose ("$CmdletName`: Begin to Unmap the Volume(ID=$VolumeID) from the Host($($mapping.host))") $rr=Remove-SCBHostVolMapping -SCBConnection $SCBConnection -VolumeID $VolumeID -HostID $mapping.host if($rr){ Write-Verbose ("$CmdletName`: Succeed to unmap the volume(ID=$VolumeID) from the Host($($mapping.host))") }else{ Write-Warning ("$CmdletName`: Fail to unmap the volume(ID=$VolumeID) from the Host($($mapping.host))") } } } } try{ $ret=GetResponse $request $CmdletName }catch{ $ex=([System.Net.WebException]$_.Exception) if($ex.Message.Contains("Not found.")){ Write-Warning "$CmdletName`: The volume with ID equal to $VolumeID is not found." Write-Verbose ("$CmdletName.EXIT") return $true }else{ throw $ex } } if([String]::IsNullOrEmpty("$ret")){$ret=$True}else{$ret=$False} Write-Verbose ("$CmdletName.EXIT") return $ret } function Resize-SCBVolume{ <# .SYNOPSIS Resizes the specified volume; Returns a already-resized SCBVolume instance if succeed while $Null if fail .PARAMETER VolumeID Alias ID; The ID for the volume to be resized. .PARAMETER Size The size for the volume to be resized. .PARAMETER SizeUnit The unit of Size. If not specified, GB will be used by default. The following units are available: TiB = 1024^4 Byte GiB = 1024^3 Byte GB = 1000^3 Byte Byte .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Resize-SCBVolume -ID 6005076801A707416800000000000B76 -NewSize 2 |ft name,id,logical_capacity name id logical_capacity ---- -- ---------------- DemoThin03 6005076801A707416800000000000B76 2000000000 Resize the volume to 2GB(from 1GB) #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName=$true,Position=0)][Alias("ID")][String] $VolumeID, [Parameter(Mandatory = $true,Position=1 )][long] $NewSize, [Parameter(Position=2 )][ValidateSet("TiB","GiB","GB","Byte")][String] $SizeUnit="GB", [SCBConnection]$SCBConnection=$Global:DefaultConnection ) $CmdletName="Resize-SCBVolume" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ID=$VolumeID, NewSize=$NewSize") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="Patch" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBVolume" $request.MappingInfor.FieldsToRenamed.Add("container_name","space_name") $request.MappingInfor.FieldsToRenamed.Add("container_id","space_id") $size=(Get-SCBVolume -SCBConnection $SCBConnection -ID $VolumeID).logical_capacity Write-Verbose ("$CmdletName.OldSize: $size") $request.Key=$VolumeID if($SizeUnit.Equals("GB")){ $request.Body.Add("new_size",$newSize) }else{ [long]$finalSize=$newSize switch($SizeUnit.ToUpper()){ "TIB"{$finalSize=1024*$newSize; $SizeUnit="gib";} "GB"{$finalSize=$newSize; $SizeUnit="gb";} "GiB"{$finalSize=$newSize;$SizeUnit="gib";} "BYTE"{$finalSize=$newSize;$SizeUnit="byte";} default{throw New-Object SCBException("Unsupported Unit, and please specify the SizeUnit from TiB/GiB/GB/MiB/Byte") } } $request.Body.Add("new_size",$finalSize) $request.Body.Add("size_unit",$SizeUnit) } $ret=GetResponse $request $CmdletName Write-Verbose ("$CmdletName.END_REST_METHOD") return $ret } #endregion #region SCBMapping function Get-SCBHostVolMapping{ <# .SYNOPSIS Retrieves the Mapping relationships between volumes and hosts on the Storage arrays managed by the PowerShell interface user. .PARAMETER HostVolMappingID Alias ID; The ID or Unique Identifier(id) for the mapping relationship; Alias ID can be used instead; .PARAMETER HostID Show only the mapping relationships of hosts with specified HostID .PARAMETER VolumeID Show only the mapping relationships of the Volume .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Get-SCBHostVolMapping -SCBConnection $sc|ft id volume host lun_number -- ------ ---- ---------- 122 6005076802B98BD390000000000000B2 6 10 117 6005076802B98BD390000000000000AF 6 9 114 6005076802B98BD390000000000000AD 6 8 ..... Retrieves all the mapping relationships. .EXAMPLE Get-SCBHostVolMapping 122 -SCBConnection $sc|ft id volume host lun_number -- ------ ---- ---------- 122 6005076802B98BD390000000000000B2 6 10 Gets mapping information via the mapping ID. .EXAMPLE Get-SCBHostVolMapping -HostID 5 -SCBConnection $sc|ft id volume host lun_number -- ------ ---- ---------- 113 6005076802B98BD390000000000000AC 5 2 3 6005076802B98BD39000000000000018 5 1 4 6005076802B98BD39000000000000017 5 0 ... Gets the mapping relationships for the given host. #> [CmdletBinding(DefaultParameterSetname="ID")] param( [parameter(ParameterSetName="ID",Position=0,ValueFromPipelineByPropertyName=$true)][Alias("ID")][String] $HostVolMappingID, [parameter(Mandatory = $true,ParameterSetName="HostID",Position=1)][String] $HostID, [parameter(Mandatory = $true,ParameterSetName="Volume",Position=2)][String] $VolumeID, [SCBConnection]$SCBConnection=$Global:DefaultConnection ) $CmdletName="Get-SCBHostVolMapping" Write-Verbose("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), ID=$HostVolMappingID, HostID=$HostID, VolumeID=$VolumeID") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="Get" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBHostVolMapping" if(![String]::IsNullOrWhiteSpace($HostVolMappingID)){ $request.Key=$HostVolMappingID } if (![String]::IsNullOrWhiteSpace($HostID)) { $request.UrlParams.Add("host","$HostID") } if (![String]::IsNullOrWhiteSpace($VolumeID)) { $request.UrlParams.Add("volume","$VolumeID") } $ret=GetResponse $request $CmdletName Write-Verbose ("$CmdletName.EXIT") return $ret } function New-SCBHostVolMapping{ <# .SYNOPSIS Maps the volume. The hosts can be specified via HostID, HostName or their endpoints. Returns mapping relationships if succeed while $null if fail. .PARAMETER VolumeID The ID of the volume to be mapped. .PARAMETER HostID The ID(s) of the host(s) that the specified volume will be mapped to. System.Collections.ArrayList object or just a Object[] is accepted here for multiple host IDs; .PARAMETER HostName The Name of the host that the specified volume will be mapped to. .PARAMETER Initiator The host endpoint(s) (either FC or iSCSI); System.Collections.ArrayList object or just a Object[] is accepted here for multiple host endpoints. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE $host=Get-SCBHost -ArrayID 0000020069C1D05A -Initiator 21000024FF2DA3F7; New-SCBHostVolMapping -VolumeID 6005076801A707416800000000000B76 -HostID $host.id {@{id=20; volume=6005076801A707416800000000000B76; host=21; lun_number=4}} Create a mapping relationship by spedifying HostID .EXAMPLE New-SCBHostVolMapping -VolumeID 6005076801A707416800000000000B76 -ArrayID 0000020069C1D05A -Initiator iqn.1991-05.com.microsoft:agino,21000024FF2DAFF6,21000024FF2DC5FB {@{id=21; volume=6005076801A707416800000000000B76; host=22; lun_number=4}, @{id=20; volume=6005076801A707416800000000000B76; host=21; lun_number=4}, @{id=19; volume=6005076801A... Map a volume to multiple hosts by specifying host endpoints .EXAMPLE New-SCBHostVolMapping -VolumeID 6005076802B98BD39000000000001B09 -HostID 12,6,3 -SCBConnection $client {@{id=614; volume=6005076802B98BD39000000000001B09; host=12; lun_number=7}, @{id=615; volume=6005076802B98BD39000000000001B09; host=6; lun_number=7}, @{id=616; volume=600507680... Map a volume to multiple hosts by their IDs .EXAMPLE New-SCBHostVolMapping -VolumeID 6005076801A707416800000000000B76 -HostName win2012r2x215 {@{id=21; volume=6005076801A707416800000000000B76; host=22; lun_number=1}} Create the mapping relationship using host name #> [CmdletBinding(DefaultParameterSetName="Host")] param( [parameter(Mandatory = $true,Position=0)][String] $VolumeID, [parameter(Mandatory = $true,ParameterSetName="Host", Position=1)][Object] $HostID, [parameter(Mandatory = $true,ParameterSetName="HostName", Position=1)][String] $HostName, [parameter(Mandatory = $true,ParameterSetName="Array", Position=1)][Object] $Initiators, [SCBConnection]$SCBConnection=$Global:DefaultConnection ) $CmdletName="New-SCBHostVolMapping" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), VolumeID=$VolumeID,HostName=$HostName,HostID=$HostID,Initiators=$Initiators") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="Post" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBHostVolMapping" $arrayID=(Get-SCBVolume -SCBConnection $SCBConnection -ID $VolumeID).array $request.Body.Add("volume_id",$VolumeID) if(![String]::IsNullOrWhiteSpace($HostName)){ #HostName=hostname1 $HostID=(Get-SCBHost -SCBConnection $SCBConnection -Name $HostName -ArrayID $ArrayID).id } if($Initiators -ne $Null){ #-Initiators 21000024FF2DA3F7,21000024FF2DFFFE,iqn.1991-05.com.microsoft:win2012r2x214.vmm.org $HostID=((Get-SCBHost -SCBConnection $SCBConnection -ArrayID $arrayID -Initiators $Initiators).id) } if($HostID -ne $Null){ #-HostIDs 22,21,1 || 21 ||@(21) $hosts = @() if($HostID -Is "System.Collections.ArrayList" -Or $HostID.GetType().IsArray){ foreach($idTemp in $HostID){ $hosts+=$idTemp.ToString() } $request.Body.Add("host_id",$hosts) #=>["21","22","1"] }else{ $request.Body.Add("host_id",$HostID.ToString()) } } $ret=GetResponse $request $CmdletName Write-Verbose ("$CmdletName.EXIT") return $ret } function Remove-SCBHostVolMapping{ <# .SYNOPSIS Unmaps the volume from the selected host; $True if succeed while $False if fail. .PARAMETER VolumeID The ID(s) of the volume(s) to be unmapped. .PARAMETER HostID The ID(s) of the host(s) that the specified volume will be unmapped from. System.Collections.ArrayList object or just a Object[] is accepted here for multiple host IDs. .PARAMETER HostName The Name of the host that the specified volume will be unmapped from. .PARAMETER Initiators The host endpoint(s) (either FC or iSCSI); System.Collections.ArrayList object or just a Object[] is accepted here for multiple host endpoints. .PARAMETER SCBConnection The client handle for SCB server. It can be created via New-SCBConnection. If the SCBConnection is not specified, a most recently used SCBConnection will be utilized by default. The connection can be found via the global variable $Global:DefaultConnection. If multiple SCB servers are connected, you must explicitly specify the SCBConnection parameter. .EXAMPLE Remove-SCBHostVolMapping -VolumeID 6005076801A707416800000000000B76 -HostName fakeFCHost -SCBConnection $client Remove the mapping relationships of the volume from host with name fakeFCHost .EXAMPLE Remove-SCBHostVolMapping -VolumeID 6005076801A707416800000000000B76 -Initiators 21000024FF2DA3F7,iqn.1991-05.com.microsoft:win2012r2x214.vmm.org,21000024FF2DFFFE -SCBConnection $client Remove the mapping relationships of the volume from hosts with the specified initiators .EXAMPLE Remove-SCBHostVolMapping -VolumeID 6005076801A707416800000000000B76 -HostIDs 22,21 -SCBConnection $client Remove the mapping relationships of the volume from hosts 22 and 21 #> [CmdletBinding(DefaultParameterSetName="Host")] param( [parameter(Mandatory = $true,Position=0)][String] $VolumeID, [parameter(Mandatory = $true,ParameterSetName="Host", Position=1)][Object] $HostID, [parameter(Mandatory = $true,ParameterSetName="HostName", Position=1)][String] $HostName, [parameter(Mandatory = $true,ParameterSetName="Array", Position=1)][Object] $Initiators, [SCBConnection]$SCBConnection=$Global:DefaultConnection ) $CmdletName="Remove-SCBHostVolMapping" Write-Verbose ("$CmdletName.ENTER: SCBConnection=($($SCBConnection|ConvertTo-Json -Compress)), VolumeID=$VolumeID,HostID=$HostID,ArrayID=$ArrayID,Initiators=$Initiators,HostName=$HostName") ValidateConnection $SCBConnection $request=New-Object SCBRequest; $request.Method="Delete" $request.SCBConnection=$SCBConnection; $request.MappingInfor.ObjectName="SCBHostVolMapping" $arrayID=(Get-SCBVolume -SCBConnection $SCBConnection -ID $VolumeID).array $request.Body.Add("volume_id",$VolumeID) $hosts = @() if(![String]::IsNullOrWhiteSpace($HostName)){ #HostName=hostname1 $HostID=(Get-SCBHost -SCBConnection $SCBConnection -Name $HostName -ArrayID $ArrayID).id } if($Initiators -ne $Null){ #-Initiators 21000024FF2DA3F7,21000024FF2DFFFE,iqn.1991-05.com.microsoft:win2012r2x214.vmm.org $HostID=((Get-SCBHost -SCBConnection $SCBConnection -ArrayID $arrayID -Initiators $Initiators).id) } if($HostID -ne $Null){ #-HostIDs 22,21,1 || 21 ||@(21) if($HostID -Is "System.Collections.ArrayList" -Or $HostID.GetType().IsArray){ foreach($idTemp in $HostID){ $hosts+=$idTemp.ToString() } $request.Body.Add("host_id",$hosts) #=>["21","22","1"] }else{ $request.Body.Add("host_id",$HostID.ToString()) } } $ret=GetResponse $request $CmdletName if([String]::IsNullOrEmpty("$ret")){$ret=$True}else{$ret=$False} Write-Verbose ("$CmdletName.EXIT") return $ret } #endregion Set-Alias -Name mkclient -Value New-SCBConnection Set-Alias -Name lsspace -Value Get-SCBSpace Set-Alias -Name lsservice -Value Get-SCBService Set-Alias -Name lshost -Value Get-SCBHost Set-Alias -Name lsvol -Value Get-SCBVolume Set-Alias -Name mkvol -Value New-SCBVolume Set-Alias -Name rmvol -Value Remove-SCBVolume Set-Alias -Name chvol -Value Resize-SCBVolume Set-Alias -Name lsmapping -Value Get-SCBHostVolMapping Set-Alias -Name mkmapping -Value New-SCBHostVolMapping Set-Alias -Name rmmapping -Value Remove-SCBHostVolMapping Export-ModuleMember -Function New-SCBConnection,Get-SCBSpace,Get-SCBService,Get-SCBHost,Get-SCBVolume Export-ModuleMember -Function New-SCBVolume,Remove-SCBVolume,Resize-SCBVolume,Get-SCBHostVolMapping,New-SCBHostVolMapping,Remove-SCBHostVolMapping Export-ModuleMember -Alias mkclient,lsspace,lsservice,lshost,lsvol,mkvol,rmvol,chvol,lsmapping,mkmapping,rmmapping #endregion # SIG # Begin signature block # MIIOnAYJKoZIhvcNAQcCoIIOjTCCDokCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUVPwbWhDlf5T95qzPyFDY664Q # ZBqgggubMIIFiTCCBHGgAwIBAgIQHxxF6QW+gydxgPm+BZaoKjANBgkqhkiG9w0B # AQsFADCBtDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8w # HQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBv # ZiB1c2UgYXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEuMCwG # A1UEAxMlVmVyaVNpZ24gQ2xhc3MgMyBDb2RlIFNpZ25pbmcgMjAxMCBDQTAeFw0x # NDExMTQwMDAwMDBaFw0xNzExMTMyMzU5NTlaMIG8MQswCQYDVQQGEwJDTjEQMA4G # A1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEvMC0GA1UEChQmSUJNIChD # aGluYSkgSW52ZXN0bWVudCBDb21wYW55IExpbWl0ZWQxJzAlBgNVBAsUHklCTSBT # eXN0ZW1zICYgVGVjaG5vbG9neSBHcm91cDEvMC0GA1UEAxQmSUJNIChDaGluYSkg # SW52ZXN0bWVudCBDb21wYW55IExpbWl0ZWQwggEgMA0GCSqGSIb3DQEBAQUAA4IB # DQAwggEIAoIBAQDVE1mFkn6R6GERYBpu2tVQkknIrFqTHPOHAPXDN5LUGVaPA+sN # 6yYS9ZXFcOTj2iQH1EtFOr0ndC7ZnimHOsYxmM2SA5IYPGW3NMNFcm27u7oeF7q6 # yLkjEJjQr1dbvIS/usKi3XtunsLM2aPiDB1yovttSv6AK3RRA6SghxSos93I+pFp # L/jYsRwgzvu257jH9RE1Gb3IUAiyVu5dTsPapagb2Ip6nsGrXmB0XdHS2dxugmrL # 49iqnaFQ7Cp6RQwlKVQA5QHK0eehX0NwU8pIBSZeyG5o8npiBJGa4yk66W9kGivC # BPD4csocbjzLaksUf3xEz0hiBOc+501c2rJDAgEDo4IBjTCCAYkwCQYDVR0TBAIw # ADAOBgNVHQ8BAf8EBAMCB4AwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3NmLnN5 # bWNiLmNvbS9zZi5jcmwwZgYDVR0gBF8wXTBbBgtghkgBhvhFAQcXAzBMMCMGCCsG # AQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2NwczAlBggrBgEFBQcCAjAZDBdo # dHRwczovL2Quc3ltY2IuY29tL3JwYTATBgNVHSUEDDAKBggrBgEFBQcDAzBXBggr # BgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zZi5zeW1jZC5jb20wJgYI # KwYBBQUHMAKGGmh0dHA6Ly9zZi5zeW1jYi5jb20vc2YuY3J0MB8GA1UdIwQYMBaA # FM+Zqep7JvRLyY6P1/AFJu/j0qedMB0GA1UdDgQWBBTiB9YFmAEtsIP3KnC7JMkA # FnwHdTARBglghkgBhvhCAQEEBAMCBBAwFgYKKwYBBAGCNwIBGwQIMAYBAQABAf8w # DQYJKoZIhvcNAQELBQADggEBAI54+6DaAS5D5li/evS2jlKXT/aa6KPK/KTkaOOy # C0mdlnsECj2qPi0IapNYvtOL0DNYVl0r3h0GM554ZZCHyNtJRftxNsRKdcmF5bh4 # 2Q0VNGsJN+yD3FBGxS7aCr1cgxqkgmCAIJQUgm6mPu0kctFAP/cQLoUG7wrVU95l # 8HzxNVinPGrbgW1S6dGkxWIKi32j9Esb7E6r5VS6VzdrEg9abfX3IULypdJ/6Ib5 # O5lLOaV7n+kvgLmrtq1cvXbFfFV+aODlZ8QGckqfJvjHdLSvh7eMy3Ez4mfl8wm/ # lLZCmimOmvWayX5DVGOrCpTlcPGnITCzWUqp51OdSXbiGj0wggYKMIIE8qADAgEC # AhBSAOWqJVb8GobtlsnUSzPHMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV # UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy # dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0g # Rm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNz # IDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNTAe # Fw0xMDAyMDgwMDAwMDBaFw0yMDAyMDcyMzU5NTlaMIG0MQswCQYDVQQGEwJVUzEX # MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 # IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52 # ZXJpc2lnbi5jb20vcnBhIChjKTEwMS4wLAYDVQQDEyVWZXJpU2lnbiBDbGFzcyAz # IENvZGUgU2lnbmluZyAyMDEwIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB # CgKCAQEA9SNLXqXXirsy6dRX9+/kxyZ+rRmY/qidfZT2NmsQ13WBMH8EaH/LK3Ue # zR0IjN9plKc3o5x7gOCZ4e43TV/OOxTuhtTQ9Sc1vCULOKeMY50Xowilq7D7zWpi # gkzVIdob2fHjhDuKKk+FW5ABT8mndhB/JwN8vq5+fcHd+QW8G0icaefApDw8QQA+ # 35blxeSUcdZVAccAJkpAPLWhJqkMp22AjpAle8+/PxzrL5b65Yd3xrVWsno7VDBT # G99iNP8e0fRakyiF5UwXTn5b/aSTmX/fze+kde/vFfZH5/gZctguNBqmtKdMfr27 # Tww9V/Ew1qY2jtaAdtcZLqXNfjQtiQIDAQABo4IB/jCCAfowEgYDVR0TAQH/BAgw # BgEB/wIBADBwBgNVHSAEaTBnMGUGC2CGSAGG+EUBBxcDMFYwKAYIKwYBBQUHAgEW # HGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9jcHMwKgYIKwYBBQUHAgIwHhocaHR0 # cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYB # BQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XT # GoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3Zz # bG9nby5naWYwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2lnbi5j # b20vcGNhMy1nNS5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC52ZXJpc2lnbi5jb20wHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF # BwMDMCgGA1UdEQQhMB+kHTAbMRkwFwYDVQQDExBWZXJpU2lnbk1QS0ktMi04MB0G # A1UdDgQWBBTPmanqeyb0S8mOj9fwBSbv49KnnTAfBgNVHSMEGDAWgBR/02Wnwt3s # u/AwCfNDOfoCrzMxMzANBgkqhkiG9w0BAQUFAAOCAQEAViLmNKTEYctIuQGtVqhk # D9mMkcS7zAzlrXqgIn/fRzhKLWzRf3EafOxwqbHwT+QPDFP6FV7+dJhJJIWBJhyR # FEewTGOMu6E01MZF6A2FJnMD0KmMZG3ccZLmRQVgFVlROfxYFGv+1KTteWsIDEFy # 5zciBgm+I+k/RJoe6WGdzLGQXPw90o2sQj1lNtS0PUAoj5sQzyMmzEsgy5AfXYxM # NMo82OU31m+lIL006ybZrg3nxZr3obQhkTNvhuhYuyV8dA5Y/nUbYz/OMXybjxuW # nsVTdoRbnK2R+qztk7pdyCFTwoJTY68SDVCHERs9VFKWiiycPZIaCJoFLseTpUiR # 0zGCAmswggJnAgEBMIHJMIG0MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNp # Z24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNV # BAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBh # IChjKTEwMS4wLAYDVQQDEyVWZXJpU2lnbiBDbGFzcyAzIENvZGUgU2lnbmluZyAy # MDEwIENBAhAfHEXpBb6DJ3GA+b4FlqgqMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3 # AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG # AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBSQxi9OTLEN # 1nrihwWqMlT7mSgOEDANBgkqhkiG9w0BAQEFAASCAQAs9ugiuXH/Lciq7Hhu+MU7 # hbjgLCwKprXLO62EwYStdzYkzwLZzBeler4rFYBlDjqh8vGp9P2DsS4V+OTSFeuf # ZnfmVp6k292om1/9jXWIJPOVVRf4Zh1Ar2fI+6ZISoq0gFtwmtsq32KhpjiMurl/ # FW96ODlD5QhmEbcHMk2zYBWKpOlpNJ+phazrlM7znDUcUahU9Unls40U5/jrk+Ii # 7p6ozqI71DUapr3eDVIafnL8lDQcIuWoINbUaPfVylrxsV8bCnfb+URE65RFL84S # aGdh+O4troj3Rgiok6SFwK1OnqfPq4M2HiK1YsUKBgwqjA9zEEzpB7DUkYbJeAYy # SIG # End signature block |