Friday, July 20, 2012

Powershell to get VM, ESX, Datastores and... computer rooms

Having decided to make a Powershell map of the vSphere virtual machines, ESX hosts, datastores and computer rooms of my company, I wrote the following script which aims to generating a grid view (through the use of the nice Out-GridView cmdlet) showing those information in a easily human-readable manner. Of course in my script I had to make a few concession: for instance I had no dynamic information about the computer room inside which every ESX is found, so I have hard-coded the list of our computer rooms in a Switch statement which you have to adapt to your infrastructure:
switch -wildcard ($_.host) 
    { 
        "ESXhost1*" {"Site-1-Computer-room1"} 
        "ESXhost2*" {"Site-1-Computer-room1"} 
        "ESXhost3*" {"Site-1-Computer-room2"} 
        "ESXhost4*" {"Site-2-Computer-room1"} 
        "ESXhost5*" {"Site-3-Computer-room1"} 
        "ESXhost6*" {"Site-3-Computer-room2"} 
        "ESXhost7*" {"Site-3-Computer-room3"} 
        default {"Unknown computer room"}
    }
For the rest all the operations are automatic. I first retrieve all the VMs on my vSphere servers (with Get-VM), then for each VM I go retrieve 8 properties:

 - the VM name
 - the VMhost
 - the computer room
 - the # of GB effectively used by the VM
 - the # of GB provisioned to the VM
 - the name of the datastore containing the virtual machine disks (vmdk)
 - the size in GB of the datastore
 - the # of free GB of the datastore

To get the final report done I had to use a few Powershell goodies such as  'Nested Expressions' and mathematical Round function:
@{label="Datastore size";expression={(Get-Datastore -VM $_ | 
select @{label="Datastore size";expression={[Math]::Round($_.capacityMB/1kb,2)}}).'Datastore size'}}
Probably this is not the bast way to do it, that's why I am asking you to comment and suggest improvements to my script if you feel like.

Once the grid appears on the screen you can perform any kind of filtering using the methods allowed by this kind of object.

Heres' the script which you can copy and save as 'vm_and_datastores.ps1'.
<#
.SYNOPSIS
Generates a table with VM allocation size and datastore utilization.

.DESCRIPTION
Generates a table reporting detailed information about VMs size, provisioned and used space as well as datastore information.

Mandatory parameters is vc.
No optional parameters.

 -vc
Specifiy the name of the vSphere server.

.EXAMPLE
vm_and_datastores -vc virtualcentername

.NOTES
Author: Carlo happysysadm.com
Date: 7/20/2012
#>

[CmdletBinding()]

param
(
[parameter(Mandatory=$true)]
$vc = $(Read-Host 'Enter vSphere server name'))

Add-PSSnapIn VMware.VimAutomation.Core
Connect-VIServer $vc

Get-VM | sort name | select name,vmhost,@{label="Computer room";expression={switch -wildcard ($_.host) 
    { 
        "ESXhost1*" {"Site-1-Computer-room1"} 
        "ESXhost2*" {"Site-1-Computer-room1"} 
        "ESXhost3*" {"Site-1-Computer-room2"} 
        "ESXhost4*" {"Site-2-Computer-room1"} 
        "ESXhost5*" {"Site-3-Computer-room1"} 
        "ESXhost6*" {"Site-3-Computer-room2"} 
        "ESXhost7*" {"Site-3-Computer-room3"} 
        default {"Unknown computer room"}
    }}},
@{label="Used GB";expression={[Math]::Round($_.usedspacegb,2)}},
@{label="Provisioned GB";expression={[Math]::Round($_.provisionedspaceGB,2)}},
@{label="Datastore name";expression={(Get-Datastore -VM $_ | select name).name}}, 
@{label="Datastore size";expression={(Get-Datastore -VM $_ | 
select @{label="Datastore size";expression={[Math]::Round($_.capacityMB/1kb,2)}}).'Datastore size'}},
@{label="Datastore free GB";expression={(Get-Datastore -VM $_ | 
select @{label="Datastore free GB";expression={[Math]::Round($_.freespaceMB/1kb,2)}}).'Datastore free GB'}} |
Out-GridView -Title "VM & Datastores by happysysadm.com"
As I said, I am open to comments/questions/suggestions/critics/whatever. 

