Powershell Script to Setup new NetApp clustered Data ONTAP system

Setting up a new NetApp clustered Data ONTAP system involves a number of steps. I have tried to automate these steps using NetApp Powershell toolkit. This saves time and reduces human errors while configuring new systems.
This script assumes that hardware is installed, “cluster setup” is run and all nodes joined in. I use a suffix of “-mgmt” with the cluster name when i configure a new clustered Data ONTAP system. This script has been tested to work with ONTAP 8.3 (simulators).

At present i have automated the following tasks:
    1.  Rename Nodes
2.  Rename root aggregates
3.  Create failover groups for cluster-mgmt and node-mgmt interfaces
4.  Add feature licenses
5.  Configure Storage Failover
6.  Unlock diag User
7.  Setup diag user password
8.  Create admin user for access to logs through http
9.  Setup Timezone and NTP server
10. Remove 10 Gbe ports from Default broadcast domain
11. Create ifgroups and add ports to ifgroups
12. Enable Cisco Discovery Protocol (cdpd) on all of the nodes
13. Setup disk auto assignment
14. Setup flexscale options
15. Disable flowcontrol on all the ports
16. Create data aggregates

The script displays the results on Powershell console as it iterates through the setup tasks. A transcript is also saved as a text file.

Cluster_config_screenshot

Source Code

 

<# .SYNOPSIS Automate Setup of a new NetApp clustered Data ONTAP cluster install .DESCRIPTION The script assumes the basic cluster setup is completed and all nodes joined. The script automates the following tasks: 1. Rename Nodes 2. Rename root aggregates 3. Create failover groups for cluster-mgmt and node-mgmt interfaces 4. Add feature licenses 5. Configure Storage Failover 6. Unlock diag User 7. Setup diag user password 8. Create admin user for access to logs through http 9. Setup Timezone and NTP server 10. Remove 10 Gbe ports from Default broadcast domain 11. Create ifgroups and add ports to ifgrps 12. Enable Cisco Discovery Protocol (cdpd) on all of the nodes 13. Setup disk auto assignment 14. Setup flexscale options 15. Disable flowcontrol on all the ports 16. Create data aggregates Example: PS C:\Users\vadmin\Documents\pshell-scripts> .\cluster_config_v1.5.ps1
.PARAMETER settingsFilePath
    Location of the File with User defined Parameters.
.EXAMPLE
    PS C:\Users\vadmin\Documents\pshell-scripts> .\cluster_config_v1.5.ps1
#>
#####################
# Declare Variables
#####################
$ClusterName             = "ntapclu1-mgmt"
$mgmtIP                  = "aa.bb.cc.dd"
$mgmtSubnet              = "aaa.bbb.ccc.ddd"
$mgmtGateway             = "aa.bb.cc.xx"
$ntpServer               = "ntp-server1"
$ClusterNameMgmtPort     = "e0d"
$NodeMgmtPort            = "e0c"
$timezone                = "Australia/Sydney"
[int]$maxraidsize        = 17 #raid group size for creating an aggregate
[int]$diskCount          = 51 
$TranscriptPath          = "c:\temp\cluster_setup_transcript_$(get-date -format "yyyyMMdd_hhmmtt").txt"
$licensesPath            = "c:\temp\licenses.txt" 
$ifgrp_a0a_port1         = "e0e"
$ifgrp_a0a_port2         = "e0f"
$timezone                = 'Australia/Sydney'

