Microsoft Analytics Platform System Nodes and Component Groups Discovery

Microsoft.SQLServerAppliance.APS.ApplianceNodes.Discovery (Discovery)

This object discovery detects any Microsoft Analytics Platform System Nodes and Component Groups.

Knowledge Base article:

Summary

This object discovery detects all Nodes and Component Groups of particular Microsoft Analytics Platform System.

Element properties:

TargetMicrosoft.SQLServerAppliance.APS.Appliance
EnabledTrue
Frequency14400
RemotableFalse

Object Discovery Details:

Discovered Classes and their attribuets:

Member Modules:

ID Module Type TypeId RunAs 
DS DataSource Microsoft.SQLServerAppliance.APS.ScriptedDiscoveryProvider Default

Source Code:

<Discovery ID="Microsoft.SQLServerAppliance.APS.ApplianceNodes.Discovery" Target="APSLibrary!Microsoft.SQLServerAppliance.APS.Appliance" Enabled="true">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.Appliance">
<Property TypeID="SSALibrary!Microsoft.SQLServerAppliance.Appliance" PropertyID="ApplianceVersion"/>
<Property TypeID="SSALibrary!Microsoft.SQLServerAppliance.Appliance" PropertyID="ApplianceManufacturer"/>
</DiscoveryClass>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.HostNodesCluster">
<Property TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster" PropertyID="ApplianceID"/>
<Property TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster" PropertyID="Name"/>
<Property TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster" PropertyID="ApplianceNetworkAddress"/>
<Property TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster" PropertyID="ApplianceTdsAddress"/>
</DiscoveryClass>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.HostNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.ComputeNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.ControlNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.ManagementNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.HeadNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.DataNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.ManagementNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.SecureNode"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.Network"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.Cluster"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.Software"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.StorageExternal"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.StorageInternal"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.Cooling"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.PowerSupply"/>
<DiscoveryClass TypeID="APSLibrary!Microsoft.SQLServerAppliance.APS.ServerProcessing"/>
</DiscoveryTypes>
<DataSource ID="DS" TypeID="Microsoft.SQLServerAppliance.APS.ScriptedDiscoveryProvider">
<IntervalSeconds>14400</IntervalSeconds>
<SyncTime/>
<ScriptName>APSApplianceNodesDiscovery.vbs</ScriptName>
<Arguments>"$MPElement$" "$Target/Id$" "$Target/Host/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$" "$Target/Property[Type="SSALibrary!Microsoft.SQLServerAppliance.Appliance"]/ApplianceID$" "$Target/Property[Type="APSLibrary!Microsoft.SQLServerAppliance.APS.Appliance"]/NetworkAddress$" "$Target/Property[Type="APSLibrary!Microsoft.SQLServerAppliance.APS.Appliance"]/TdsAddress$"</Arguments>
<ScriptBody><Script>Option Explicit
SetLocale("en-us")
' Eventlog EventID for all errors generated by scripts
Const SCRIPT_EVENT_ID = 4320

' This string will be appended to the beginning of first Event description parameter (Params/Param[1])
' Use &lt;RegExExpression /&gt; to narrow alerts to this MP only
Const MANAGEMENT_PACK_ID = "SQL2012APS MP"

Dim USE_EVENT_LOG, USE_ERROR_LOG

Const DEBUG_MODE = False
Const DEBUG_SCRIPT_EVENT_ID = 4201

USE_EVENT_LOG = true
USE_ERROR_LOG = false

Const ManagementGroupName = "$Target/ManagementGroup/Name$"
Dim sApplianceID
sApplianceID = ""

Sub LogWarning(ByVal customMessage)
Dim logger
Set logger = new ScriptLogger
logger.LogWarning(customMessage)
End Sub

Sub HandleWarning(ByVal customMessage)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogWarning(customMessage)
End If
End Sub

Sub HandleError(ByVal customMessage)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogError(customMessage)
If Not USE_ERROR_LOG Then
Wscript.Quit 0
Else
Wscript.Quit 1
End If
End If
End Sub

Sub HandleErrorInMonitoring(ByVal customMessage, ByRef oBag, ByRef oAPI)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogError(customMessage)
Call oAPI.Return(oBag)
Wscript.Quit 0
End If
End Sub