Thursday, July 19, 2012

One-liner to update virtual port group label on a ESX

I am publishing this here for reference because I couldn' t find what I needed elsewhere on the Web. This is a Powershell one-liner to update the virtual port group label on a specific ESX host:

Get-VirtualPortGroup -VMHost "esxhostname" |
  ?{$_.name -like "old_label"} |
  Set-VirtualPortGroup -Name "new_label"-Confirm:$false
 
Just be aware that any VM running on this ESX host and bound to this network label will immediately loose its connection. To prevent this and change the network label for the virtual machines, check the first script showed in this post here, which you should run just after mine.

Powershell script to dismount NFS datastore from ESX

There are days in which you are dreaming of everything being completely automated in your job. As an IT administrator these are the days I fire my new PowerGUI and start scripting. Yesterday for instance I had to remove a old NFS datastore which was mounted on a dozen of ESX servers and I felt like I didn't wanted to do it through the vSphere Client. I knew that there are cmdlets for this and decided to take advantage of them. Here follows the script as I imagined it. As you can see I started paying more attention to the style I use in my Powershell scripts, surely because next year I am wishing to enter the 2013 Powershell Scripting Games and I have the felling that the judges will pay a special attention to the look my scripts will have. So I introduced the script with a Synopis containing the syntax to call the script as well as two examples.

The script is structured around three main actions:
  1. Remove the datastore by means of Remove-Datastore cmdlet
  2. Remove the NIC used for vMotion with Remove-VMHostNetworkAdapter
  3. Remove the port group which contained the vMotion interface with Remove-VirtualPortGroup
Also the script takes in parameters, but I won't detail this here because  you can already read about in the script itself.

<#
.SYNOPSIS
Unmount the specified NFS datastore and removes the vMotion NIC from a given ESX host.

.DESCRIPTION
Connect to an ESX, unmounts the NFS datastore and suppress the vMotion port group.

Mandatory parameters are vc and host_name
Optional parameters are nfsname, vSwitch, vMotionNic and vMotionPG selection.

 -vc
Specifiy the name of the vSphere server.

 -host_name 
Specify the name of the ESX host on which to dismount the NFS datastore.

-nfsname
Specify the name of datastore to dismount.
By default it is nfs.

-vSwitch
Specify the name of virtual switch otowhich the vMotion PG is bound.
By default it is vSwitch0.

-vMotionNic
Specify the name of the vMotion NIC.
By default it is vMotion.

-vMotionPG
Specify the name of the vMotionport group.
By default it is vMotion.

.EXAMPLE
remove-nfs.ps1 -vc virtualcentername -host_name esxhostname

.EXAMPLE
remove-nfs.ps1 -vc virtualcentername -host_name esxhostname -nfsname nfs -vSwitch vSwitch0 -vMotionNic vMotion -vMotionPG  vMotion

.NOTES
Author: Carlo www.happysysadm.com
Date: 7/19/2012
#>

[CmdletBinding()]

param
(
[parameter(Mandatory=$true)]
$vc = $(Read-Host 'Enter vSphere server name'),
[parameter(Mandatory=$true)]
$host_name = $(Read-Host 'Enter ESX server name'),
$nfsname="nfs",
$vSwitch = "vSwitch0",
$vMotionNic = "vMotion",
$vMotionPG = "vMotion"
)

Add-PSSnapIn VMware.VimAutomation.Core
Connect-VIServer $vc

remove-datastore –datastore $nfsname –vmhost $host_name -Confirm:$false

$esxhost = Get-VMHost -Name $host_name

$vmk = Get-VMHostNetworkAdapter -VMHost $esxhost | where {$_.PortgroupName -eq $vMotionNic}
Remove-VMHostNetworkAdapter -Nic $vmk -Confirm:$false |Out-Null

$vpg = Get-VirtualPortGroup -Name $vMotionPG -VMHost $host_name
Remove-VirtualPortGroup -VirtualPortGroup $vpg -Confirm:$false |Out-Null

The outcome of the execution of this script can be followed on the interface of the vSphere Client:


For more information on managing Datastores using PowerCLI check this article in the VMware knowledge base. You could also check this blog post of mine which has a script to mount a NFS datastore.

