Server-side NIC selection for the collector
This commit is contained in:
@@ -31,10 +31,11 @@
|
||||
#
|
||||
# WHY ONE NIC ONLY:
|
||||
# Some bays carry two NICs - a private controller NIC and the routable
|
||||
# corporate NIC. Only the routable one belongs in ShopDB. A site may name its
|
||||
# corporate ranges (-AllowedRanges, or the CollectorRanges registry value); with
|
||||
# none configured the NIC carrying the DEFAULT ROUTE is used, which expresses
|
||||
# the same intent without knowing any site's addressing.
|
||||
# corporate NIC. This script reports ALL of them and lets the server decide
|
||||
# which is the PC's address, from the collector_corporate_ranges setting.
|
||||
# Choosing here was wrong: a bay cannot know a site's addressing, and a CMM
|
||||
# whose 10Gbit controller link carries a default route reported the
|
||||
# machine-side address as the PC's - with the corporate one never sent.
|
||||
|
||||
param(
|
||||
# Flask collector endpoint for the computers plugin. Empty resolves from
|
||||
@@ -90,18 +91,6 @@ if (-not $ApiUrl) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# A site may name its routable ranges rather than rely on the default route.
|
||||
if (-not $AllowedRanges) {
|
||||
foreach ($regPath in @('HKLM:\SOFTWARE\GE\ShopDB', 'HKLM:\SOFTWARE\WOW6432Node\GE\ShopDB')) {
|
||||
try {
|
||||
if (Test-Path $regPath) {
|
||||
$v = [string](Get-ItemProperty -Path $regPath -Name CollectorRanges -ErrorAction Stop).CollectorRanges
|
||||
if ($v -and $v.Trim()) { $AllowedRanges = $v.Trim(); break }
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
# collector key from the GE-Enforce secret store when not passed via Args.
|
||||
if (-not $ApiKey) {
|
||||
foreach ($regPath in @('HKLM:\SOFTWARE\GE\ShopDB', 'HKLM:\SOFTWARE\WOW6432Node\GE\ShopDB')) {
|
||||
@@ -118,38 +107,13 @@ if (-not $ApiKey) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Routable ranges, if this site named any. NO SITE ADDRESSING IS BAKED IN: an
|
||||
# unconfigured site falls through to the default-route NIC below (ADR-015).
|
||||
#
|
||||
# NOT named $allowedRanges: PowerShell variable names are case-insensitive, so
|
||||
# that collides with the [string] parameter above and the array is silently
|
||||
# COERCED to a string. .Count on a string is 1, so the script then believes a
|
||||
# range is configured and never falls back to the default route.
|
||||
$rangeList = @()
|
||||
foreach ($cidr in ($AllowedRanges -split ',')) {
|
||||
$cidr = $cidr.Trim()
|
||||
if (-not $cidr) { continue }
|
||||
$parts = $cidr -split '/'
|
||||
if ($parts.Count -ne 2) { Log "WARN ignoring malformed range '$cidr'"; continue }
|
||||
$rangeList += @{ Network = $parts[0].Trim(); PrefixLen = [int]$parts[1] }
|
||||
}
|
||||
|
||||
function ConvertTo-Uint32([string]$ip) {
|
||||
$bytes = ([System.Net.IPAddress]::Parse($ip)).GetAddressBytes()
|
||||
[Array]::Reverse($bytes)
|
||||
return [BitConverter]::ToUInt32($bytes, 0)
|
||||
}
|
||||
|
||||
function Test-InAllowedRange([string]$ip) {
|
||||
try {
|
||||
$ipInt = ConvertTo-Uint32 $ip
|
||||
foreach ($r in $rangeList) {
|
||||
$netInt = ConvertTo-Uint32 $r.Network
|
||||
$mask = [uint32]([math]::Pow(2, 32) - [math]::Pow(2, 32 - $r.PrefixLen))
|
||||
if (($ipInt -band $mask) -eq ($netInt -band $mask)) { return $true }
|
||||
}
|
||||
} catch {}
|
||||
return $false
|
||||
# -AllowedRanges and the CollectorRanges registry value are ACCEPTED AND
|
||||
# IGNORED. Deciding which NIC is the PC's address moved to the server, where
|
||||
# the site's addressing actually lives: this script now reports every NIC and
|
||||
# takes no view. The parameter stays so a scheduled task still passing it does
|
||||
# not fail on an unknown argument.
|
||||
if ($AllowedRanges) {
|
||||
Log 'NOTE -AllowedRanges is ignored; set collector_corporate_ranges in ShopDB settings instead.'
|
||||
}
|
||||
|
||||
Log '=== Report asset to ShopDB (collector) ==='
|
||||
@@ -299,42 +263,48 @@ try {
|
||||
Log "WARN could not read make/model: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
# The routable IPv4 - a physical, connected NIC. The collector schema takes a
|
||||
# single ipaddress string, so report the corporate NIC and drop any
|
||||
# controller/machine-LAN NIC. With ranges configured the IP must be in one;
|
||||
# with none, the NIC carrying the default route is the routable one by
|
||||
# definition, which needs no knowledge of a site's addressing.
|
||||
$corpIp = ''
|
||||
# EVERY connected hardware NIC with a routable IPv4, and no opinion about
|
||||
# which one matters. Choosing used to happen here and this was the wrong place
|
||||
# for it: a bay cannot know a site's addressing. A CMM whose Marvell AQtion
|
||||
# 10Gbit link to its controller sits on 100.0.0.1 reported THAT as the PC's
|
||||
# address - the link carries a default route too - and the corporate address
|
||||
# was never sent at all, so ShopDB could not even show it as a second row.
|
||||
#
|
||||
# The server picks the primary now, from the site's configured corporate
|
||||
# ranges, and records the rest. Report what is here; let it decide.
|
||||
$interfaces = @()
|
||||
$defaultRouteIfIndexes = @()
|
||||
if ($rangeList.Count -eq 0) {
|
||||
try {
|
||||
$defaultRouteIfIndexes = @(Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop |
|
||||
Sort-Object RouteMetric |
|
||||
Select-Object -ExpandProperty InterfaceIndex -Unique)
|
||||
} catch {
|
||||
Log "WARN could not read the route table: $($_.Exception.Message)"
|
||||
}
|
||||
try {
|
||||
$defaultRouteIfIndexes = @(Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop |
|
||||
Select-Object -ExpandProperty InterfaceIndex -Unique)
|
||||
} catch {
|
||||
Log "WARN could not read the route table: $($_.Exception.Message)"
|
||||
}
|
||||
try {
|
||||
$ipObjs = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
|
||||
Where-Object { $_.IPAddress -notmatch '^169\.254' -and $_.IPAddress -ne '127.0.0.1' }
|
||||
foreach ($ipo in $ipObjs) {
|
||||
if ($corpIp) { break }
|
||||
$adapter = $null
|
||||
try { $adapter = Get-NetAdapter -InterfaceIndex $ipo.InterfaceIndex -ErrorAction Stop } catch {}
|
||||
if (-not $adapter) { continue }
|
||||
if (-not $adapter.HardwareInterface) { continue }
|
||||
if ($adapter.Status -ne 'Up') { continue }
|
||||
if ($rangeList.Count -gt 0) {
|
||||
if (Test-InAllowedRange $ipo.IPAddress) { $corpIp = $ipo.IPAddress }
|
||||
} elseif ($defaultRouteIfIndexes -contains $ipo.InterfaceIndex) {
|
||||
$corpIp = $ipo.IPAddress
|
||||
$interfaces += @{
|
||||
ipaddress = $ipo.IPAddress
|
||||
macaddress = $adapter.MacAddress
|
||||
adaptername = $adapter.InterfaceDescription
|
||||
isdefaultroute = [bool]($defaultRouteIfIndexes -contains $ipo.InterfaceIndex)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Log "WARN interface enumeration failed: $($_.Exception.Message)"
|
||||
}
|
||||
if (-not $corpIp) { Log 'WARN no routable IPv4 NIC found; posting identity without ipaddress.' }
|
||||
if ($interfaces.Count -eq 0) {
|
||||
Log 'WARN no routable IPv4 NIC found; posting identity without any interface.'
|
||||
} else {
|
||||
Log ("Reporting {0} NIC(s): {1}" -f $interfaces.Count,
|
||||
(($interfaces | ForEach-Object { $_.ipaddress }) -join ', '))
|
||||
}
|
||||
|
||||
# collector schema fields (lowercase concatenated). hostname is required; the
|
||||
# rest are sent only when present so a partial read never blanks a good value.
|
||||
@@ -348,7 +318,7 @@ if ($model) { $body['modelnumber'] = $model }
|
||||
if ($osVersion) { $body['osname'] = $osVersion }
|
||||
if ($lastBootTime) { $body['lastboottime'] = $lastBootTime }
|
||||
if ($loggedInUser) { $body['loggedinuser'] = $loggedInUser }
|
||||
if ($corpIp) { $body['ipaddress'] = $corpIp }
|
||||
if ($interfaces.Count -gt 0) { $body['interfaces'] = $interfaces }
|
||||
$body['lastcheckin'] = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss')
|
||||
|
||||
$json = $body | ConvertTo-Json -Compress -Depth 4
|
||||
|
||||
Reference in New Issue
Block a user