Thursday, March 30, 2017

WFA Command to check and remediate iscsi connections


During my workflow creation to check and re-mediate an iSCSI environment and had to create a final WFA command that would create/re-mediate the actual iSCSI connections.





So here are the workflow steps :

1 : Ask for vserver & windows host
2 : Check/Install snapdrive on the host (upgrade will follow)
3 : Check/Start the msiscsi windows service (set to automatic)
4 : Check/Install the multipath-IO feature
5 : Check/Alarm if the windows host has (enough) iscsi ip addresses
6 : Check if the vserver runs iscsi and has (enough) iscsi lifs
7 : Check/Create new mutual chap passwords and store in KeePass database
8 : Check/Create iscsi connections

This post is about step 8.

This step will :
- Get chap passwords from KeePass database (created earlier)
- Get host ip's (checked validity earlier)
- Get vserver ip's (checked validity earlier)
- Check/Create vserver chap authentication (mutual chap)
- Check/Create iscsi target group
- Check/Create iscsi connections (mpio)

Download the dar

Here is the code


param(
    [Parameter(Mandatory=$true,HelpMessage="Cluster name or ip")]
    [string]$Cluster,

    [Parameter(Mandatory=$true,HelpMessage="Vserver name")]
    [string]$VserverName,

    [Parameter(Mandatory=$true,HelpMessage="Host IQN Name")]
    [string]$Iqn,

    [Parameter(Mandatory=$true,HelpMessage="Computername")]
    [string]$ComputerName,

    [Parameter(Mandatory=$true,HelpMessage="Credential name to make a remote connection")]
    [string]$CredentialName,

    [Parameter(Mandatory=$true,HelpMessage="Network adddress")]
    [string]$NetworkAddress,

    [Parameter(Mandatory=$true,HelpMessage="Subnet mask")]
    [string]$SubnetMask,

    [Parameter(Mandatory=$false,HelpMessage="KeePass Database Profile")]
    [string]$DatabaseProfileName = "wfa_keepass",
 
    [Parameter(Mandatory=$true,HelpMessage="KeePass Group Path, relative from root")]
    [string]$GroupPath,
 
    [Parameter(Mandatory=$true,HelpMessage="KeePass Database Path")]
    [ValidatePattern(“^.*\.kdbx$”)]
    [string]$DatabasePath,
 
    [Parameter(Mandatory=$false,HelpMessage="MasterKey KeePass Credential Name")]
    [string]$MasterKeyName = "KeePass",
 
    [Parameter(Mandatory=$false,HelpMessage="Force remove KeePass config")]
    [bool]$ForceRemoveConfig,

    [Parameter(Mandatory=$true,HelpMessage="iSCSI in chap-user name")]
    [string]$iSCSIinUser,

    [Parameter(Mandatory=$true,HelpMessage="iSCSI out chap-user name")]
    [string]$iSCSIoutUser

)
function getIpAddresses(){
    return Invoke-Command -Session $session -ScriptBlock {
        [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() `
        | ForEach-Object { $_.GetIPProperties() } `
        | Select-Object -ExpandProperty 'UnicastAddresses';
    }
}
function getIscsiConnections(){
    return Invoke-Command -Session $session -ScriptBlock {Get-IscsiConnection}
}
function getIscsiSessions(){
    return Invoke-Command -Session $session -ScriptBlock {Get-IscsiSession}
}
function addIscsiTarget($iscsiOut,$ip,$sourceip,$user,$pass){
    $targetfound = $false
    $target = getIscsiTarget
    if($target){
        if(($target.TargetPortalAddress -eq $sourceip) -and ($target.TargetPortalAddress -eq $ip)){
            Get-WFALogger -Info -Message "iscsi target found [$($target.InitiatorPortalAddress) - $($target.TargetPortalAddress)]"
            $targetfound=$true
        }
    }
    if(-not $targetfound){
        Get-WFALogger -Info -Message "adding iscsi target [$sourceip - $ip]"
        invoke-Command -session $session -script {param($iscsiOut) Set-IscsiChapSecret -ChapSecret $iscsiOut} -ArgumentList $iscsiOut
        $out = invoke-Command -session $session -script {param($ip,$sourceip,$user,$pass) New-IscsiTargetPortal -TargetPortalAddress $ip -InitiatorPortalAddress $sourceip -AuthenticationType MUTUALCHAP -ChapUsername $user -ChapSecret $pass} -ArgumentList $ip,$sourceip,$user,$pass
        Get-WFALogger -Info -Message "added iscsi target [$sourceip - $ip]"
    }
}
function setIscsiConnection($iscsiOut,$vserverIqn,$ip,$sourceip,$user,$pass){
    addIscsiTarget -iscsiOut $iscsiOut -ip $ip -sourceip $sourceip -user $user -pass $pass
    Get-WFALogger -Info -Message "connecting iscsi target [$sourceip - $ip]"
    $out = invoke-Command -session $session -script {param($vserverIqn,$ip,$sourceip,$user,$pass) Connect-IscsiTarget -NodeAddress $vserverIqn -TargetPortalAddress $ip -InitiatorPortalAddress $sourceip -AuthenticationType MUTUALCHAP -IsPersistent $true -IsMultipathEnabled $true -ChapUsername $user -ChapSecret $pass} -ArgumentList $vserverIqn,$ip,$sourceip,$user,$pass
    Get-WFALogger -Info -Message "[connected : $($out.IsConnected)]"
}
function getIscsiTarget(){
    return Invoke-Command -Session $session -ScriptBlock {Get-IscsiTargetPortal}
}
function createCredentials($username,$password){
    $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
    return (New-Object System.Management.Automation.PSCredential ($username, $secpasswd))
}
function Get-NetworkAddress {
    param (
        [IpAddress]$ip,
        [IpAddress]$Mask
    )
 
    $IpAddressBytes = $ip.GetAddressBytes()
    $SubnetMaskBytes = $Mask.GetAddressBytes()
 
    if ($IpAddressBytes.Length -ne $SubnetMaskBytes.Length) {
        throw "Lengths of IP address and subnet mask do not match."
        exit 0
    }
 
    $BroadcastAddress = @()
 
    for ($i=0;$i -le 3;$i++) {
        $BroadcastAddress += $ipAddressBytes[$i]-band $subnetMaskBytes[$i]
 
    }
 
    $BroadcastAddressString = $BroadcastAddress -Join "."
    return [IpAddress]$BroadcastAddressString
}
function Test-IsInSameSubnet {
    param (
        [IpAddress]$ip1,
        [IpAddress]$ip2,
        [IpAddress]$mask1,
        [IpAddress]$mask2
    )
 
    $Network1 = Get-NetworkAddress -ip $ip1 -mask $mask1
    $Network2 = Get-NetworkAddress -ip $ip2 -mask $mask2
 
    return $Network1.Equals($Network2)
}
function correctKeePassPath($path){
    $path = $path.Trim("/")
    $rootPath = (Get-KeePassGroup -DatabaseProfileName $DatabaseProfileName -MasterKey $masterPassword | ?{-not $_.ParentGroup}).Name
    Get-WFALogger -Info -Message "Root path : $rootPath"
    return "$rootPath/$path"
}

$ErrorActionPreference = "stop"

$iSCSIHosts = @()
$iSCSILifs = @()

try{

    #--------------------------------------------
    # Make keepass connection and grab passwords
    #--------------------------------------------
    Import-Module PoShKeePass
 
    $masterCred = Get-WfaCredentials $MasterKeyName
    $masterPassword=$masterCred.Password
 
    Get-WfaLogger -info -message "Open connection to keepass db"
    New-KPConnection -Database $DatabasePath -MasterKey $masterPassword
 
    Get-WfaLogger -info -message "Getting keypass profile"
    $config = Get-KeePassDatabaseConfiguration -DatabaseProfileName $DatabaseProfileName
    if($config -and $ForceRemoveConfig){
        Get-WfaLogger -info -message "Removing keypass profile"
        Remove-KeePassDatabaseConfiguration -DatabaseProfileName $DatabaseProfileName
    }
    if(-not $config){
        Get-WfaLogger -info -message "Creating keypass profile"
        New-KeePassDatabaseConfiguration -DatabaseProfileName $DatabaseProfileName -DatabasePath $DatabasePath -UseMasterKey
    }
 
    # correct path
    $GroupPath = correctKeePassPath -path $GroupPath

    # Get Keepass entries
    Get-WfaLogger -info -message "Check if entry exists"
    $groups = Get-KeePassGroup  -DatabaseProfileName $DatabaseProfileName -MasterKey $masterPassword
    $entries = Get-KeePassEntry -KeePassEntryGroupPath $GroupPath -AsPlainText -DatabaseProfileName $DatabaseProfileName -MasterKey $masterPassword
    
    # get the iscsi entries
    $iscsiInEntry = $entries | ?{$_.Title -eq $iSCSIinUser -and $_.UserName -eq $iSCSIinUser}
    $iscsiOutEntry = $entries | ?{$_.Title -eq $iSCSIoutUser -and $_.UserName -eq $iSCSIoutUser}
 
    # check the iscsi entries
    if($iscsiInEntry){
        Get-WfaLogger -info -message "Found iSCSIin password"
    }else{
        throw "No iSCSIin entry found in keepass"
    }
    if($iscsiOutEntry){
        Get-WfaLogger -info -message "Found iSCSIout password"
    }else{
        throw "No iSCSIout entry found in keepass"
    }

    # get keepass passwords
    $iscsiInPwd = $iscsiInEntry.Password
    $iscsiOutPwd = $iscsiOutEntry.Password

    #--------------------------------------------
    # Make host connection, grab ip's & iscsi connections
    #--------------------------------------------

    # get credentials
    $mycreds = Get-WfaCredentials $CredentialName
    
    # create remote ps session
    $session = New-PSSession -ComputerName $ComputerName -Credential $mycreds

    $ipAddresses = getIpAddresses

    # loop every ipaddres and check if in iscsi range
    foreach($ip in $IpAddresses){
        if($ip.IPv4Mask -ne "0.0.0.0"){
            if(Test-IsInSameSubnet -ip1 $ip.Address -ip2 $NetworkAddress -mask1 $ip.IPv4Mask -mask2 $SubnetMask){
                $iSCSIHosts+=($ip.Address.IPAddressToString)
            }
        }
    }

    # get iscsi connection
    $iscsiConnections = getIscsiConnections
    $iscsiSessions = getIscsiSessions

    #--------------------------------------------
    # Make vserver connection, check chap and grab lifs
    #--------------------------------------------

    # connect to cluster
    $clstr = Connect-WfaCluster $Cluster

    # get vserver iscsi lifs
    $lifs = Get-NcNetInterface -Vserver $VserverName -DataProtocols iscsi
    foreach($l in $lifs){
        if(Test-IsInSameSubnet -ip1 $l.Address -ip2 $NetworkAddress -mask1 $l.Netmask -mask2 $SubnetMask){
            $iSCSILifs+=$l.Address
        }
    }

    # create chap credentials
    $inCreds = createCredentials -username $iSCSIinUser -password $iscsiInPwd
    $outCreds = createCredentials -username $iSCSIOutUser -password $iscsiOutPwd

    # check iscsi credentials
    $vserverInitiator = Get-NcIscsiInitiatorAuth -Initiator $iqn -VserverContext $VserverName
    $vserverIqn = (Get-NcIscsiNodeName -VserverContext $VserverName).Name
    if($vserverInitiator -and ($vserverInitiator.Username -eq $iSCSIinUser)){
        Get-WfaLogger -info -message  "[vserver] Vserver has valid iscsi credentials [$iqn]"
    }else{
        Get-WfaLogger -info -message  "[vserver] Adding vserver iscsi credentials [$iqn -> $iSCSIinUser]"
        Set-NcIscsiInitiatorAuth -Initiator $iqn -Chap -InboundCredential $inCreds -OutboundCredential $outCreds -VserverContext $VserverName
    }

    #--------------------------------------------
    # Make iscsi connections
    #--------------------------------------------

    foreach($t in $iSCSILifs){
        foreach($i in $iSCSIHosts){
            # check if the connection already exists
            $c = ( $iscsiConnections | ?{$_.InitiatorAddress -eq $i -and $_.TargetAddress -eq $t} )
            if($c){
                Get-WfaLogger -info -message  "[iscsi] valid iscsi connection [$($c.InitiatorAddress) -> $($c.TargetAddress)]"
            }else{
                # setIscsiConnection
                Get-WfaLogger -warn -message  "[iscsi] Missing iscsi connection [$i) -> $($t)]"
                setIscsiConnection -iscsiOut $iscsiOutPwd -vserverIqn $VserverIqn -ip $t -sourceip $i -user $iSCSIinUser -pass $iscsiInPwd
            }
        }
    }

}catch{
    throw $_.Exception
}finally{
    try{
        $out = Disconnect-PSSession $session -EA SilentlyContinue
        $out = Remove-PSSession $session -EA SilentlyContinue
    }catch{
    }
}

No comments :

Post a Comment