Do not hesitate to tell if this post was useful to you. Comment are always welcome.

Wednesday, July 18, 2012

Powershell error: Cannot overwrite variable Host

While I was trying to run a new Powershell script today, I encountered a new error which I didn't expect but which wasn't difficult to solve. I thought that other people might overlook the same thing as I, so I decided to share it here.

The error I got was the following:

Cannot overwrite variable host because it is read-only or constant.
At line:1 char:1
+  <<<< ./remove-share.ps1
    + CategoryInfo          : WriteError: (host:String) [], SessionStateUnauth
   orizedAccessException
    + FullyQualifiedErrorId : VariableNotWritable

After a few seconds trying to focus on the final lines of this error message, the first line drew my attention. I was in fact trying to use the automatic variable $host to store a value as if it was normal variable. Which is not the case.

The solution to this problem consisted in changing the name of this variable to something else, something not reserved by the system I daresay, such as $host_name.

You can have a look here for more information on $host.

Monday, July 16, 2012

Next year wishlist...

These days I am wondering what I am going to to when summer ends. There are many possibilities, but a few of them attract me more than others.

I think first of all that I am going to attend VMworld in 2013 if the situation permits.


Actually I entered the contest to go to San Francisco for free for the 2012 edition (thanks to vDestination blog) but, needless to say, I didn't win... The lucky winner was Stacy Carter (@virtualstace). Here's the video of the official announcement of the winner. 

Beside this I think I am going to compete for the 2013 Powershell Scripting Games. There are of course far better Powershell developers out there, but I think that taking part in such a competition can be pretty fun, so I'll regularly check the Scripting Guys twits waiting for the announcement of the next edition.

What else could I do? I don't know, but feel free to suggest or share your personal objectives and maybe I'll be inspired. In the meantime I will do my best to keep this blog up-to-date and try to make more and more connections in the blogosphere.

Friday, July 13, 2012

Mounting NFS on VMware ESX with Powershell

This Friday I want to share with you a Powershell script I am using and re-using a lot of times these days to add a vMotion interface on my good ESX hosts and then mount a NFS filesystem. The code is pretty straightforward if you are used to administering VMware with Powershell. I have put a comments to make the reading more comfortable. Here's the code:
# let's load VMWare PowerCLI
Add-PSSnapIn VMware.VimAutomation.Core 
Connect-VIServer yourVCname 

# name of the ESX server to configure
$targethost = "youresxhost.com"

$vMotionIP = "your.ip.address.here"
$vMotionGateway = "your.gateway.ip.here"
$nfshost="your.nfs.ip.here"

# name you want to give to the mounted nfs
$nfsname="nfs" 

# switch that contains the vMotion port
$vswitch = "vSwitch0" 

# vMotion network label
$vMotionName = "VMotion" 

# vMotion subnet mask
$vMotionSubnet = "255.255.255.0"

# let's check that this IP is available
$qry = ('select statuscode from win32_pingstatus where address="' + $vMotionIP + '"')
$rslt = gwmi -query "$qry"
$res=$rslt.statuscode
if ($res -eq 0) #if IP isn't free, exit
{Write-Host "vMotion IP already used, choose another";exit}

# let's configure the nfs lock behavior
$tgt=Get-VMHost $targethost
if (($tgt |Get-VMHostAdvancedConfiguration -Name "nfs.lockdisable")["NFS.LockDisable"] -ne 1)
{Write-Host "NFS.LockDisable parameter was NOT set! updating..."
$tgt | set-VMHostAdvancedConfiguration -Name "NFS.LockDisable" -Value 1
if (($tgt |Get-VMHostAdvancedConfiguration -Name "nfs.lockdisable")["NFS.LockDisable"] -ne 1){Write-Host "NFS.LockDisable parameter still NOT set! Exiting...";exit}}

# going to create the port group for vMotion
New-VMHostNetworkAdapter -VMHost $targethost -PortGroup $vMotionName -VirtualSwitch $vswitch -IP $vMotionIP -SubnetMask $vMotionSubnet -VMotionEnabled:$true