###########################
# Declare the functions
###########################
function Write-ErrMsg ($msg) {
    $fg_color = "White"
    $bg_color = "Red"
    Write-host " "
    Write-host $msg -ForegroundColor $fg_color -BackgroundColor $bg_color
    Write-host " "
}
#'------------------------------------------------------------------------------
function Write-Msg ($msg) {
    $color = "yellow"
    Write-host " "
    Write-host $msg -foregroundcolor $color
    Write-host " "
}
#'------------------------------------------------------------------------------
function Invoke-SshCmd ($cmd){
    try {
        Invoke-NcSsh $cmd -ErrorAction stop | out-null
        "The command completed successfully"
    }
    catch {
       Write-ErrMsg "The command did not complete successfully"
    }
}
#'------------------------------------------------------------------------------
function Check-LoadedModule {
  Param( 
    [parameter(Mandatory = $true)]
    [string]$ModuleName
  )
  $LoadedModules = Get-Module | Select Name
  if ($LoadedModules -notlike "*$ModuleName*") {
    try {
        Import-Module -Name $ModuleName -ErrorAction Stop
        Write-Msg ("The module DataONTAP is imported")
    }
    catch {
        Write-ErrMsg ("Could not find the Module DataONTAP on this system. Please download from NetApp Support")
        stop-transcript
        exit 
    }
  }
}
#'------------------------------------------------------------------------------
##############################
# Begin Cluster Setup Process
##############################
#'------------------------------------------------------------------------------
## Load Data ONTAP Module
start-transcript -path $TranscriptPath
#'------------------------------------------------------------------------------
Write-Msg  "##### Beginning Cluster Setup #####"
Check-LoadedModule -ModuleName DataONTAP
try {
    Connect-nccontroller $ClusterName -ErrorAction Stop | Out-Null   
    "connected to " + $ClusterName
    }