Sub HandleErrorInDiscovery(ByVal customMessage, ByRef oDiscovery, ByRef oAPI)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogError(customMessage)
oDiscovery.IsSnapshot = false
HandleDebugMessage("Discovery: IsSnapshot = false")
oAPI.Return(oDiscoveryData)
Wscript.Quit 0
End If
End Sub

Sub HandleNonCriticalErrorInDiscovery(ByVal customMessage, ByRef oDiscovery)
Dim logger
Set logger = new ScriptLogger
logger.LogError(customMessage)
oDiscovery.IsSnapshot = false
HandleDebugMessage("Discovery: IsSnapshot = false")
End Sub

Sub HandleDebugMessage(ByVal debugMessage)
Dim logger
Set logger = new ScriptLogger
logger.LogDebug(debugMessage)
End Sub


Const ERROR_EVENT_TYPE = 1
Const WARNING_EVENT_TYPE = 2
Const INFO_EVENT_TYPE = 4

Class ScriptLogger
Dim sourceLogEvent
Dim oAPI

Private Sub Class_Initialize()
sourceLogEvent = MANAGEMENT_PACK_ID
If (sApplianceID &lt;&gt; "") Then
sourceLogEvent = sourceLogEvent &amp; ". Appliance: " &amp; sApplianceID
End If
sourceLogEvent = sourceLogEvent &amp; ". Script: " &amp; WScript.ScriptName
Set oAPI = CreateObject("MOM.ScriptAPI")
End Sub

Private Function LogEvent(ByVal message, ByVal eventType, ByVal scriptEventID)
On Error Resume Next
Call oAPI.LogScriptEvent(sourceLogEvent, scriptEventID, eventType, message)
End Function

Public Function LogDebug(ByVal message)
if DEBUG_MODE Then
WScript.StdOut.WriteLine message
Call oAPI.LogScriptEvent(sourceLogEvent, DEBUG_SCRIPT_EVENT_ID, INFO_EVENT_TYPE, message)
End If
End Function

Public Function LogWarning(ByVal customMessage)
Dim message
If Err.number &lt;&gt; 0 Then
message = Replace(" Error Number: #P1# " &amp; VbCrLf &amp; " Description: #P2# ", "#P1#", CStr(Err.number and 65535) )
message = Replace(message, "#P2#", Err.Description )
message = customMessage &amp; VbCrLf &amp; message &amp; VbCrLf
Else
message = customMessage &amp; VbCrLf
End If

if DEBUG_MODE Then
WScript.StdOut.WriteLine message
End If
LogEvent message, WARNING_EVENT_TYPE, SCRIPT_EVENT_ID
End Function

Public Function LogError(ByVal customMessage)
Dim message
If Err.number &lt;&gt; 0 Then
message = Replace(" Error Number: #P1# " &amp; VbCrLf &amp; " Description: #P2# ", "#P1#", CStr(Err.number and 65535) )
message = Replace(message, "#P2#", Err.Description )
message = customMessage &amp; VbCrLf &amp; message &amp; VbCrLf
Else
message = customMessage &amp; VbCrLf
End If

If DEBUG_MODE Then
WScript.StdOut.WriteLine message
End If

If USE_EVENT_LOG Then
LogEvent message, ERROR_EVENT_TYPE, SCRIPT_EVENT_ID
End If
If USE_ERROR_LOG Then
WScript.StdErr.WriteLine message
End If
End Function

End Class

Function IsStringEmpty(ByVal sValue)
If sValue = EMPTY Or Trim(sValue) = "" Then
IsStringEmpty = true
Else
IsStringEmpty = false
End If
End Function
Const DEFAULT_SCHEMA = "[sys]"

'This class is wrapper for ADODB typical operations (Connect and Run SQL query)
Class SQLQuery
PRIVATE adodbConnection

Private Sub Class_Initialize()
On Error Resume Next
Set adodbConnection = CreateObject("ADODB.Connection")
Call HandleError("Cannot create ADODB.Connection")
End Sub

Private Sub Class_Terminate()
On Error Resume Next

