• 0
Paul Carroll

Add device to Collector with least devices in rest api

Question

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

Share this post


Link to post
Share on other sites

Recommended Posts

  • 0
1 hour ago, Cole McDonald said:

We use User Profile Disks in a couple of our environments.  When a user logs in, it creates a symlink to a mounted VHD volume.  As this reads as the system adding a device, it registers as an instance under the volume dataSources.  When 2000 users log into a load balanced RDP Terminal Server set, that's a ton of instances that come online all at once.  One of the drawbacks is that the symlink doesn't disappear when the user logs out.  So every user's symlink is potentially on every server in the LBSet.  So 2K users across 10 Terminal servers = 20K potential devices that LM can't differentiate between a live link and a dead link.  So it ties up the resources for 18K instances while they wait to time out.  It also seems as though (when looking at !tlist c-wmi h=<IP for 1 TS>) that it's holding onto them even after it's timed out... so the socket pool never empties on the collector and a ping query with no sockets becomes a "host down" alert in LM for each host that isn't able to respond to the ping that is never happening.

Wow, that is an interesting situation. If I understand the situation, LM sees that your Terminal server as suddenly gaining some 2000 new volumes in-and-out that it wants to monitor. It then sends out 2000 WMI requests to query each disk for free space and the like. And with 10 servers, that comes out to 20,000 individual remote WMI calls every 3 minutes or so? I can see that killing a collector.

If I understand that correctly, perhaps it's best to instead write a custom replacement Volume DataSource to either ignore all these symlink volumes, if you don't need to monitor them, or have it use Batch Script to convert 2000 WMI calls into just one or two calls that collects all the volume details all at once.

Share this post


Link to post
Share on other sites
  • 0

http://woshub.com/user-profile-disks-in-windows-server-2012-r2-rds/

I'm trying to have it pass them by as they all have a very discernable and easily regex-able naming convention.  I have to figure out how to address them in the filters in the LM interface... now that I see the REST docs, I assume that the fieldnames are going to be the same in the interface as in the REST object... I may try again (unsuccessful last time - Drinking from the firehose is fun).

Share this post


Link to post
Share on other sites
  • 0
1 hour ago, Cole McDonald said:

I'm trying to have it pass them by as they all have a very discernable and easily regex-able naming convention.  I have to figure out how to address them in the filters in the LM interface... now that I see the REST docs, I assume that the fieldnames are going to be the same in the interface as in the REST object... I may try again (unsuccessful last time - Drinking from the firehose is fun).

 

Try looking at https://www.logicmonitor.com/support/datasources/active-discovery/datasource-discovery-filters. If I understand the issue correctly, too many DataSource Instances and you want to ignore some, you can fix that without any REST API work and making changes within the system instead.

  • Upvote 1

Share this post


Link to post
Share on other sites
  • 0
20 hours ago, Mike Moniz said:

 

Try looking at https://www.logicmonitor.com/support/datasources/active-discovery/datasource-discovery-filters. If I understand the issue correctly, too many DataSource Instances and you want to ignore some, you can fix that without any REST API work and making changes within the system instead.

That's where I was trying to do it.  It's just not cooperating... so I'll have to play with the Regex a bit to get it to cooperate, assuming the property name "name" I'm referencing is correct.

Share this post


Link to post
Share on other sites
  • 0

Hi All,

 

I'm trying to create a new device with auto balance group with the below code. But I'm getting response error message "Collector (id=0) does not exist" status 1404. Not sure what went wrong... please advise what need to be done if you've come across this before.

