This blog has moved to http://ThePowerShellGuy.com
Greetings /\/\o\/\/
The following script will watch a 2003 Cluster for Events using a WMI Event Query.
As it’s the 100th post is a bit more descriptive ;-)
First it will make a WMI Connection to the Cluster, note that the Namespace path is not
root\CimV2 but
root\MSCluster as the Cluster WMI classes reside in there own namespace.
In the next part of the script we make the ManagementEventWatcher object.
And we provide it with a Query.
Note we use MSCluster_Event , this is the BaseClass of all other MS_cluster event classes, so this will give us all Events, You can also use the more specific classes as MSCluster_EventGroupStateChange to get only that events.
Next I set a Timeout on the EventWatcher,
this is because you can not break the script with Ctrl-C when the EventWatcher is waiting for an event, (you can break with ctrl-Break but this will exit the complete MSH shell session.), I did set it to 1 second, so I can Break the script every 1 seconds, more about this you can find here :MSH directory watcher with popup-balloon(there I did attach it to a hidden form and a pop-up baloon so I could do A-Sync event handling, this time the Eventwatcher is not in another Thread state, but I could not get the eventhandlers working for the start() and Stop() Methods, I did get the Stopped eventhandler script-block fired but only after a WaitForNextEvent() call)
Note, that this is the Timeout on waiting for an event to come into the Client queue, not how often WMI is looking for Events !That is stated in the Event Query :
SELECT * FROM MSCluster_Event WITHIN 10
The Within 10 is the interval that WMI will check for changes on the server side !.
So WMI will check for changes every 10 seconds, we are only stating that we only wait 1 second for an event and then go on to check for a key press and look in the Queue again.
So with a 1 second wait, we can check more servers in this loop or we can set the timeout higher for example 1 minute, still WMI will check every 10 Seconds and put changes in the queue, (for the example as we check all the events so sometimes we get 10 events or more in a couple of seconds this is not very handy (it will take us 10 loops to read them out, but in a production environment a warning on only a group change would be enough.)
So be aware of the difference between WITHIN 10 in the Event Query and the timeout on the check of the Event watcher Queue!.
In the next part of the Script,
We call FillTables, this is a helper function below the main function in the script that fills some HashTable Variables to do the translation of the Eventcodes we get back from WMI.
Note that it is called with a “Dot” so the variables get loaded in the current scope, in this case the Main function’s scope (That is the scripts not the Global!) as we do call it from there.
You see also that Main get called at the end of the script so that the FillTables function is declared before it gets called, but I still can place it below the main script.
Next the script enters the loop here :,
while (!$host.ui.rawui.KeyAvailable){
so it will loop until a key gets pressed (the timing we did in the event watcher timeout as explained earlier.
Then we wait for an event and display some info if we get an event.
First we have to translate the Date (as it is in Filetime Format).
"$([datetime]::fromfiletime($e.TIME_CREATED))"
Then we use the created HashTables to translate the codes we get back, for example :
“$($ClusterObject[[int]$e.EventObjectType])”
$e.EventObjectType is the Type of Object the change is about, this can be :.
0 = "Handle";
1 = "Cluster";
2 = "Node";
3 = "Group";
4 = "Resource";
5 = "ResourceType";
6 = "Network";
7 = "NetworkInterface";
8 = "Registry";
9 = "Quorum"
This number we give as an index to the HashTable (I had to explicitly cast it to an INT to get this working) so we get a more descriptive value back for our Message.
We do this a couple of times more till we come to the
EventNewState property.This one is a bit tricky because the Code returned will have
a different meaning Depending on the Object type of the Event.!!
So we do a Switch to select the right HashTable for the Translation.
(note I did not implement all hashTables yet.)
After all the translations we have the Following output :
.\clusterWatch.msh ClusterName
Watching ClusterName, Press Any Key to Quit
12/28/2005 5:10:10 PM Resource : TestApp Change : RESOURCE_STATE Node : Node1 State : OnlinePending
In this case I Brought a Test Application on Line.
But As we did subscribe to all events we get some more events as the loops goes on.
Cluster ClusterName Event : 12/28/2005 5:10:10 PM Group : TestGroup Change : GROUP_STATE Node : Node1 State : Pending Cluster ClusterName Event : 12/28/2005 5:10:10 PM Resource : TestAppChange : RESOURCE_STATE Node : Node1 State : Online Cluster ClusterName Event : 12/28/2005 5:10:10 PM Group : TestGroup Change : GROUP_STATE Node : Node1 State : Online
With a Application Down the State of the Goup was PartialOnline, the resource comes online, hence the state of the resourcegroup will also change now.
Also you can see how important the Switch statement is, !
The Resourcestate 2 means "Online" and GroupstateType 2 means Fialed !!
That’s it,
my next step would be to get the messages in a popup-balloon as in the :
MSH directory watcher with popup-balloon, example, I started with this but could not get the eventhandler scriptblocks called, but you still use the notify icon to output the Message, but I would do that Only for the GroupChanged Events By using the MSCluster_EventGroupStateChange Class, to not overflow yourself with balloons.
I will post a example to output the current status of a cluster later.
gr /\/\o\/\/
Reference :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mscs/mscs/mscluster_event.aspAnd The Script !
# ClusterWatch.msh
#
# Watch a Cluster for Events,
#
# /\/\o\/\/ 2005
# http://mow001.blogspot.com
Param ($cluster = "ClusterName")
Function Main {
"Watching $cluster, Press Any Key to Quit"
trap {Break}
# Make a WMI Connection to the Cluster
$ms = new-object system.management.managementscope
$ms.path = "\\$cluster\root\MSCluster"
$ms.connect()
# Make Event Watcher
$ew = new-object system.management.ManagementEventWatcher
$ew.scope = $ms
$ew.query = "SELECT * FROM MSCluster_Event WITHIN 10"
$opt = new-object System.Management.EventWatcherOptions
$opt.Timeout = [timespan]::FromSeconds(1)
$ew.options = $opt
# Fill Lookup Tables :
. FillTables
# Wait for event :
while (!$host.ui.rawui.KeyAvailable){
trap {Continue}
$e = $null
$e = $ew.WaitForNextEvent() 2> $null
if ($e) {
"`nCluster $cluster Event :`n"
"$([datetime]::fromfiletime($e.TIME_CREATED))"
"$($ClusterObject[[int]$e.EventObjectType]) : $($e.EventObjectName)"
"Change : $($ChangeType[[int]$e.EventTypeMinor])"
"Node : $($e.EventNode)"
switch ($e.EventObjectType) {
2 {"State : $($NodeState[[int]$e.EventNewState])"}
3 {"State : $($GroupstateType[[int]$e.EventNewState])"}
4 {"State : $($ResourceState[[int]$e.EventNewState])"}
}
}
}
}
# Definition of LookUp Tables :
Function FillTables {
$NodeState = @{
0 = "Up"
1 = "Down"
2 = "Joining"
3 = "Paused"
-1 = "StateUnknown"
}
$GroupstateType = @{
-1 = "StateUnknown";
0 = "Online";
1 = "Offline";
2 = "Failed";
3 = "PartialOnline";
4 = "Pending"
}
$ResourceState = @{
-1 = "StateUnknown"
0 = "Inherited"
1 = "Initializing"
2 = "Online"
3 = "Offline"
4 = "Failed"
128 = "Pending"
129 = "OnlinePending"
130 = "OfflinePending"
}
$ClusterObject = @{
0 = "Handle";
1 = "Cluster";
2 = "Node";
3 = "Group";
4 = "Resource";
5 = "ResourceType";
6 = "Network";
7 = "NetworkInterface";
8 = "Registry";
9 = "Quorum"
}
$ChangeType = @{
0x00000001 = "NODE_STATE";
0x00000002 = "NODE_DELETED";
0x00000004 = "NODE_ADDED";
0x00000008 = "NODE_PROPERTY";
0x00000010 = "REGISTRY_NAME";
0x00000020 = "REGISTRY_ATTRIBUTES";
0x00000040 = "REGISTRY_VALUE";
0x00000080 = "REGISTRY_SUBTREE";
0x00000100 = "RESOURCE_STATE";
0x00000200 = "RESOURCE_DELETED";
0x00000400 = "RESOURCE_ADDED";
0x00000800 = "RESOURCE_PROPERTY";
0x00001000 = "GROUP_STATE";
0x00002000 = "GROUP_DELETED";
0x00004000 = "GROUP_ADDED";
0x00008000 = "GROUP_PROPERTY";
0x00010000 = "RESOURCE_TYPE_DELETED";
0x00020000 = "RESOURCE_TYPE_ADDED";
0x00100000 = "NETWORK_STATE";
0x00200000 = "NETWORK_DELETED";
0x00400000 = "NETWORK_ADDED";
0x00800000 = "NETWORK_PROPERTY";
0x01000000 = "NETINTERFACE_STATE";
0x02000000 = "NETINTERFACE_DELETED";
0x04000000 = "NETINTERFACE_ADDED";
0x08000000 = "NETINTERFACE_PROPERTY";
0x10000000 = "QUORUM_STATE";
0x20000000 = "CLUSTER_STATE";
0x40000000 = "CLUSTER_PROPERTY";
0x80000000 = "HANDLE_CLOSE"
}
}
. main