catch {
    Write-ErrMsg ("Failed connecting to Cluster " + $ClusterName + " : $_.")
    stop-transcript
    exit
}
#'------------------------------------------------------------------------------
## Get the nodes in the cluster
$nodes = (get-ncnode).node
#'------------------------------------------------------------------------------
## Rename the nodes (remove "-mgmt" string)
Write-Msg  "+++ Renaming Node SVMs +++"
foreach ($node in $nodes) { 
    Rename-NcNode -node $node -newname ($node -replace "-mgmt") -Confirm:$false |Out-Null
} 
Get-NcNode |select Node,NodeModel,IsEpsilonNode | Format-Table -AutoSize
$nodes = (get-ncnode).node
#'------------------------------------------------------------------------------
## Rename root aggregates
Write-Msg  "+++ Renaming root aggregates +++"
# get each of the nodes
Get-NcNode | %{ 
    $nodeName = $_.Node
    # determine the current root aggregate name
    $currentAggrName = (
        Get-NcAggr | ?{ 
             $_.AggrOwnershipAttributes.HomeName -eq $nodeName `
               -and $_.AggrRaidAttributes.HasLocalRoot -eq $true 
        }).Name
    # no dashes
    $newAggrName = $nodeName -replace "-", "_"
    # can't start with numbers
    $newAggrName = $newAggrName -replace "^\d+", " "
    # append the root identifier
    $newAggrName = "$($newAggrName)_root"
    if ($currentAggrName -ne $newAggrName) {
        Rename-NcAggr -Name $currentAggrName -NewName $newAggrName | Out-Null 
    }
    sleep -s 5
    Write-Host "Renamed aggregates containing node root volumes"
    (Get-NcAggr | ?{ $_.AggrOwnershipAttributes.HomeName -eq $node -and $_.AggrRaidAttributes.HasLocalRoot -eq $true }).Name 
}
#'------------------------------------------------------------------------------
## Create failover groups for cluster-mgmt and node-mgmt interfaces
Write-Msg  "+++ Create failover groups for cluster-mgmt and node-mgmt interfaces +++"
# get admin vserver name
$adminSVMTemplate = Get-NcVserver -Template
Initialize-NcObjectProperty -Object $adminSVMTemplate -Name VserverType | Out-Null
$adminSVMTemplate.VserverType = "admin"
$adminSVM         = (Get-NcVserver -Query $adminSVMTemplate).Vserver
# create cluster-mgmt failover group 
$clusterPorts     = ((get-ncnode).Node | % { $_,$ClusterNameMgmtPort -join ":" })
$nodePorts        = ((get-ncnode).Node | % { $_,$NodeMgmtPort -join ":" })
$firstClusterPort = $clusterPorts[0]
$allClusterPorts  = $clusterPorts[1..($clusterPorts.Length-1)]
New-NcNetFailoverGroup -Name cluster_mgmt -Vserver $adminSVM -Target $firstClusterPort | Out-Null
foreach ($cPort in $allClusterPorts) {
    Add-NcNetFailoverGroupTarget -Name cluster_mgmt -Vserver $adminSVM -Target $cPort | Out-Null
}
Set-NcNetInterface -Name cluster_mgmt -Vserver $adminSVM -FailoverPolicy broadcast_domain_wide -FailoverGroup cluster_mgmt | Out-Null
Write-Host "Created cluster-mgmt failover group"
Get-NcNetInterface -Name cluster_mgmt  | select InterfaceName,FailoverGroup,FailoverPolicy
# create node-mgmt failover-group for each node
foreach ($node in $nodes) {
    $prt1 = ($node,$NodeMgmtPort -join ":")
    $prt2 = ($node,$ClusterNameMgmtPort -join ":")
    New-NcNetFailoverGroup -Name $node"_mgmt" -Vserver $adminSVM -Target $prt1 | Out-Null
    Add-NcNetFailoverGroupTarget -Name $node"_mgmt" -Vserver $adminSVM -Target $prt2 | Out-Null
    $nodeMgmtLif = (Get-NcNetInterface -Role node-mgmt | Where-Object {$_.HomeNode -match "$node"}).InterfaceName
    Set-NcNetInterface -Name $nodeMgmtLif -Vserver $adminSVM -FailoverPolicy local-only -FailoverGroup $node"_mgmt" | Out-Null
    sleep -s 5
    Write-Host "Created node-mgmt failover group for node "$node
    Get-NcNetInterface -Role node-mgmt | Where-Object {$_.HomeNode -match "$node"} | select InterfaceName,FailoverGroup,FailoverPolicy
}
sleep -s 15
#'------------------------------------------------------------------------------
## Add licenses to cluster
Write-Msg "+++ Adding licenses +++"
$test_lic_path = Test-Path -Path $licensesPath
if ($test_lic_path -eq "True") {
    $count_licenses = (get-content $licensesPath).count
    if ($count_licenses -ne 0) {
        Get-Content $licensesPath |  foreach { Add-NcLicense -license $_ }
        Write-Host "Licenses successfully added"
        Write-Host " "
    }
    else {
        Write-ErrMsg ("License file is empty. Please add the licenses manually")
    }
}
else {
    Write-ErrMsg ("License file does not exist. Please add the licenses manually")       
}
sleep -s 15
#'------------------------------------------------------------------------------
## Configure storage failover
Write-Msg  "+++ Configure SFO +++"
Write-Host "SFO Does not work with Simulators"
if ($nodes.count -gt 2) {
    foreach ($node in $nodes) {
        $sfo_enabled = Invoke-NcSsh "storage failover modify -node " $node " -enabled true"
        if (($sfo_enabled.Value.ToString().Contains("Error")) -or ($sfo_enabled.Value.ToString().Contains("error"))) {
            Write-ErrMsg ($sfo_enabled.Value)
        }
        else {
            Write-Host ("Storage Failover is enabled on node " + $node)
        }

	    $sfo_autogive = Invoke-NcSsh "storage failover modify -node " $node " -auto-giveback true"
        if (($sfo_autogive.Value.ToString().Contains("Error")) -or ($sfo_autogive.Value.ToString().Contains("error"))) {
                Write-ErrMsg ($sfo_autogive.Value)
        }
        else {
            Write-Host ("Storage Failover option auto giveback is enabled on node " + $node)
            Write-Host " "
        }
        sleep -s 2
    }
}
elseif ($nodes.count -eq 2) {
    foreach ($node in $nodes) {
        $sfo_enabled = Invoke-NcSsh "cluster ha modify -configured true"
        if (($sfo_enabled.Value.ToString().Contains("Error")) -or ($sfo_enabled.Value.ToString().Contains("error"))) {
            Write-ErrMsg ($sfo_enabled.Value)
        }
        else {
            Write-Host ("Cluster ha is enabled on node " + $node)
            Write-Host
        }  
    }
}
else {
    Write-Host "No HA required for single node cluster. Continuing with the setup"
    Write-Host " "
}
sleep -s 15
#'------------------------------------------------------------------------------
## Unlock the diag user
Write-Msg "+++ Unlock the diag user +++"
try {
    Unlock-NcUser -username diag -vserver $ClusterName -ErrorAction stop |Out-Null
    Write-Host "Diag user is unlocked"
}
catch {
    Write-ErrMsg "Diag user is either unlocked or script could not unlock the diag user"
}
#'------------------------------------------------------------------------------
## Setup diag user password
Set-Ncuserpassword -UserName diag -password netapp123! -vserver $ClusterName | Out-Null
Write-Host "created diag user password"
sleep -s 15
#'------------------------------------------------------------------------------
## Create admin user for access to logs through http
Write-Msg "+++ create web log user +++"
Set-NcUser -UserName admin -Vserver $ClusterName -Application http -role admin -AuthMethod password | Out-Null
Write-Host "created admin user access for http log collection"
sleep -s 15
#'------------------------------------------------------------------------------
## Set Date and NTP on each node
Write-Msg  "+++ setting Timezones/NTP/Datetime +++"
foreach ($node in $nodes) {
    Set-NcTime -Node $node -Timezone $timeZone | Out-Null
    Set-NcTime -Node $node -DateTime (Get-Date) | Out-Null
}
New-NcNtpServer -ServerName $ntpServer -IsPreferred | Out-Null
Write-Host "NTP Sever setup complete"
sleep -s 15
#'------------------------------------------------------------------------------
## Remove 10 Gbe ports from Default broadcast domain
Write-Msg  "+++ Rmoving 10Gbe Ports from Default broadcast domain +++"
# remove ports from Default broadcast domain
$broadCastTemplate = Get-NcNetPortBroadcastDomain -Template
Initialize-NcObjectProperty -Object $broadCastTemplate -Name Ports | Out-Null
$broadCastTemplate.BroadcastDomain = "Default"
$defaultBroadCastPorts = ((Get-NcNetPortBroadcastDomain -Query $broadCastTemplate).Ports).Port
foreach ($bPort in $defaultBroadCastPorts) {
	if (($bPort -notlike "*$ClusterNameMgmtPort") -and ($bPort -notlike "*$NodeMgmtPort")) {
		Write-Host "Removing Port: " $bPort
		Set-NcNetPortBroadcastDomain -Name Default -RemovePort $bPort | Out-Null
	}	
}
sleep -s 15
#'------------------------------------------------------------------------------
## Create ifgroups and add ports to ifgrps
Write-Msg  "+++ starting ifgroup creation +++"
foreach ($node in $nodes) {
    try {
        New-NcNetPortIfgrp -Name a0a -Node $node -DistributionFunction port -Mode multimode_lacp -ErrorAction Stop | Out-Null
        Add-NcNetPortIfgrpPort -name a0a -node $node -port $ifgrp_a0a_port1 -ErrorAction Continue | Out-Null
        Add-NcNetPortIfgrpPort -name a0a -node $node -port $ifgrp_a0a_port2 -ErrorAction Continue | Out-Null
        Write-Host ("Successfully created ifgrp a0a on node " + $node)
    }
    catch {
        Write-ErrMsg ("Error exception in ifgrp a0a " + $node + " : $_.")
    }
}
sleep -s 15
#'------------------------------------------------------------------------------
## Enable cdpd on all of the nodes
Write-Msg  "+++ enable cdpd on nodes +++"
foreach ($node in $nodes) {
    $cdpd_cmd = Invoke-NcSsh "node run -node " $node " -command options cdpd.enable on"
    if (($cdpd_cmd.Value.ToString().Contains("Error")) -or ($cdpd_cmd.Value.ToString().Contains("error"))) {
        Write-ErrMsg ($cdpd_cmd.Value)
    }
    else {
        Write-Host ("Successfully modified cdpd options for " + $node)
    }
}
sleep -s 15
#'------------------------------------------------------------------------------
## Set option disk.auto_assign on
Write-Msg  "+++ Setting disk autoassign +++"
foreach ($node in $nodes) {
    $set_disk_auto = Invoke-NcSsh "node run -node " $node " -command options disk.auto_assign on"
    if (($set_disk_auto.Value.ToString().Contains("Error")) -or ($set_disk_auto.Value.ToString().Contains("error"))) {
        Write-ErrMsg ($set_disk_auto.Value)
    }
    else {
        Write-Host ("Successfully modified disk autoassign option on node " + $node)
    }   
}
sleep -s 15
#'------------------------------------------------------------------------------
## Set flexscale options
Write-Msg  "+++ Setting flexscale options +++"
foreach ($node in $nodes) {
	$flexscale_enable = Invoke-NcSsh "node run -node " $node " -command options flexscale.enable on" 
    if (($flexscale_enable.Value.ToString().Contains("Error")) -or ($flexscale_enable.Value.ToString().Contains("error"))) {
        Write-ErrMsg ($flexscale_enable.Value)
    }
    else {
        Write-Host ("options flexscale.enable set to on for node " + $node)
    } 

	$flexscale_lopri = Invoke-NcSsh "node run -node " $node " -command options flexscale.lopri_blocks on"
    if (($flexscale_lopri.Value.ToString().Contains("Error")) -or ($flexscale_lopri.Value.ToString().Contains("error"))) {
        Write-ErrMsg ($flexscale_lopri.Value)
    }
    else {
        Write-Host ("options flexscale.lopri_blocks set to on for node " + $node)
    } 

	$flexscale_data = Invoke-NcSsh "node run -node " $node " -command options flexscale.normal_data_blocks on"
    if (($flexscale_data.Value.ToString().Contains("Error")) -or ($flexscale_data.Value.ToString().Contains("error"))) {
        Write-ErrMsg ($flexscale_data.Value)
    }
    else {
        Write-Host ("options flexscale.normal_data_blocks set to on for node " + $node)
        Write-Host " "
    } 

}
sleep -s 15
#'------------------------------------------------------------------------------
## Disable flowcontrol on all of the ports
Write-Msg  "+++ Setting flowcontrol +++"
foreach ($node in $nodes) {
    try {
        Write-Host "Setting flowcontrol for ports on node: " $node
        get-ncnetport -Node $node | Where-Object {$_.Port -notlike "a0*"} | select-object -Property name, node | set-ncnetport -flowcontrol none -ErrorAction Stop | Out-Null
        sleep -s 15
        Get-NcNetPort -Node $node | Select-Object -Property Name,AdministrativeFlowcontrol | Format-Table -AutoSize
    }
    catch {
        Write-ErrMsg ("Error setting flowcontrol on node " + $node + ": $_.")
    }
}
sleep -s 15
#'------------------------------------------------------------------------------
## Create data aggregates
Write-Msg  "+++ Creating Data Aggregates +++"
# get each of the nodes
Get-NcNode | %{ 
    $nodeName = $_.Node
    # no dashes
    $newAggrName = $nodeName -replace "-", "_"
    # can't start with numbers
    $newAggrName = $newAggrName -replace "^\d+", " "
    # append the root identifier
    $newAggrName = "$($newAggrName)_data_01"
    # create an aggreagate
    $aggrProps = @{
        'Name' = $newAggrName;
        'Node' = $nodeName;
        'DiskCount' = $diskCount;
        'RaidSize' = $maxraidsize;
        'RaidType' = "raid_dp";
    }
    New-NcAggr @aggrProps | Out-Null
#
    sleep -s 15
    # enable free space reallocation
    Get-NcAggr $newAggrName | Set-NcAggrOption -Key free_space_realloc -Value on
}
#'------------------------------------------------------------------------------
Write-Host " "
Write-Host " "
stop-transcript
#'------------------------------------------------------------------------------

Leave a Reply

Your email address will not be published. Required fields are marked *