'adodbConnection.State = 0 - Closed
If adodbConnection.State &gt; 0 Then
Call adodbConnection.Close()
End If

Call HandleError("Cannot close ADODB.Connection")
End Sub

'This method create and open connection to the server
'&lt;connectionString&gt; - host address to be used for connection;
Public Sub Connect(ByVal connectionString, ByVal sUser, ByVal sPass, ByVal nTimeout)
On Error Resume Next

Dim providerString, bSuccess, sDriver
bSuccess = False
Dim aSupportedDrivers : aSupportedDrivers = Array("10.0","11.0")

adodbConnection.ConnectionTimeout = nTimeout
' Since PDW allows for either SNAC 10 or 11 versions, the connection logic
' should retry for both if one failed to connect.
For Each sDriver In aSupportedDrivers
Call Err.Clear
providerString = "Driver={SQL Server Native Client " &amp; sDriver &amp; "};Server=" &amp; connectionString &amp; ";UID=" &amp; sUser &amp; ";PWD=" &amp; sPass
Call adodbConnection.Open(providerString)
If adodbConnection.State &lt;&gt; 1 Then
' Retry if connection failed with next version number
Else
bSuccess = True
Exit For
End If
Next
End Sub

'This function execute &lt;query&gt; with &lt;parameters&gt; and returns ADODB.RecordSet
'&lt;parameters&gt; must be array, and must be with the same order as sql query &lt;?&gt; paremetrs
Public Function ExecuteQueryToRecordSet(ByVal query, ByVal parameters)
Dim adodbCommand, oResults
Set adodbCommand = CreateObject("ADODB.Command")
adodbCommand.ActiveConnection = adodbConnection
adodbCommand.CommandText = query
Dim i, dataType, dataSize, parameter, length
length = UBound(parameters)
'Put parameters in to ADODB.Command
For i=0 To length
parameter = parameters(i)
dataType = GetDataTypeEnum(parameter)
dataSize = GetDataTypeSize(parameter)
adodbCommand.Parameters.Append(adodbCommand.CreateParameter("p"&amp;i, dataType, 1, dataSize, parameter)) '1 - this is adParamInput in ADODB. see http://www.w3schools.com/ado/met_comm_createparameter.asp#parameterdirenum
Next
Set ExecuteQueryToRecordSet = adodbCommand.Execute()
End Function

'returns size of string or 0 for others types
Private Function GetDataTypeSize(ByVal vValue)
If TypeName(vValue)="String" Then
GetDataTypeSize = Len(vValue)
Else
GetDataTypeSize = 0
End If
End Function

'this function returns int value of some DataTypeEnum
'values of adodb types see on http://www.w3schools.com/ado/met_comm_createparameter.asp#datatypeenum
Private Function GetDataTypeEnum(ByVal vValue)
If TypeName(vValue) = "Byte" Then
GetDataTypeEnum = 2 ' adSmallInt=2
End If
If TypeName(vValue)="Integer" or TypeName(vValue)="Long" Then
GetDataTypeEnum = 3 ' adInteger=3
End If
If TypeName(vValue)="Double" Then
GetDataTypeEnum = 5 ' adDouble=5
End If
If TypeName(vValue)="String" Then
GetDataTypeEnum = 8 ' adBSTR = 8
End If
End Function

End Class
On Error Resume Next

Dim GetApplianceVendorQuery, GetApplianceVersionQuery, GetNodesQuery, GetComponentGroupsQuery
GetApplianceVendorQuery = Replace("SELECT " &amp; _
" CASE ISNULL(MAX(LEFT(group_id, 1)), 0) " &amp; _
" WHEN 1 THEN 'Microsoft' " &amp; _
" WHEN 2 THEN 'HP' " &amp; _
" WHEN 3 THEN 'DELL' " &amp; _
" WHEN 4 THEN 'SWIFT' " &amp; _
" ELSE 'Unknown' " &amp; _
" END as 'appliance_vendor' " &amp; _
" FROM {0}.[pdw_health_component_groups] " &amp; _
" OPTION (LABEL = 'SCOM-APS-MP');", "{0}", DEFAULT_SCHEMA)

GetApplianceVersionQuery = "SELECT RTRIM(left(version(), charindex(' (X64)', version(), 0))) as 'pdw_version' OPTION (LABEL = 'SCOM-PDW-MP');"

GetNodesQuery = Replace("SELECT n.[pdw_node_id], n.[type], n.[name], n.[address], n.[is_passive], nc.[cluster_name], n.[region] as 'owning_region' " &amp; _
" FROM {0}.[dm_pdw_nodes] n " &amp; _
" LEFT OUTER JOIN ( SELECT DISTINCT s.[pdw_node_id], s.[property_value] as 'cluster_name' " &amp; _
" FROM {0}.[dm_pdw_component_health_status] s " &amp; _
" JOIN {0}.[pdw_health_components] c " &amp; _
" ON s.[component_id] = c.[component_id] " &amp; _
" JOIN {0}.[pdw_health_component_groups] g " &amp; _
" ON c.[group_id] = g.[group_id] " &amp; _
" JOIN {0}.[pdw_health_component_properties] p " &amp; _
" ON s.[property_id] = p.[property_id] " &amp; _
" AND s.[component_id] = p.[component_id] " &amp; _
" WHERE " &amp; _
" g.[group_name] = 'Cluster' " &amp; _
" AND c.[component_name] = 'Node' " &amp; _
" AND p.[property_name] = 'cluster_name' " &amp; _
" ) nc ON nc.[pdw_node_id] = n.[pdw_node_id] " &amp; _
" OPTION (LABEL = 'SCOM-APS-MP');", "{0}", DEFAULT_SCHEMA)

GetComponentGroupsQuery = Replace("SELECT " &amp; _
" nodes.[pdw_node_id] " &amp; _
" , nodes.[name] " &amp; _
" , groups.[group_id] " &amp; _
" , groups.[group_name] " &amp; _
" FROM " &amp; _
" {0}.[dm_pdw_component_health_status] healthStatus " &amp; _
" INNER JOIN {0}.dm_pdw_nodes nodes " &amp; _
" ON healthStatus.pdw_node_id = nodes.pdw_node_id " &amp; _
" INNER JOIN {0}.pdw_health_components components " &amp; _
" ON healthStatus.component_id = components.component_id " &amp; _
" INNER JOIN {0}.pdw_health_component_groups groups " &amp; _
" ON components.group_id = groups.group_id " &amp; _
" GROUP BY " &amp; _
" nodes.[pdw_node_id] " &amp; _
" , nodes.[name] " &amp; _
" , groups.[group_id] " &amp; _
" , groups.[group_name] " &amp; _
" OPTION (LABEL = 'SCOM-APS-MP');", "{0}", DEFAULT_SCHEMA)

Dim oAPI, oDiscoveryData
Dim SourceID, ManagedEntityID, sPrincipalName, sNetworkAddress, sTdsAddress
Dim oAppliance
Dim sUser, sPass
Dim oSqlQry

Set oAPI = CreateObject("MOM.ScriptAPI")
Call HandleError("Cannot create MOM.ScriptAPI object")

Call Main()

Sub Main()
On Error Resume Next

Dim oSecureInput
oSecureInput = Split(WScript.StdIn.ReadLine(), "@@secureseparator@@")

sUser = oSecureInput(0)
Call HandleError("Cannot read Username from SecureInput")
sPass = oSecureInput(1)
Call HandleError("Cannot read Password from SecureInput")

Dim oParams
Set oParams = WScript.Arguments

SourceID = oParams(0)
Call HandleError("Cannot read Source ID from parameters")

ManagedEntityID = oParams(1)
Call HandleError("Cannot read Managed Entity ID from parameters")

sPrincipalName = oParams(2)
Call HandleError("Cannot read Principal Name from parameters")

sApplianceID = oParams(3)
Call HandleError("Cannot read Appliance ID from parameters")

sNetworkAddress = oParams(4)
Call HandleError("Cannot read Network Address from parameters")

sTdsAddress = oParams(5)
Call HandleError("Cannot read TDS Address from parameters")

Set oDiscoveryData = oAPI.CreateDiscoveryData(0, SourceId, ManagedEntityID)
Call HandleError("Cannot create Discovery Data object")

