Forum Discussion

mnagel's avatar
mnagel
Icon for Professor rankProfessor
8 years ago

Collapse Clustered Instances at Group Level

It has recently become clear to me that when you have multiple devices with the same instances (due to clustering or otherwise shared resources), there is no way to collapse these into a single alerting instance without a lot of manual and maintenance-intensive effort.  The most recent example I have run into relates to vSphere with shared datastores (i.e., any implementation with vCenter, vMotion, etc.).  In those cases, LM fails to distinguish the datastores presented at the cluster level from host-local datastores, and all are presented in parallel.  With a vCenter and 8 hosts, this generates 9 alerts per clustered DS.  I have been told by support I am out of luck on this for now, which is unfortunate, but I had an idea that could be used for this case along with other similar cases.  Sadly, the name "Cluster Alerts" has already been taken, but I would call this idea that as well, just with a different methodology.  So either an extension to Cluster Alerts or some new name TBD.  The idea is to collapse specific instances within a group into a single instance at the group level.  This way we can pick which instances are clustered (still manual, but manageable) and ensure only a single alert and view of those instances.  Ideally, LM could be smart enough to identify the clustered elements and do this automatically, but this method would apply to any similar situation even if the API does not reveal the cluster binding details like ESX does.  Please consider adding this enhancement soon!

Thanks,
Mark

11 Replies