Thank you in advance    

        $httpVerb = 'POST'
        $resourcePath = '/device/devices'
        $data = "{`"name`":`"10.10.10.10`",`"displayName`":`"test1`",`"autoBalancedCollectorGroupId`":2,`"preferredCollectorGroupName`":`"group1`",`"hostGroupIds`":`"316`",`"preferredCollectorId`":0}"
        <# Construct URL #>
        $url = 'https://' + $company + '.logicmonitor.com/santaba/rest' + $resourcePath

        <# 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 Post -Header $headers -Body $data


   

Share this post


Link to post
Share on other sites
  • 0

In my tests I had to wrap the preferredCollectorId value of zero in double quotes:

{
  "name": "10.1.1.22",
  "displayName": "My Device",
  "autoBalancedCollectorGroupId": 4,
  "preferredCollectorId": "0"
}
 
I acknowledge it's not really consistent with the other ID value for the Collector Group, but give it a try and let me know.
 
~Forrest

Share this post


Link to post
Share on other sites
  • 0

I'm nearly done with my re-balancing 'Source.  I've got it gathering all of the data and it's ready to start reassigning resources to different collectors.  I have one collector that is showing up in the collector's interface, but doesn't come up in the return from the REST API call for /settings/collectors/collector.  So the code won't consider that as a place to send devices.

Due to imbalanced instance counts per type of device in our environments, I'm ending up with all of the same type of device auto balancing to a single collector rather than evenly spread across both.  It's an interesting sorting behavior caused by the balancing algorithm over time.  As a result, when a batch script fires, it opens TCP ports for each of the instances being collected... and does so on a single collector in the group due to the strange sorting behavior (they only consider instance count).  More resource exhaustion.  I've ended up doubling threads and halving timeouts on script/batchscript/powershell/WMI on most of my collectors to get them to fail faster to keep the systems from log jamming.  The collectors are all "Large."

Hopefully, I'll have this done soon and can post it here to help out others encountering problems with the autobalance rebalancing algorithms.  I've also got it gathering the number of instances and using the threshold calculator I wrote (posted around here somewhere) to set the threshold at the end of the re-balance to make sure the balance is set correctly for the collector sizes and the number of instances.

@Forrest Evans - LM, can collectors of different sizes be added into an ABCG?  I'd have to figure out how to account for that in the threshold calculations.  I'm also working on building it out for groups with > 2 collectors to make sure it's handing them out to all of them equally.

Share this post


Link to post
Share on other sites
  • 0

Yes.  ABCG can properly address collectors of different sizes.  The JVM memory value is used to determine how many instances should be assigned to each collector.  The configured threshold is used for Medium collectors and the value is scaled up/down.

Share this post


Link to post
Share on other sites
  • 0
3 hours ago, Forrest Evans - LM said:

The JVM memory value is used to determine how many instances should be assigned to each collector.

Thank you for that.

If I have a medium and a large in the same group, I'd need to determine threshold for each, then add the results or would a min/max/avg be a better option?

Here's the calculator I'm using currently.  It works amazingly well for groups with the same size collectors in them: 

 

Share this post


Link to post
Share on other sites
  • 0

Initial testing results of my algorithm looks good.  Once I've added the payload to change the preferred collector on the devices, I'll post the code.  Device listing are "Id InstanceCount" so you can see how they are sorting:

ABCG 1 : Instance Balance - 640/388
	624 80
	620 126
	626 152
	625 155
	627 156
	628 359

ABCG 2 : Instance Balance - 4747/5006
	2433 5
	2314 8
	2257 27
	2258 27
	2421 27
	2249 28
	2256 28
	2283 28
	2254 29
	2269 29
	2280 29
	2315 29
	2427 29
	2252 30
	2272 30
	2310 30
	2317 30
	2419 30
	2423 30
	2251 31
	2270 31
	2285 31
	2299 31
	2420 31
	2425 31
	2428 31
	2432 31
	2242 32
	2275 32
	2278 32
	2294 32
	2295 32
	2296 32
	2297 32
	2303 32
	2422 32
	2426 32
	2429 32
	2430 32
	2431 32
	2250 33
	2301 33
	2424 33
	2476 33
	2240 35
	2241 35
	2255 36
	2298 36
	2233 37
	2237 37
	2286 38
	2289 38
	2306 38
	2309 38
	2236 39
	2268 39
	2287 39
	2288 39
	2304 39
	2307 39
	2316 39
	2276 40
	2281 40
	2318 40
	2246 41
	2279 42
	2248 43
	2274 43
	2235 44
	2259 44
	2273 44
	2238 45
	2271 45
	2312 45
	2232 46
	2244 47
	2313 49
	2253 50
	2266 50
	2302 51
	2245 56
	2308 56
	2267 59
	2260 60
	2261 60
	2418 62
	2231 70
	2239 70
	2305 70
	2264 71
	2282 71
	2292 71
	2300 73
	2263 74
	2262 77
	2293 77
	2311 77
	2265 78
	2277 79
	2291 80
	614 92
	2247 104
	607 109
	2234 109
	632 122
	608 124
	599 125
	598 127
	601 132
	2194 132
	602 133
	2193 133
	2191 135
	2195 139
	613 142
	611 154
	615 154
	603 155
	616 155
	610 423
	609 435
	605 466
	612 572
	604 581
	639 620

ABCG 3 : Instance Balance - 124/123
	619 123
	618 124

ABCG 4 : Instance Balance - 4909/4786
	1950 6
	1976 6
	1981 6
	1983 6
	1960 7
	1964 7
	1965 7
	1966 7
	1972 7
	1973 7
	1974 7
	1975 7
	1984 7
	1985 7
	2013 7
	2014 7
	2080 7
	2081 7
	2082 7
	2083 7
	2084 7
	2393 7
	2513 7
	1947 8
	1948 8
	1949 8
	1951 8
	1952 8
	1953 8
	1954 8
	1959 8
	1971 8
	1980 8
	1982 8
	2010 8
	2469 8
	2470 8
	2471 8
	1956 9
	1961 9
	1962 9
	1963 9
	1967 9
	1968 9
	2394 9
	2508 10
	2498 81
	2480 82
	1999 85
	2000 85
	2071 85
	2072 86
	2073 86
	2037 87
	1992 88
	2012 88
	2029 88
	2015 89
	2018 89
	2020 89
	2070 90
	2044 91
	2045 91
	2001 92
	2002 92
	2040 92
	2074 92
	2031 94
	2046 95
	2047 95
	2030 96
	1997 97
	2032 97
	2075 97
	1989 98
	2003 98
	2011 98
	2039 98
	1991 100
	2068 100
	1988 101
	2017 102
	2019 102
	2026 102
	2004 105
	2042 105
	1986 106
	2024 106
	2025 106
	2022 107
	2023 107
	1987 109
	2038 113
	1998 116
	2008 118
	2036 119
	1996 121
	1990 127
	2043 130
	2057 145
	2050 158
	2056 158
	2051 160
	2055 169
	2052 174
	2138 175
	2053 176
	2065 176
	2054 182
	600 183
	617 183
	2049 184
	2064 193
	2131 221
	2132 223
	2061 230
	2060 239
	2059 254
	2006 264
	2005 285

ABCG 5 : instance Balance - 0/121
	629 121

ABCG 6 : Instance Balance - 7574/7777
	2381 3
	2382 3
	2484 3
	2345 4
	2346 4
	2347 4
	2490 4
	2111 5
	2211 5
	2212 5
	2217 5
	2229 5
	2320 5
	2327 5
	2389 5
	2448 5
	2455 5
	2486 5
	2493 5
	2210 6
	2220 6
	2221 6
	2336 6
	2338 6
	2360 6
	2488 6
	2489 6
	2491 6
	2495 6
	2496 6
	2214 7
	2219 7
	2222 7
	2226 7
	2353 7
	2354 7
	2355 7
	2357 7
	2358 7
	2359 7
	2440 7
	2483 7
	2485 7
	2487 15
	2515 20
	2494 22
	2096 23
	2103 24
	2374 24
	2514 24
	2341 25
	2365 25
	2367 25
	2462 25
	2507 25
	2344 26
	2371 26
	2492 26
	2118 27
	2094 28
	2113 28
	2363 28
	2377 28
	2457 28
	2459 28
	2460 28
	2461 28
	2093 29
	2095 29
	2333 29
	2361 29
	2362 29
	2506 29
	2120 30
	2122 30
	2340 30
	2334 31
	2408 31
	2331 32
	2369 32
	2370 32
	2372 32
	2396 32
	2453 32
	2401 33
	2409 33
	2337 34
	2099 35
	2100 36
	2368 36
	2400 36
	2101 37
	2105 37
	2329 37
	2335 37
	2417 37
	2104 38
	2323 38
	2410 38
	2504 38
	2366 39
	2330 40
	2332 40
	2379 40
	2398 40
	2406 40
	2378 41
	2404 41
	2102 42
	2107 42
	2412 42
	2464 42
	2097 43
	2326 43
	2376 43
	2397 43
	2415 43
	2324 45
	2373 45
	2452 45
	2375 46
	2112 47
	2322 48
	2450 50
	2499 51
	2121 53
	2124 53
	2458 53
	2463 54
	2465 54
	2466 54
	2402 55
	2364 56
	2109 59
	2115 59
	2206 59
	2116 62
	2117 62
	2405 63
	2114 65
	2407 65
	2203 69
	2108 70
	2350 71
	2209 73
	2110 74
	2395 74
	2207 75
	2106 76
	2348 79
	2505 80
	2343 81
	2383 82
	2446 82
	2403 83
	2479 84
	2328 85
	2399 87
	2456 87
	2443 88
	2342 90
	2468 90
	2467 92
	2414 94
	2454 94
	2356 102
	2119 105
	2215 115
	2352 115
	2223 124
	2225 124
	596 128
	2445 130
	2481 131
	597 132
	2478 133
	2477 134
	2416 135
	2441 135
	2442 135
	2444 136
	2092 138
	2321 141
	2230 142
	2349 145
	2351 145
	2380 151
	2213 152
	2216 152
	2218 152
	2224 152
	2391 162
	2516 162
	2198 167
	2449 189
	2201 191
	2518 191
	2228 193
	2388 193
	2390 202
	2196 211
	2197 252
	2447 259
	2204 265
	2517 296
	2451 306
	2208 307
	2205 334
	2199 335
	2202 343
	2200 358
	2227 492
	2339 546

 

Share this post


Link to post
Share on other sites
  • 0

@Forrest Evans - LM I've got all of the pieces in place... except for the precollectorid change... I'm still stuck on that a bit.  Let's assume I'm looking to move all of the devices in an ABCG into arbitrary collectors, then allow the ABCG to adjust as necessary once everything has been set.  From a logic standpoint should that be a 2 step process?

  • loop through devices
    • set auto collector id to 0
    • set pref collector id to $thisCollector
  • loop through devices
    • set auto collector id to ABCG id
  • Set threshold to account for current calculated threshold

Since I'm looking to make this as portable as possible, I have to assume that the memory sizes of the collectors are different.  In terms of calculating that threshold, let's assume I have a 2Gb and a 4Gb  collector in a group.  If the group overall has 3000 incidents we want A to handle half as much as the other... so how does that apply to that threshold?  If we set it for the # for A, it'll never trigger for B ... if we set it for B, it'll never trigger on A which will be constantly overwhelmed.  I would assume we have to set it to the smallest possible threshold and hope that the other/s don't end up balanced wrong and choke out?  If we add a C at 4 Gb, what if B and C lose balance?  If we set it for A, will they constantly try to balance?  So in these cases, the calculator I've got does no good as it assumes evenly provisioned collectors in the group and we should set it to:

(instances / total ram) * lowest ram setting? (rounded up a touch to the nearest 50 to account for any slight disparities in instances/device counts)

All that said, I'm initially implementing with the calculator as it is, since our collectors are evenly provisioned within each group.

Share this post


Link to post
Share on other sites
  • 0

Last piece of the puzzle... I need to set the autobalance threshold of the ABCG. Assuming that everything else works codewise (it's the same function everything else goes through successfully)... what am I missing here?

# This is the same path as retrieving all of the groups.. with the ID tacked on to the end.
$resourcePath = "/setting/collector/groups/4"

# This is the property name returned from the grab... it doesn't seem to be in SwaggerUI (2.0.0) yet.
$data         = "{`"autoBalanceInstanceCountThreshold`":`"5050`"}"

 