Set oSqlQry = new SQLQuery
Call oSqlQry.Connect(sTdsAddress, sUser, sPass, 60)
Call HandleErrorInDiscovery("Connection failed", oDiscoveryData, oAPI)

Set oAppliance = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Appliance']$")
Call oAppliance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", sPrincipalName)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceID$", sApplianceID)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceVersion$", GetApplianceVersion())
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceManufacturer$", GetApplianceVendor())
Call oDiscoveryData.AddInstance(oAppliance)

Dim hNodes
Set hNodes = CreateObject("Scripting.Dictionary")
Call HandleError("Cannot create Scripting.Dictionary object")

Call DiscoverNodes(hNodes)
Call DiscoverComponentGroups(hNodes)

Call oAPI.Return(oDiscoveryData)
End Sub

Function GetApplianceVendor
On Error Resume Next
Dim oRecordSet
Set oRecordSet = oSqlQry.ExecuteQueryToRecordSet(GetApplianceVendorQuery, Array())
Call HandleWarning("Cannot determine Appliance Vendor")
GetApplianceVendor = oRecordSet(0)
oRecordSet.Close()
End Function

Function GetApplianceVersion
On Error Resume Next
Dim oRecordSet
Set oRecordSet = oSqlQry.ExecuteQueryToRecordSet(GetApplianceVersionQuery, Array())
Call HandleWarning("Cannot determine Appliance Version")
GetApplianceVersion = oRecordSet(0)
oRecordSet.Close()
End Function

Sub DiscoverNodes(ByRef hNodes)
On Error Resume Next
Dim hHostNodesClusters, oCluster
Set hHostNodesClusters = CreateObject("Scripting.Dictionary")
Call HandleError("Cannot create Scripting.Dictionary object")

Dim oRecordSet
Set oRecordSet = oSqlQry.ExecuteQueryToRecordSet(GetNodesQuery, Array())
Call HandleErrorInDiscovery("Cannot run query", oDiscoveryData, oAPI)

Dim oNode, nNodeID, sNodeType, sNodeTypeLowered, sClusterName, sName, sAddress, bIsPassive, sOwningRegion, sRegion
Dim oHostNodesClustersGroup, oRelationship

Dim bIsClustersGroupDiscovered : bIsClustersGroupDiscovered = False
Set oHostNodesClustersGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNodesClustersGroup']$")
Call oHostNodesClustersGroup.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", sPrincipalName)
Call oHostNodesClustersGroup.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", "Host Nodes Clusters")
Call oHostNodesClustersGroup.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNodesClustersGroup']/ApplianceID$", sApplianceID)


Do While Not oRecordSet.EOF
nNodeID = oRecordSet(0)
sNodeType = oRecordSet(1)
sNodeTypeLowered = LCase(sNodeType)
sName = oRecordSet(2)
sAddress = oRecordSet(3)
bIsPassive = oRecordSet(4)
sClusterName = oRecordSet(5)
sOwningRegion = oRecordSet(6)

oCluster = Empty

Call CreateNode(sNodeTypeLowered, oNode, sRegion)

If (Not IsEmpty(oNode)) Then
Call FillNodeProperties(oNode, nNodeID, sNodeType, sName, sAddress, sClusterName, bIsPassive, sRegion, sOwningRegion)
Call oDiscoveryData.AddInstance(oNode)
Call hNodes.Add(nNodeID, oNode)

' Nodes to Cluster
If (Not IsNull(sClusterName)) Then
If sNodeTypeLowered = "host" Then
If (Not bIsClustersGroupDiscovered) Then
Call oDiscoveryData.AddInstance(oHostNodesClustersGroup)
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ApplianceContainsHostNodesClustersGroup']$", oAppliance, oHostNodesClustersGroup)
bIsClustersGroupDiscovered = True
End If

Dim bIsNewClusterCreated
bIsNewClusterCreated = EnsureCluster(hHostNodesClusters, "$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNodesCluster']$", sClusterName)
Set oCluster = hHostNodesClusters.Item(sClusterName)

