Archive for the ‘Data Centre’ Category


In this post I’m going to share a script I wrote that speeds up the creation of Cisco MDS 9000 series SAN switch configurations. If like me you’re in the business of adding Cisco UCS Blades in bulk you will know that copying and pasting large amounts of wwpn’s and zoning can be prone to errors – let alone time consuming. Thankfully Cisco released the Cisco PowerTool pack for PowerShell for managing UCS environments; this allows easy access to extract all the necessary information needed to create our configurations quickly and consistently.

The SAN that was used to demo this script contains two Cisco MDS switches with two connections a piece to SPs on an EMC VNX5300.

This script should be treated as an example rather than a script that is generic enough to be run in any environment. In the below script you will notice three areas that have “#User to change as needed” comments, here you will see that I have unique values specific to my environment that will need to be edited. These values include my VSAN numbering, Zoneset naming and some datacentre location information.

First you will need to import the PowerShell UCS module and then connect to your UCS environment:

Import-Module CiscoUCSPS
Connect-Ucs <UCSIP> -Credential (Get-Credential)

Upon running the script you will be prompted for the serviceProfilePrefix which I use to filter Service Profile Names, in my example I will use the text “PDC” which represents a collection of VMware vCloud Director Provider vDC Blades.

Note: To copy the code please double click within the code box.

<#
.NOTES
Author: John Milner aka jfrmilner
Blog  : https://jfrmilner.wordpress.com
Post  : http://wp.me/pFqJZ-6y
Requires: Powershell + Cisco UCS PowerTool Pack
Legal: This script is provided "AS IS" with no warranties or guarantees, and confers no rights. You may use, modify, reproduce, and distribute this script file in any way provided that you agree to give the original author credit.
Version: v1.0 - 27 Oct 2013
#>
param ( [Parameter(Mandatory=$true)] [System.String]$serviceProfilePrefix )

$vSANs = 3801, 3802 #User to change as needed

#Clear Host Screen
Clear-Host

#Collect all Initiators
$initiators =  Get-UcsWwnInitiator | ? { $_.Assigned -eq "yes" } | Sort-Object -Property AssignedToDn
foreach ( $initiator in $initiators ) {
$split = $initiator.AssignedToDn -split "/"
Add-Member -InputObject $initiator -Name ServiceProfile -MemberType NoteProperty -Value ($Split[1] -replace "^ls-") -Force
Add-Member -InputObject $initiator -Name vHBA -MemberType NoteProperty -Value "vHBA$($split[2].ToCharArray()[-1])" -Force
}

#Create Service Profile List
$serviceProfiles = $initiators | Sort-Object serviceprofile | Select-Object serviceprofile -Unique
#Filter List with serviceProfilePrefix param
$serviceProfiles = $serviceProfiles | ? { $_.serviceProfile -match $serviceProfilePrefix } | % { $_.serviceprofile }

$vHBAs = "vHBA0","vHBA1"

foreach ( $vSAN in $vSANs ) {

switch ($vSAN) {
3801 {"!D10 Config" ; $vHBANum = $vHBAs[0] ; $zoneSetName = "D11_MDS01" ; $SPA = "SPA0" ; $SPB = "SPB1" ; [int]$dataHall = 1 } #User to change as needed
3802 {"!H18 Config" ; $vHBANum = $vHBAs[1] ; $zoneSetName = "H18_MDS02" ; $SPA = "SPA1" ; $SPB = "SPB0" ; [int]$dataHall = 2 } #User to change as needed
}
foreach ( $serviceProfile in $serviceProfiles ) {
$vHBA = $initiators | ? { ($_.serviceProfile -eq $serviceProfile) -and ($_.vHBA -eq $vHBANum)}

"fcalias name ESX_DH$($dataHall)_$($serviceProfile)_$($vHBA.vHBA) vsan $($vSAN)"
"member pwwn $(($vHBA.Rn).tolower())"
}
foreach ( $serviceProfile in $serviceProfiles ) {
"zone name Z_ESX_DH$($dataHall)_$($serviceProfile)_$($vHBANum)_DH$($dataHall)_VNX5300 vsan $($vSAN)"
"member fcalias DH$($dataHall)_VNX5300_$($SPA)"
"member fcalias DH$($dataHall)_VNX5300_$($SPB)"
"member fcalias ESX_DH$($dataHall)_$($name)_$($vHBANum)"
}
"zoneset name ZS_NDC1_$($zoneSetName)_SAN_JFRMILNER_NET vsan $($vSAN)"
foreach ( $serviceProfile in $serviceProfiles ) {
"member Z_ESX_DH$($dataHall)_$($serviceProfile)_$($vHBANum)_DH$($dataHall)_VNX5300"
}

}

The output of the script will be presented to the console, here is an example:

MDS Config

As you can see in the above output the script has created fcaliases for each of the newly provisioned PDC Service Profile HBAs, created Zones and added these to the Zoneset for both my Switches.