Share this post


Link to post
Share on other sites
  • 0

Just to show where we're at right now...  The only part not working is the threshold PATCH at the end of the group loop (same disclaimer as always; neither I nor Beyond Impact 2.0, LLC warranty this software.  It's aggressive, use with caution):

Import-Module     CredentialManager

function          Send-Request               {
    param (
        $cred               ,
        $URL                ,
        $accessid    = $null,
        $accesskey   = $null,
        $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

    Return $response

}
function          Get-LMRestAPIObjectListing {
    param (
        $URLBase          ,
        $resourcePathRoot , # "/device/devices"
        $size = 1000      ,
        $accessKey        ,
        $accessId
    )

    $output  = @()
    $looping = $true
    $counter = 0
    
    # write-host "Gathering data from $resourcePathRoot"

    while ($looping) {
        #re-calc offset based on iteration
        $offset = $counter * $size

        $resourcePath    = $resourcePathRoot
        $queryParam      = "?size=$size&offset=$offset"
        $url             = $URLBase + $resourcePath + $queryParam

        # Make Request
        $response        = Send-Request `
            -accesskey    $accessKey    `
            -accessid     $accessId     `
            -URL          $url

        if ( $response.items.count -eq $size ) {
            # Return set is full, more items to retrieve
            $output     += $response.items
            $counter++

        } elseif ( $response.items.count -gt 0 ) {
            # Return set is not full, store date, end loop
            $output     += $response.items
            $looping     = $false

        } else {
            # Return set is empty, no data to store, end loop
            $looping     = $false

        }
    }

    write-output $output
}
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        = 4

    # 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)
}

$company        = "YourCompanyNameHere"
$URLBase        = "https://$company.logicmonitor.com/santaba/rest"

# This will resolve to proper values if it's being run from inside LM
$accessID       = "##Logicmonitor.AccessID.key##"
$accessKey      = "##Logicmonitor.AccessKey.key##"

if ( $accessID -like "##*" )       {
    # Not being run from inside LM - populate manually for testing
    Import-Module     CredentialManager

    $Cred       = Get-StoredCredential -Target LogicMonitor
    $accessID   = $cred.UserName
    $accessKey  = $Cred.GetNetworkCredential().Password
}

#region Get Groups
$resourcePath        = "/setting/collector/groups"

$collectorGroups     = Get-LMRestAPIObjectListing `
    -resourcePathRoot $resourcePath               `
    -accessKey        $accessKey                  `
    -accessId         $accessID                   `
    -URLBase          $URLBase

#endregion
#region Get collectors
$resourcePath   = "/setting/collector/collectors"

$collectors     = Get-LMRestAPIObjectListing `
    -resourcePathRoot $resourcePath          `
    -accessKey        $accessKey             `
    -accessId         $accessID              `
    -URLBase          $URLBase

#endregion
#region Get Devices
$resourcePath   = "/device/devices"

$devices        = Get-LMRestAPIObjectListing `
    -resourcePathRoot $resourcePath          `
    -accessKey        $accessKey             `
    -accessId         $accessID              `
    -URLBase          $URLBase
    
#endregion

# Get list of Resources

$members         = @{}

foreach ( $group in ( $collectors.collectorgroupname | sort -unique ) ) {
    
    write-host $group

    # Find Collectors
    $groupCollectors  = $collectors | ? collectorGroupName -eq $group
    $collectorCount   = $groupCollectors.count
    $collectorGroupID = ($collectorGroups | ? name -like "*$group*").id
    # foreach member
    $deviceCollection = $()

    foreach ( $member in ( $devices | ? preferredCollectorGroupName -eq $group ) ) {
        $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
        })

        # write-host "$group - $($member.id) - $instances"
    }

    $members[$group] += $deviceCollection

    # Sort by Instance count

    $flipFlop         = $false
    $currentCollector = 0
    $groupInstances   = 0

    $members[$group].instances `
    | sort -Unique `
    | foreach { $members[$group] | ? instances -eq $_ } `
    | foreach {
        
        $collectorID = $groupCollectors[$currentCollector].id
        # Set the preferred ABCG to 0 and
        # Move every other to the next Collector in the group

        # write-host "`t$($_.deviceID)`t$($_.instances)`t$collectorID" -ForegroundColor White

        $resourcePath = "/device/devices/$($_.deviceID)"

        $data         = @"
        {
            `"autoBalancedCollectorGroupId`": `"0`",
            `"preferredCollectorId`"        : `"$collectorID`"
        }
"@
        $url          = $URLBase + $resourcePath

        $properties        = Send-Request   `
            -accessKey        $accessKey    `
            -accessId         $accessID     `
            -data             $data         `
            -URL              $URL          `
            -httpVerb         "PATCH"

        $groupInstances += $_.instances

        $nextCollector = $currentCollector + 1

        if ( ($collectorCount -gt 1) -and ($nextCollector -lt $collectorCount) ) {
            $currentCollector = $nextCollector
        } else {
            $currentCollector = 0
        }
    }

    # Set the preferred ABCG back to the original Setting
    $members[$group].instances `
    | sort -Unique `
    | foreach { $members[$group] | ? instances -eq $_ } `
    | foreach {
        $resourcePath = "/device/devices/$($_.deviceID)"

        $data         = @"
        {
            `"autoBalancedCollectorGroupId`": `"$collectorGroupID`"
        }
"@
        $url          = $URLBase + $resourcePath

        $properties   = Send-Request `
            -accessKey  $accessKey   `
            -accessId   $accessID    `
            -data       $data        `
            -URL        $URL         `
            -httpVerb   "PATCH"
    }

    # Set the threshold for the ABCG based on number of instances and the size of the collectors
	<#
    $threshold = Calculate-Threshold     `
        -InstanceCount   $groupInstances `
        -collectorCount  $collectorCount `
        -collectorMemory 8

    $resourcePath = "/setting/collector/groups/$collectorGroupID"

    $data         = "{`"autoBalanceInstanceCountThreshold`":`"$threshold`"}"
    $url          = $URLBase + $resourcePath

    $properties        = Send-Request   `
        -accessKey        $accessKey    `
        -accessId         $accessID     `
        -data             $data         `
        -URL              $URL          `
        -httpVerb         "PATCH"
    #>
}

 

Edited by Cole McDonald

Share this post


Link to post
Share on other sites
  • 0

!!! 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.

Share this post


Link to post
Share on other sites
  • 0

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"

Share this post


Link to post
Share on other sites
  • 0

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"

 

Share this post


Link to post
Share on other sites
  • 0

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.

Share this post


Link to post
Share on other sites
  • 0

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.

Share this post


Link to post
Share on other sites
  • 0

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

Share this post


Link to post
Share on other sites
  • 0

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!

Share this post


Link to post
Share on other sites
  • 0

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

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.