If (bIsNewClusterCreated) Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNodesClustersGroupContainsHostNodesCluster']$", oHostNodesClustersGroup, oCluster)
End If

Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNodesClusterContainsHostNode']$", oCluster, oNode)
End If
Else
' Nodes to Appliance
If sNodeTypeLowered = "management" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ApplianceContainsManagementNode']$", oAppliance, oNode)
ElseIf sNodeTypeLowered = "compute" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ApplianceContainsComputeNode']$", oAppliance, oNode)
ElseIf sNodeTypeLowered = "control" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ApplianceContainsControlNode']$", oAppliance, oNode)
ElseIf sNodeTypeLowered = "hdihead" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.ApplianceContainsHDIHeadNode']$", oAppliance, oNode)
ElseIf sNodeTypeLowered = "hdidata" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.ApplianceContainsHDIDataNode']$", oAppliance, oNode)
ElseIf sNodeTypeLowered = "hdimanagement" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.ApplianceContainsHDIManagementNode']$", oAppliance, oNode)
ElseIf sNodeTypeLowered = "hdisecure" Then
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.ApplianceContainsHDISecureNode']$", oAppliance, oNode)
End If
End If
Else
Call LogWarning("Node with ID " &amp; nNodeID &amp; " has unknown type '" &amp; sNodeType &amp; "' and will not be discovered.")
End If

Call oRecordSet.MoveNext()
Loop
Call oRecordSet.Close()
End Sub

' Creates new Node of specified type
Sub CreateNode(ByVal sNodeTypeLowered, ByRef oNode, ByRef sRegion)
Dim sPdw, sHdi, sFabric
sPdw = "Pdw"
sHdi = "Hdi"
sFabric = "Fabric"

If sNodeTypeLowered = "control" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ControlNode']$")
sRegion = sPdw
ElseIf sNodeTypeLowered = "compute" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ComputeNode']$")
sRegion = sPdw
ElseIf sNodeTypeLowered = "management" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ManagementNode']$")
sRegion = sPdw
ElseIf sNodeTypeLowered = "host" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNode']$")
sRegion = sFabric
ElseIf sNodeTypeLowered = "hdihead" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.HeadNode']$")
sRegion = sHdi
ElseIf sNodeTypeLowered = "hdidata" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.DataNode']$")
sRegion = sHdi
ElseIf sNodeTypeLowered = "hdimanagement" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.ManagementNode']$")
sRegion = sHdi
ElseIf sNodeTypeLowered = "hdisecure" Then
Set oNode = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.SecureNode']$")
sRegion = sHdi
Else
oNode = Empty
sRegion = Empty
End If
End Sub

' Populates Node's properties
Sub FillNodeProperties(ByRef oNode, ByVal nNodeID, ByVal sNodeType, ByVal sName, ByVal sAddress, ByVal sClusterName, ByVal bIsPassive, ByVal sRegion, ByVal sOwningRegion)
Call oNode.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", ExtractDisplayName(sName))
Call oNode.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", sPrincipalName)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/ApplianceID$", sApplianceID)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/ID$", nNodeID)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/Type$", sNodeType)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/NodeName$", sName)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/Address$", sAddress)
If (Not IsNull(sClusterName)) Then
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/ClusterName$", sClusterName)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/ClusterDisplayName$", ExtractDisplayName(sClusterName))
End If
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/IsPassive$", bIsPassive)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/ApplianceNetworkAddress$", sNetworkAddress)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/ApplianceTdsAddress$", sTdsAddress)
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Node']/Region$", sRegion)

If (LCase(sNodeType) = "host") Then
Call oNode.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HostNode']/OwningRegion$", sOwningRegion)
End If
End Sub

' Ensures cluster existance in collection
' Returns True if new instance was created, False otherwise
Function EnsureCluster(ByRef hClusters, ByVal sClusterType, ByVal sClusterName)
If hClusters.Exists(sClusterName) Then
EnsureCluster = False
Exit Function
End If
Dim oCluster

Set oCluster = oDiscoveryData.CreateClassInstance(sClusterType)
Call oCluster.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", ExtractDisplayName(sClusterName))
Call oCluster.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", sPrincipalName)
Call oCluster.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster']/ApplianceID$", sApplianceID)
Call oCluster.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster']/Name$", sClusterName)
Call oCluster.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster']/ApplianceNetworkAddress$", sNetworkAddress)
Call oCluster.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.NodesCluster']/ApplianceTdsAddress$", sTdsAddress)
Call oDiscoveryData.AddInstance(oCluster)
Call hClusters.Add(sClusterName, oCluster)
EnsureCluster = True
End Function