# going to set the default gateway for VMkernel vMotion
# on device vmk0 (it may differ for you
Get-VMHostNetwork -VMHost $targethost | Set-VMHostNetwork -VMKernelGateway $VMotionGateway -VMKernelGatewayDevice "vmk0" 

# going to mount the NFS store
New-datastore –nfs –vmhost $targethost –name $nfsname –path "/mnt/vol1/vol1/vol" –nfshost $nfshost
As you can see the last line is the one that actually mounts the NFS filesystem through the New-Datastore cmdlet. In my case it is an Openfiler volume shared as "/mnt/vol1/vol1/vol". If you have any question about the configuration of Openfiler to work as a NFS for VMWare do not hesitate to ask! I personally use version 2.3.
Here's a few useful links to learn from:

Wednesday, July 11, 2012

Measuring VMWare Storage vMotion performance

Hello guys! A lot of Powershell these days as you can see from my previous blog posts. I have just been busy taking care of a Storage vMotion between a source ESX host, a buffer Openfiler-based NFS datastore, and a destination ESX host. As I was encountering pretty slow transfer rates between the ESX and the NFS datastore, I decided to improve the classic Powershell script used to relocate VMDKs (which is easily found on many blog posts). So, to make it better, I added the capability of executing a network performance measurement by taking advantage of the Measure-Command cmdlet.

In fact this cmdlet measure the time it takes to execute any kind of script/command/block. You just have to include the block of commands containing the "RelocateVM" method between "Measure-Command {" and "}" and just retrieve the "TotalSeconds" property to know how many seconds it took to make the transfer.

At this point round the returned value to the nearest whole number:
$duration1rounded = [math]::round($duration1)
Then make a quick calculation of the size of the disks for the VM you want to migrate:
$vmdksize =  [math]::round((Get-VM -Name $vm | Select-Object Name,UsedSpaceGB).usedspaceGB)
... and find the speed rate by dividing $vmdksize by $duration1rounded:
$speedrate = [math]::round(($vmdksize * 1024) / $duration1rounded)
Easy, right? Let's have a look at the whole script.

Friday, July 6, 2012

How to use Powershell to manage Counter Logs

After having written this post about using Powershell to deploy and start Counter Logs, I decided to go further and extends the same script to allow two more actions, that is:
  • stop collecting performance data on multiple server
  • remove performance data collection from multiple servers
As in the previous script configuration, the stop/remove operation is executed on a list of computers which is stored in a text file. The other required parameter for the stop/remove operation is the name of the Data Collector Set. The stop and remove operations are in fact methods of the interface DataCollectorSet which is used to manage performance data collections.

In my case the name of the Data Collection is always 'perf_$server', where $server is the name of the server recursively retrieved for the host list.

Wednesday, July 4, 2012

Using Powershell and Logman to deploy Counter Logs

In my working environment I have the necessity of automating most of my administration tasks in order to be able to pass them to other people or to keep the solution I have found at hand in case I have to do it again.

Powershell scripting is often my first choice in Windows farms, for its simplicity and for the capacity it has to integrate with older tools.

Last week I was trying to detect a widespread performance problem on a large farm of hundreds of Windows 2003 servers, and I decided to use Perfmon counters. Of course I knew well of the existence of Logman as a valid solution for deploying remote Counter Logs. My idea was then to use it and to pass two arguments to it: a list of servers and a list of counters.

On the Internet there are many resources explaining you how you can massively deploy Counter Logs to remote systems but none of them take care of explaining how you can start a remote Collection from Powershell, nor how to solve one of the most common problems you can face trying to do it

In fact, you will surely get two events when starting remote Counter Collections.

The first one is:
  • Event Type:    Warning
  • Event Source:    SysmonLog
  • Event ID:    2046
  • Description: The service was unable to add the counter '\\servername.domaine.com\Process(_Total)\Working Set' to the perf_servername log or alert. This log or alert will continue, but data for that counter will not be collected. Use the Run As option in the configuration program to run the log under  an account that has access to the performance data on the computer  from which you are collecting data.
The second one is:
  • Event Type:    Warning
  • Event Source:    SysmonLog
  • Event ID:    2029
  • Description: The service was unable to add any counters to the perf_servername log or alert.   This log or alert will not be started.

Related Posts Plugin for WordPress, Blogger...