Replies have been turned off for this discussion
  • Previous had an issue with the properties it was adding.  If you're creating a new property from a proertySource script, it adds it as an "auto.*" property, which goes away as soon as the script stops processing.  To add a new permanent custom property, you have to use the REST API, not just a "category.name=data" output from the script.  Here's the final: 

    # These first two lines will need to change to fit your environment.
    # The groupParentID is the id of a group to house the dynamic groups that will be created...
    # if that's you root level, use that ID.
    # We're using a group named "Failover Clusters" in our heirarchy to house them.
    
    #######
    # Cole McDonald - Sr. Technical Analyst
    # cole.mcdonald@beyondimpactllc.com
    # Beyond Impact 2.0, llc
    # No warranty provided for this code, use at your own risk
    #######
    
    $company            = "Your_Company_Name"
    $groupParentID      = "566"
    
    $URLRoot            = "https://$company.logicmonitor.com/santaba/rest"
    
    $server             = "##system.displayname##"
    $accessID           = "##LogicMonitor.accessId.key##"
    $accessKey          = "##LogicMonitor.accessKey.key##"
    
    function Send-Request {
        param (
            $cred,
            $accessid   = $null,
            $accesskey  = $null,
            $URL               ,
            $data       = $null,
            $version    = '2'  ,
            $httpVerb   = "GET"
        )
    
        if ( $accessId -eq $null) {
            $accessId   = $cred.UserName
            $accessKey  = $cred.GetNetworkCredential().Password
        }
    
        # Use TLS 1.2
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
        # Get current time in milliseconds
        $epoch          = [Math]::Round(
            ( New-TimeSpan `
                -start (Get-Date -Date "1/1/1970") `
                -end (Get-Date).ToUniversalTime()).TotalMilliseconds
            )
    
        # Concatenate Request Details
        $requestVars    = $httpVerb + $epoch + $data + $resourcePath
    
        # Construct Signature
        $hmac           = New-Object System.Security.Cryptography.HMACSHA256
        $hmac.Key       = [Text.Encoding]::UTF8.GetBytes( $accessKey )
        $signatureBytes = $hmac.ComputeHash( [Text.Encoding]::UTF8.GetBytes( $requestVars ) )
        $signatureHex   = [System.BitConverter]::ToString( $signatureBytes ) -replace '-'
        $signature      = [System.Convert]::ToBase64String( [System.Text.Encoding]::UTF8.GetBytes( $signatureHex.ToLower() ) )
    
        # Construct Headers
        $auth           = 'LMv1 ' + $accessId + ':' + $signature + ':' + $epoch
        $headers        = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
        $headers.Add(     "Authorization", $auth              )
        $headers.Add(     "Content-Type" , 'application/json' )
    
        # uses version 2 of the API
        $headers.Add(     "X-version"    , $version           )
    
        # Make Request
        $response       = Invoke-RestMethod `
            -Uri           $URL             `
            -Method        $httpVerb        `
            -Body          $data            `
            -Header        $headers
    
        $result         = $response
    
        Return $result
    }
    
    if ( test-path "\\$server\C`$\Windows\Cluster\CLUSDB" ) {
        
        "system.categories=ClusterMember"   
        
        $clusterInfo    = invoke-command `
            -ComputerName $server        `
            -scriptBlock  {
                Import-Module failoverclusters
                $cluster = get-cluster
                "$($cluster.name):$($cluster.id)"
            }
    
        $clustername    = ($clusterinfo -split ':')[0]
        $clusterid      = ($clusterinfo -split ':')[1]
    
        $groupName      = "Failover Cluster - $clustername"
    
        # Read Groups
        # Construct URL
        $resourcePath   = "/device/groups"
        $url            = $URLRoot + $resourcePath
    
        # Make Request
        $response       = Send-Request  `
            -accessid     $accessID     `
            -accesskey    $accessKey    `
            -URL          $url         
            
        $group   = $response.items | ? name -eq $groupName
        
        if ( ($group | measure-object).count -gt 0 ) {
    
            # "*** Group Already exists.  Need Device properties? ***"
            try {
                $resource = "##Failover.Cluster.GUID##"
            } catch {
                $resource = $null
            }
    
            if ( $resource -ne $clusterid ) {
                # Add Properties
                # Construct URL
                $resourcePath   = "/device/devices/##system.deviceid##/properties/"
                $url            = $URLRoot + $resourcePath
    
                # Construct Data Body
                $data = `
    @"
        {
            `"type`"         : `"custom`"                ,
            `"name`"         : `"Failover.Cluster.GUID`" ,
            `"value`"        : `"$ClusterID`"
        }
    "@
                $response       = Send-Request  `
                    -accesskey    $accessKey    `
                    -accessid     $accessId     `
                    -URL          $url          `
                    -data         $data         `
                    -httpVerb     "POST"
            }
        } else {
            # "*** create group & tag resource ***"
    
            # Construct URL
            $resourcePath = "/device/groups"
            $url          = $URLRoot + $resourcePath
    
            # Construct Data Body
            $data = `
    @"
    {
        `"name`"             : `"$groupName`"                                ,
        `"parentId`"         : `"$groupParentID`"                            ,
        `"disableAlerting`"  : `"true`"                                      ,
        `"enableNetflow`"    : `"false`"                                     ,
        `"appliesTo`"        : `"Failover.Cluster.GUID == \`"$ClusterID\`"`" ,
        `"customProperties`" : [{
            `"name`"         : `"Failover.Cluster.ParentGUID`"               ,
            `"value`"        : `"$ClusterID`"
        }]
    }
    "@
    
            try {
                $response       = Send-Request  `
                    -accesskey    $accessKey    `
                    -accessid     $accessId     `
                    -URL          $url          `
                    -data         $data         `
                    -httpVerb     "POST"
                
    
                # Add Properties
                # Construct URL
                $resourcePath   = "/device/devices/##system.deviceid##/properties/Failover.Cluster.GUID"
                $url            = $URLRoot + $resourcePath
    
                # Construct Data Body
                $data = `
    @"
        {
                `"type`"         : `"custom`"                ,
                `"name`"         : `"Failover.Cluster.GUID`" ,
                `"value`"        : `"$ClusterID`"
        }
    "@
                $response       = Send-Request  `
                    -accesskey    $accessKey    `
                    -accessid     $accessId     `
                    -URL          $url          `
                    -data         $data         `
                    -httpVerb     "PUT"
            } catch {
                $error[0] | out-file $logPath -append
            }
        }
    }