' Adds containment relationship between specified Source and Target objects
Sub AddContainmentRelationship(ByVal sRealtionshipType, ByRef oSource, ByRef oTarget)
Dim oRelationship
Set oRelationship = oDiscoveryData.CreateRelationshipInstance(sRealtionshipType)
oRelationship.Source = oSource
oRelationship.Target = oTarget
Call oDiscoveryData.AddInstance(oRelationship)
End Sub

' Extracts display name from Node or Cluster name
Function ExtractDisplayName(ByVal sFullName)
ExtractDisplayName = Mid(sFullName, InStr(sFullName, "-") + 1)
End Function

Sub DiscoverComponentGroups(hNodes)
On Error Resume Next

Dim oRecordSet
Set oRecordSet = oSqlQry.ExecuteQueryToRecordSet(GetComponentGroupsQuery, Array())
Call HandleErrorInDiscovery("Cannot run query", oDiscoveryData, oAPI)

Dim oComponentGroup
Dim nNodeID, sNodeName, nGroupID, sGroupName

Do While Not oRecordSet.EOF
nNodeID = oRecordSet(0)
sNodeName = ExtractDisplayName(oRecordSet(1))
nGroupID = RemoveVendorID(oRecordSet(2))
sGroupName = oRecordSet(3)

Call CreateComponentGroup(nGroupID, oComponentGroup)
If Not IsEmpty(oComponentGroup) Then
Call FillComponentGroupProperties(oComponentGroup, nNodeID, nGroupID, sGroupName)
Call oDiscoveryData.AddInstance(oComponentGroup)
Call AddContainmentRelationship("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.NodeContainsComponentGroup']$", hNodes.Item(nNodeID), oComponentGroup)
End If

Call oRecordSet.MoveNext()
Loop
Call oRecordSet.Close()
End Sub

Function RemoveVendorID(ByVal nID)
Dim sID
sID = CStr(nID)
RemoveVendorID = CLng(Right(sID, Len(sID) -3))
End Function

' Creates new Component Group
Sub CreateComponentGroup(ByVal nComponentGroupID, ByRef oComponentGroup)
If nComponentGroupID = 80000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Cluster']$")
ElseIf nComponentGroupID = 60000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Network']$")
ElseIf nComponentGroupID = 20000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.StorageExternal']$")
ElseIf nComponentGroupID = 10000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.StorageInternal']$")
ElseIf nComponentGroupID = 100000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Software']$")
ElseIf nComponentGroupID = 50000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.Cooling']$")
ElseIf nComponentGroupID = 40000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.PowerSupply']$")
ElseIf nComponentGroupID = 30000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ServerProcessing']$")
ElseIf nComponentGroupID = 200000 Then
Set oComponentGroup = oDiscoveryData.CreateClassInstance("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.HDI.HDInsight']$")
Else
oComponentGroup = Empty
End If
End Sub

' Populates ComponentGroup's properties
Sub FillComponentGroupProperties(ByRef oComponentGroup, ByVal nNodeID, ByVal nGroupID, ByVal sGroupName)
Call oComponentGroup.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", sGroupName)
Call oComponentGroup.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", sPrincipalName)
Call oComponentGroup.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ComponentGroup']/ApplianceID$", sApplianceID)
Call oComponentGroup.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ComponentGroup']/NodeID$", nNodeID)
Call oComponentGroup.AddProperty("$MPElement[Name='APSLibrary!Microsoft.SQLServerAppliance.APS.ComponentGroup']/ID$", nGroupID)
End Sub
</Script></ScriptBody>
<SecureInput>$RunAs[Name="APSLibrary!Microsoft.SQLServerAppliance.APS.ActionProfile"]/UserName$@@secureseparator@@$RunAs[Name="APSLibrary!Microsoft.SQLServerAppliance.APS.ActionProfile"]/Password$</SecureInput>
<TimeoutSeconds>300</TimeoutSeconds>
</DataSource>
</Discovery>