Additionally this script can be easily modified to do bulk removals as it’s just a matter of prefixing some of the configuration lines with “no” for example “no fcalias name” and “no zone name”, as this has potential of causing an APD situation I will not be providing any example of this.

I hope you find this script useful and that it saves you as much time and reduces human error incidents as it has for me. As always please add a comments below and thank you for reading.

Regards,

jfrmilner


Today I found myself in a situation where I needed to enter a product key and then activate several Windows 2008 R2 Servers, this would be a real chore if I had to use the GUI.

A quick internet search brought me to slmgr.vbs a great little script that has been included with Windows since Vista/2008 and it even works remotely. Looking at the options on the TechNet page you can see that a simple “slmgr.vbs ServerName -ipk “AAAAA-BBBBB-CCCCC-DDDDD-EEEEE””(Replace the latter with a valid product key) would sort the product key task out for me. To activate and complete the task I would need to use with the following command “slmgr.vbs ServerName –ato”, simple.

One of the tricks I find myself doing again and again from the PowerShell prompt is simple string manipulation, for example:

Get-VM LAB* | % { write "slmgr.vbs $($_.Name) -ipk `" AAAAA-BBBBB-CCCCC-DDDDD-EEEEE `" }

Would create something along the lines of:

slmgr.vbs LAB1-SQL-001 -ipk “AAAAA-BBBBB-CCCCC-DDDDD-EEEEE”
slmgr.vbs LAB1-DC-001 -ipk “AAAAA-BBBBB-CCCCC-DDDDD-EEEEE”
slmgr.vbs LAB1-EX-001 -ipk “AAAAA-BBBBB-CCCCC-DDDDD-EEEEE”

To save yourself even more time you can send the output of the above command directly to the clipboard by piping to “clip”, for example:

Get-VM LAB* | % { write "slmgr.vbs $($_.Name) -ipk `" AAAAA-BBBBB-CCCCC-DDDDD-EEEEE `" } | clip

This allows you to paste back into the console and execute your commands.

After each command executes you are presented with a pop up window like the one below

Activate-01

Now do the same for your activation command

Get-VM LAB* | % { write "slmgr.vbs $($_.Name) -ato" }  | clip

After each command executes you are again presented with another pop up window, this one will look like the one below

Activate-02

Admittedly this is a little quick and dirty and could do with some error checking but it got the job done and saved me a couple of hours work, hopefully it will save others time as well.

Thanks for reading.

jfrmilner


This month I managed to get my hands on an Opengear IP-PDU 9108 8 Port Switched & Metered PDU http://www.opengear.com/product-ip-pdu.html which seems to be a rebranded http://www.digipower.com.tw/PDU/BravoPDUswitched.htm. The PDU can be controlled over the network using a web interface or SNMP.

IPPDU-04

Digipower have an online demo available on the following link http://www.digipower.com.tw/PDU/BravoPDUswitched.htm, at the time of writing this post the address was http://211.75.143.21:8080 (snmp:1234). I’ll demo my code against this test system and if you want to test the code is a safe environment this could be useful for you.

Why bother if you have web and SNMP access?

Well I wanted to do this with native PowerShell cmdlets so that ruled out SNMP and to be quite honest the web portal is great for setting up the main configuration but a bit too long winded to turn a single socket on and off, or get a quick outlet status report. I also wanted to do this with web requests to that I could later use the same techniques learned for this post with an Arduino project I have on my to-do list.

Investigating the cgi and scripts running on the PDU

The PDU’s default configuration is to have the hostname  set to “digiboard” so assuming DHCP is running on your network after a few seconds you’ll be able to connect to the web portal with http://digiboard. I navigated to the Control > Outlet (http://digiboard/outlet.htm) page and selected View Source, near the bottom of the code I noticed a mention of the status.xml:

newAJAXCommand('status.xml'

Let’s starts with the Status.XML, in my browser I loaded up http://digiboard/status.xml and the response was:

<?xml version="1.0"?>

<a href="http://digiboard/status.xml"><response></a><pot0>,,0.4,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1,1,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,5,5,5,5,5,5,5,0.4,0,</pot0><outn>,,,,,,,,</outn></response>

Jackpot, now I needed to find where in that long response the actual statuses of each of the outlets were. I thought the best to make them stand out was to set them alternate e.g.

1,0,1,0,1,0,1,0 first

Result : ,,0.2,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,5,5,5,5,5,5,5,0.2,0,

and then 0,1,0,1,0,1,0,1.

Result: ,,0.2,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,5,5,5,5,5,5,5,0.2,0,

This pattern was easy to spot and just to make things as clear as possible for this post I’ve bolded and underlined the areas of interest.

To read this with PowerShell I used the Invoke-WebRequest cmdlet but first I created a variable to hold the credentials to access the web portal (this is just to prevent entering the credentials on each request):

$cred = Get-Credential #Defaults are snmp:1234
$statusXML = Invoke-WebRequest -Uri http://digiboard/status.xml -Credential $cred

Collect the Sub String that we’re interested in:

$statusXML.Content.Substring(52,15)

0,1,0,1,0,1,0,1

That wraps up the Status.xml, now onto turning the outlets on and off.

Also on the http://digiboard/outlet.htm  source code I noticed two functions of interest:

function GetGroupOn()
{
var s = '';
var i = 1;
//for(var i = 1; i < 3; i++)
for(var j = 1; j < 9; j++)
{
if( document.getElementById('C'+ i + j).checked )
s += 1;
else
s += 0;
}
s += "00000000" + "00000000";
if(confirm("turn on the outlet"))
newAJAXCommand('ons.cgi?led='+s, null, false);
}

function GetGroupOff()
{
var s = '';
var i = 1;
//for(var i = 1; i < 3; i++)
for(var j = 1; j < 9; j++)
{
if( document.getElementById('C'+ i + j).checked )
s += 1;
else
s += 0;
}
s += "00000000" + "00000000";
if(confirm("turn off the outlet"))
newAJAXCommand('offs.cgi?led='+s, null, false);
}

You can see from these two functions that either ons.cgi? or off.cgi? is called with two blocks of eight numbers, with a bit of luck I tried:

Invoke-WebRequest http://digiboard/offs.cgi?led=000000010000000000000000 -Credential $cred

Sure enough outlet 8 (H) turned off, let’s try and turn this back on:

Invoke-WebRequest http://digiboard/ons.cgi?led=000000010000000000000000 -Credential $cred

Yep, that worked!

I would guess that the second block of 8 numbers is for 16 outlet PDU’s but I cannot be sure.

Creating the Get-IPPDU function

Using what I discovered I wrote the following function to collect and format the current status of the outlets:

function Get-IPPDU {
<#
.NOTES
Author: John Milner aka jfrmilner
Blog  : https://jfrmilner.wordpress.com
Post  : http://wp.me/pFqJZ-5l
Requires: Powershell V3
Legal: This script is provided "AS IS" with no warranties or guarantees, and confers no rights. You may use, modify, reproduce, and distribute this script file in any way provided that you agree to give the original author credit.
Version: v1.0 - 06 Jan 2013
#>
param(
[System.Management.Automation.PSCredential]$Credential,
$URI = "http://digiboard"
)
try {
$statusXML = Invoke-WebRequest -Uri ($URI + "/status.xml") -Credential $Credential
}
catch {
$_.Exception.Message
break
}
$statusXMLArray = $statusXML.Content.Substring(52,15)
#Status Report
$outlet = 65
$statusReport = @()
foreach ($status in ($statusXMLArray -split ",") ) {
$statusReport += @{$("Outlet" + [char]$outlet)=$status}
$outlet++
}
$statusReport
}

This provides a couple of parameters, URI and Credentials, both are self-explanatory. Here is a screenshot of me trying this against the one on my LAN and the one from the demo site mentioned earlier.

IPPDU-01

Creating the Set-IPPDU function

Using what I discovered I wrote the following function to change the power states of outlets:

function Set-IPPDU {
<#
.NOTES
Author: John Milner aka jfrmilner
Blog  : https://jfrmilner.wordpress.com
Post  : http://wp.me/pFqJZ-5l
Requires: Powershell V3
Legal: This script is provided "AS IS" with no warranties or guarantees, and confers no rights. You may use, modify, reproduce, and distribute this script file in any way provided that you agree to give the original author credit.
Version: v1.0 - 06 Jan 2013
#>
param(
[Parameter(Mandatory=$true)]
[ValidatePattern("[A-H]{1,8}")]
[array]$Outlets,
[ValidateSet("On","Off")] $PowerState,
$URI = "http://digiboard",
[System.Management.Automation.PSCredential]$Credential
)
$outletKeys = [PSCustomObject][Ordered]@{A=0;B=1;C=2;D=3;E=4;F=5;G=6;H=7}
$statusKeys = [PSCustomObject][Ordered]@{ON=1;OFF=0}

$charArray = ("00000000").ToCharArray()
foreach ($request in $outlets ) {
$charArray[($outletKeys.($request))] = "1"
"Turn {0} {1}" -f $request, $powerState
}
$stateString  = -join $charArray
$URI = ($URI + "/" + $powerState.ToLower() + "s.cgi?led=" + $stateString  + "0000000000000000")
Invoke-WebRequest $URI -Credential $Credential | Out-Null
}

We have both the URI and Credentials parameters like the Get-IPPDU function, I also added “Outlets” which accepts the letter name of the Outlet or a comma separated collection of outlet letters and finally the PowerState which accepts On or Off. Here is a screenshot of me testing the functions against the IP PDU on my LAN.

IPPDU-02

I think this is another great example of the power of the new PowerShell 3 Invoke-WebRequest cmdlet and an interesting way to get PowerShell interacting with system/hardware it was never intended for.

These functions should work with any of the digipower PDU’s, such as the Amazing PDU and Bravo PDU. Thanks for reading and as always your comments are welcome.

Regards,

jfrmilner