Forum Discussion

Paul_Carroll's avatar
7 years ago

Add device to Collector with least devices in rest api

I hope someone here can help,

Using powershell and the rest API I would like to do a lookup of selected collectors find which has the least amount of devices on it then add the a new device .

I have the script for adding the device and that works great just need the collector lookup, has anyone done something similar.

Thank you in advanced for all help provided

Paul

  • Here is what I use to "report" on collectors and their versions.

     

    $status = $null
    $body = $null
    $response = $null
    
    <# Use TLS 1.2 #>
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
    <# account info #>
    $accessId = ''
    $accessKey = ''
    $company = 'SITE'
    
    <# request details #>
    $httpVerb = 'GET'
    $resourcePath = '/setting/collectors'
    $queryParams = '?size=1000&fields=id,description,hostname,platform,collectorGroupName,collectorSize,numberOfHosts,build'
    #$queryParams = ''
    $status = $null
    $body = $null
    $response = $null
    $data = ''
    
    <# Construct URL #>
    $url = 'https://' + $company + '.logicmonitor.com/santaba/rest' + $resourcePath + $queryParams
    
    <# 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')
    
    <# Make Request #>
    $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Header $headers 
    
    <# Print status and body of response #>
    $status = $response.status
    #$body = $response.data| ConvertTo-Json -Depth 5
    
    $body = $response.data | ConvertTo-Json -Depth 2
    $Collectors = $body | ConvertFrom-Json
    
    #$Collectors.items | Sort collectorGroupName | format-table -AutoSize
    $Collectors.items | Sort collectorGroupName | Out-Gridview
    #$body

     

    The numberOfHosts would help you out in this case.

  • !!! Successful first run against our environment!  The balance in the group I tested against went from (devices:instances) 39:7112 / 175:6803 to 107:6880 / 107:7036

    Doesn't seem to be any uptick in alerts following the transition.  CPU spike on one of the collectors... but I have reconfigured one of them in the group to fail faster than the other (as a remediation for network socket resource exhaustion on the collector causing a backlog of tasks).  I'll need to transfer those settings across to the other one (more threads, shorter timeouts in key areas that were getting bogged down) to get a real read on the changes.  So far, so good.

  • Found a miss... it shifts the collector VMs as well... I have to catch for that yet.

  • OK... here's the fix for that miss:

        # foreach member
        $deviceCollection = $()
    
        foreach ( $member in ( $devices | ? preferredCollectorGroupName -eq $group ) ) {
    
            # Exclude Collectors
            if ( $member.customProperties.value -notcontains "collector" ) {
                $instances    = 0
            
                # # foreach devicedatasource
                $resourcePath = "/device/devices/$($member.id)/devicedatasources"
    
                $deviceDSs    = Get-LMRestAPIObjectListing `
                    -resourcePathRoot $resourcePath        `
                    -accessKey        $accessKey           `
                    -accessId         $accessID            `
                    -URLBase          $URLBase
    
                # count instances
                foreach ( $DS in $deviceDSs ) {
    
                    $instances      += $DS.instanceNumber
                }
    
                $deviceCollection   += @(@{
                    'deviceID'       = $member.id
                    'instances'      = $instances
                    'collectorgroup' = $group
                })
            }
        }
    
        $members[$group] += $deviceCollection

    This replaces the same chunk of code in the previous post and includes an if statement to exclude any device with a custom property "collector"

  • here's the updated calculate-threshold and call that uses the settings from the config (which did work for me, so the <##> can be removed from that portion of the code):

    function          Calculate-Threshold        {
        param (
            $collectorCount  = 2,
            $collectorMemory = 4,
            $InstanceCount
        )
        # Effective Threshold Calculation:
        # (Target_Collector_Mem/Medium_Mem)^1/2 * Medium_Threshold
        # Assumes all collectors are configured the same size
    
        $mediumMemory        = 2
    
        # Calculate the threshold to enter to achieve the correct "effective threshold"
        $effectiveThreshold  = [int](( $InstanceCount/$collectorCount ) / ( [math]::pow(($collectorMemory / $mediumMemory),.5) ))
    
        # output - I'm rounding the result up to the nearest *50 or *00 to allow for a bit of wiggle room
    
        $lastDigits = [int]$(-join ($effectiveThreshold.ToString()[-2..-1]))
    
        if ( $lastDigits -gt 50 ) {
            $addend = 100 - $lastDigits
        } else {
            $addend = 50  - $lastDigits
        }
    
        write-output ([int]$effectiveThreshold + [int]$addend)
    }
        # Set the threshold for the ABCG based on number of instances and the size of the collectors
    
        $collectorSize     = (($groupCollectors[$currentCollector].wrapperConf -split "`n" | sls wrapper.java.maxmemory) -split "=" )[-1] / 1024
    
        $threshold         = Calculate-Threshold `
            -InstanceCount   $groupInstances     `
            -collectorCount  $collectorCount     `
            -collectorMemory $collectorSize
    
        write-output $threshold #!!! remove me before deploying
    
        $resourcePath      = "/setting/collector/groups/$collectorGroupID"
        $data              = "{`"autoBalanceInstanceCountThreshold`":`"$threshold`"}"
        $url               = $URLBase + $resourcePath
    
        $properties        = Send-Request   `
            -accessKey        $accessKey    `
            -accessId         $accessID     `
            -data             $data         `
            -URL              $URL          `
            -httpVerb         "PATCH"

     

  • I've found an issue that I'm working with LM to address.  If the device is an auto-discovered Azure resource, some of the custom properties applied during that process seem to not allow the REST API to alter the device at all.  So if you have azure based resources in a collector group, they won't move.  The specific property names mentioned in the error message start with "predef." ( predef.externalResourceID, predef.externalResourceType ).

    Invoke-RestMethod : {"errorMessage":"custom property name cannot be predef.externalResourceID\ncustom property 
    name cannot be predef.externalResourceType\n","errorCode":1404,"errorDetail":null}
    At C:\Scripts\Balance-LMABCG.ps1:49 char:24
    +     $response        = Invoke-RestMethod `
    +                        ~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod] 
       , WebException
        + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodComm 
       and

    So currently, my code won't work for azure resources.  Works like a champ for us on Hyper-V hosted VMs and physicals.

  • No updates from LM on this last point.  It's the last piece I've got on this.  I'm going to put it in place anyway as it works on the other ABCGs in our environment and doesn't do anything to the other when it fails due to the azure resources being REST API incompatible due to naming convention issues with their properties.  I suspect it's just a miss and will magically start working at some point. (Again, this is where I point out that we're not liable if you implement this and it doesn't work in your environment).

    customProperties               : {@{name=predef.externalResourceID; ...

     

    hopefully they get this sorted out soon.

  • Hi Cole,

    predef.* attributes are used by our Topology Service and can't be updated through the API, only through LogicModules.  They aren't specific to Azure as they may be applied to standard devices as wel

    To update a device with Properties and not affect others you'll want to update the device using PATCH command documented here: https://www.logicmonitor.com/support/rest-api-developers-guide/v1/devices/update-a-device/#PATCH.  Pay particular attention to the customProperties opType option.
    Note: I haven't looked through the code.  I'm just offering this based on the last two posts.

    ~Forrest

  • Thank you Forrest.  As always, a font of great information.  I assume from the reading that the default is "refresh"

    From the link above (to save folks some clicking):

    "opType=replace indicates that the properties included in the request payload will be added if they don't already exist, or updated if they do already exist, but all other existing properties will remain the same"
    

    I'll be putting this in place to day and I'll report back... hopefully with a new $data block for the script and calling it done!

  • No such luck... could you verify that this is the correct URL structure for this @Forrest Evans - LM?

    https://companyname.logicmonitor.com/santaba/rest/device/devices/9999?opType=replace

    Even being really pedantic about it still isn't taking it:

    https://companyname.logicmonitor.com/santaba/rest/device/devices/2469?opType=replace&patchfields=autoBalancedCollectorGroupId,preferredCollectorId

    same error:

    Invoke-RestMethod : {"errorMessage":"custom property name cannot be predef.externalResourceID\ncustom property name cannot be predef.externalResourceType\n","errorCode":1404,"errorDetail":null}
    At C:\Scripts\Balance-LMABCG.ps1:51 char:24
    +     $response        = Invoke-RestMethod `
    +                        ~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
        + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand