It’s a new year so let’s start with a new PowerCLI post. This post is inspired by the blog post of @alanrenouf: PowerCLI easy vswitch portgroup setup. I love the whole idea of taking a good working config from a vSphere host and use it on a fresh installed vSphere host to make sure it’s compliant.  In this post I will show you how to perform the same trick with NFS datastores like Alan did with the vSwithes and Portgroups.

My home lab contains two HP ml110 g5 and a simple P4 box with some hard disks to add shared storage to the lab. It’s running Debian linux and is capable of presenting iSCSI targets, NFS and SAMBA shares .I use NFS as shared storage for my vSphere lab.

On esx2.ict-freak.local I use the following NFS datastores:

image

But I messed up the configuration of the other vSphere host in my lab called esx1.ict-freak.local. So I had to reset the network settings and lost all the NFS datastores:

image

So I needed to add the five NFS shares. This is a nice task for PowerCLI and the New-Datastore cmdlet:

Update: I had to remove the last / from the Path variable. If you do not remove the last / the script will mount the NFS share but with a new UUID. See Damian Karlson his post about this subject here.

New-Datastore -Nfs -VMHost 10.23.112.345 -Name Datastore -Path $remotePath -NfsHost $remoteHost

I can created this one-liner for all the NFS datastores and run it. But what I really wanted to do, is to grab all the NFS datastores from esx2.ict-freak.local and mount them on esx1.ict-freak.local.

What kind of info do we need to get from the reference host esx2.ict-freak.local. According to the help file from PowerCLI we need the following parameters:

-Name Datastore -Path $remotePath -NfsHost $remoteHost

So lets see if we can find these information on the reference host. First we start with a get-datastore:

$REFHOST = Get-VMHost "esx2.ict-freak.local"
$REFHOST | Get-Datastore | Where {$_.Type -eq "NFS"}

The output will look like this:

image

The only thing we see in the above output is the Name of the share. So lets see what the Get-View cmdlet can show us. I use the NFS_Iso in this example:

$nfsView = $REFHOST | Get-Datastore "NFS_Iso" | Get-View

you can see the members via $nfsView | Get-Member:

image

Hmm lots of methods and properties in here. But lets take a look at the Summery property:

image

That’s interesting the Summary property does have the Name and the Url of the NFS datastore. This is the information we need to accomplish the task. But before we can continue we need to split the Url so we can use the IP address for the –NfsHost  parameter and the /home/nfs/iso/ for the -Path parameter. I used the following script to accomplish this task:

$REFHOST = Get-VMHost "esx2.ict-freak.local"
foreach($nfs in (Get-VMhost $REFHOST | Get-Datastore | Where {$_.type -eq "NFS"} | Get-View)){
    $share = $nfs.Summary.Url
    $share = $share.Replace("//","/")
    $share = $share.TrimStart("netfs:/")
    if($share -match "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"){
        $ip = $matches[0]
    }
    $remotePath = $share.Trim("$ip")
    $remoteHost = $ip
    $shareName = $nfs.Name

    Write-Host "NFS Share info: `nPath: $($remotePath) `nHost: $($remoteHost) `nShare: $($shareName) `n"
}    

The foreach loop will load all the NFS datastores and will loop through them one by one. The first $share variable loads the Url vai $nfs.Summary.Url. The second $share will replace the double // with a single / and the latest $share variable removes the netfs:/ from the Url. The $ip variable will add the IP address if it’s present and accepted by the little regex query. The last tricky one was to split the IP address from the Url. I used the .Trim() method to remove the IP address from the $ip variable. Now lets run the script to see the output:

image

So finally we got the variables we need, so we can use the new-datastore cmdlet with all the parameters. But before we can do that we need to find a way to check if the datastore already exists on the new host (esx1.ict-freak.local). I used the trick @alanrenouf used in his PowerCLI easy vswitch portgroup setup post:

    $NEWHost | Get-Datastore | Where{$_.Name -eq $shareName -and $_.type -eq "NFS"} -ErrorAction SilentlyContinue
    If (($NEWHost | Get-Datastore | Where{$_.Name -eq $shareName -and $_.type -eq "NFS"} -ErrorAction SilentlyContinue )-eq $null){
        Write-Host "NFS mount $shareName doesn't exist on $($NEWHOST.Name)" -fore Red
        Write-Host "NFS Share info: `nPath: $($remotePath) `nHost: $($remoteHost) `nShare: $($shareName) `n" 

    }

So this is the final script to check if the Datastore exists or not:

$REFHOST = Get-VMHost "esx2.ict-freak.local"
$NEWHost = Get-VMHost "esx1.ict-freak.local"
foreach($nfs in (Get-VMhost $REFHOST | Get-Datastore | Where {$_.type -eq "NFS"} | Get-View)){
    $share = $nfs.Summary.Url
    $share = $share.Replace("//","/")
    $share = $share.TrimStart("netfs:/")
    if($share -match "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"){
        $ip = $matches[0]
    }
    $remotePath = $share.Trim("$ip")
    $remoteHost = $ip
    $shareName = $nfs.Name

    $NEWHost | Get-Datastore | Where{$_.Name -eq $shareName -and $_.type -eq "NFS"} -ErrorAction SilentlyContinue
    If (($NEWHost | Get-Datastore | Where{$_.Name -eq $shareName -and $_.type -eq "NFS"} -ErrorAction SilentlyContinue )-eq $null){
        Write-Host "NFS mount $shareName doesn't exist on $($NEWHOST.Name)" -fore Red
        Write-Host "NFS Share info: `nPath: $($remotePath) `nHost: $($remoteHost) `nShare: $($shareName) `n" 

    }
}

and this is the output:

image

The final step to accomplish my goal is to add the new-datastore cmdlet to the above script. This is the easy part of the script:

New-Datastore -Nfs -VMHost $NEWHost -Name $Sharename -Path $remotePath -NfsHost $remoteHost

The final script:

$REFHOST = Get-VMHost "esx2.ict-freak.local"
$NEWHost = Get-VMHost "esx1.ict-freak.local"
foreach($nfs in (Get-VMhost $REFHOST | Get-Datastore | Where {$_.type -eq "NFS"} | Get-View)){
    $share = $nfs.Summary.Url
    $share = $share.Replace("//","/")
    $share = $share.TrimStart("netfs:/")
    if($share -match "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"){
        $ip = $matches[0]
    }
    $remotePath = $share.Trim("$ip")
    $RemotePath = $RemotePath.TrimEnd("/")
    $remoteHost = $ip
    $shareName = $nfs.Name

    Get-VMHost $NEWHOST | Get-Datastore | Where {$_.Name -eq $shareName -and $_.type -eq "NFS"} -ErrorAction SilentlyContinue
    If ((Get-VMHost $NEWHOST | Get-Datastore | Where {$_.Name -eq $shareName -and $_.type -eq "NFS"} -ErrorAction SilentlyContinue )-eq $null){
        Write-Host "NFS mount $shareName doesn't exist on $($NEWHOST)" -fore Red
        New-Datastore -Nfs -VMHost $NEWHost -Name $Sharename -Path $remotePath -NfsHost $remoteHost    | Out-Null
    }
}

@maishsk also noticed the last / bug and he fixed it with the $remotePath.TrimEnd(“/”) So I added his solution to the script because it’s better human readable Winking smile.

and its output:

image

Note the extra $remotePath variable:

$RemotePath = $RemotePath.ToString().Remove($RemotePath.Length - 1,1)

This is a little bugfix. In the screenshots above you see an extra / at the end of the path value. This extra / is also added during the creation of the NFS mount on the second vSphere host. But when this extra / isn’t used during the initial mount of the NFS datastore it will generate a different NFS UUID and vMotion will stop working. See more info about NFS UUID mismatches in Damian Karlson his post: http://damiankarlson.com

Advertisement

3 thoughts on “PowerCLI: Easy NFS datastore setup

  1. this didn’t work with 5 anymore, but i loved the script, however after some playing this should do it! thanks for your base work!

    foreach($nfs in (Get-VMhost $refhost | Get-Datastore | Where {$_.type -eq “NFS”} | Get-View)){
    $remoteip = $nfs.Info.Nas.remotehost
    $shareName = $nfs.info.nas.remotepath
    $nfsname = $nfs.Info.name

    Get-VMHost $desthost | Get-Datastore | Where {$_.Name -eq $nfsName -and $_.type -eq “NFS”} -ErrorAction SilentlyContinue
    If ((Get-VMHost $desthost | Get-Datastore | Where {$_.Name -eq $nfsName -and $_.type -eq “NFS”} -ErrorAction SilentlyContinue )-eq $null){
    Write-Host “NFS mount $shareName doesn’t exist on $($desthost)” -fore Red
    New-Datastore -Nfs -VMHost $desthost -Name $nfsname -Path $shareName -NfsHost $remoteip | Out-Null
    }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.