Modules/Private/60-NetworkDeviceParser.ps1
|
function Invoke-RangerNetworkDeviceConfigImport { <# .SYNOPSIS Imports offline vendor config files specified in the networkDeviceConfigs hints section. .DESCRIPTION Processes config files for switches and firewalls that are listed under $Config.domains.hints.networkDeviceConfigs. Returns arrays for switchConfig and firewallConfig networking domain keys. #> param( [Parameter(Mandatory = $true)] [System.Collections.IDictionary]$Config ) $hints = $Config.domains.hints $deviceConfigHints = @($hints.networkDeviceConfigs) $switchConfigs = New-Object System.Collections.ArrayList $firewallConfigs = New-Object System.Collections.ArrayList if ($deviceConfigHints.Count -eq 0) { return [ordered]@{ switchConfig = @() firewallConfig = @() } } foreach ($hint in $deviceConfigHints) { if ($null -eq $hint) { continue } $path = $hint.path $vendor = $hint.vendor $role = $hint.role if (-not $path) { Write-RangerLog -Level warn -Message "networkDeviceConfigs hint is missing a 'path' field — skipping entry." continue } $resolvedPath = Resolve-RangerPath -Path $path if (-not (Test-Path -Path $resolvedPath)) { Write-RangerLog -Level warn -Message "networkDeviceConfigs: file not found at '$resolvedPath' — skipping." continue } $rawContent = Get-Content -Path $resolvedPath -Raw -ErrorAction Stop $normalizedVendor = ($vendor ?? 'unknown').ToLowerInvariant() $parsed = switch -Wildcard ($normalizedVendor) { 'cisco-nxos' { ConvertFrom-RangerCiscoNxosConfig -RawContent $rawContent -FilePath $resolvedPath -Role $role } 'cisco-ios' { ConvertFrom-RangerCiscoIosConfig -RawContent $rawContent -FilePath $resolvedPath -Role $role } default { Write-RangerLog -Level warn -Message "networkDeviceConfigs: vendor '$vendor' is not supported — recording file reference only." [ordered]@{ sourceFile = [System.IO.Path]::GetFileName($resolvedPath) vendor = $vendor role = $role parseStatus = 'unsupported-vendor' vlans = @() portChannels = @() interfaces = @() acls = @() } } } if ($role -eq 'firewall') { [void]$firewallConfigs.Add($parsed) } else { [void]$switchConfigs.Add($parsed) } } return [ordered]@{ switchConfig = @($switchConfigs) firewallConfig = @($firewallConfigs) } } function ConvertFrom-RangerCiscoNxosConfig { <# .SYNOPSIS Parses a Cisco NX-OS show running-config text dump. .OUTPUTS Ordered hashtable with vlans, portChannels, interfaces, and acls. #> param( [Parameter(Mandatory = $true)] [string]$RawContent, [string]$FilePath, [string]$Role ) $lines = $RawContent -split '\r?\n' $vlans = New-Object System.Collections.ArrayList $portChannels = New-Object System.Collections.ArrayList $interfaces = New-Object System.Collections.ArrayList $acls = New-Object System.Collections.ArrayList $i = 0 while ($i -lt $lines.Count) { $line = $lines[$i].Trim() # VLAN database entries: "vlan <id>" or "vlan <id>,<id>-<id>" if ($line -match '^vlan\s+([\d,\-]+)$') { $vlanRange = $Matches[1] $vlanIds = Expand-RangerVlanRange -Range $vlanRange $vlanName = $null $vlanState = $null # Look ahead for name and state lines within the same vlan block $j = $i + 1 while ($j -lt $lines.Count -and $lines[$j] -match '^\s+') { $inner = $lines[$j].Trim() if ($inner -match '^name\s+(.+)$') { $vlanName = $Matches[1] } if ($inner -match '^state\s+(\S+)$') { $vlanState = $Matches[1] } $j++ } foreach ($id in $vlanIds) { [void]$vlans.Add([ordered]@{ vlanId = $id name = $vlanName state = $vlanState ?? 'active' }) } $i = $j continue } # Port-channel / LAG interfaces if ($line -match '^interface\s+(port-channel\d+)$') { $pcName = $Matches[1] $pcDesc = $null $pcMembers = @() $pcMode = $null $allowedVlans = $null $j = $i + 1 while ($j -lt $lines.Count -and $lines[$j] -match '^\s+') { $inner = $lines[$j].Trim() if ($inner -match '^description\s+(.+)$') { $pcDesc = $Matches[1] } if ($inner -match '^switchport mode\s+(\S+)$') { $pcMode = $Matches[1] } if ($inner -match '^switchport trunk allowed vlan\s+(.+)$') { $allowedVlans = $Matches[1] } $j++ } [void]$portChannels.Add([ordered]@{ name = $pcName description = $pcDesc mode = $pcMode allowedVlans = $allowedVlans }) $i = $j continue } # All other interfaces (Ethernet, mgmt, etc.) if ($line -match '^interface\s+(Ethernet\S+|mgmt\S+|Vlan\d+)$') { $ifName = $Matches[1] $ifDesc = $null $ifMode = $null $ifVlan = $null $ifTrunkVlans = $null $ifChannel = $null $ifShutdown = $false $j = $i + 1 while ($j -lt $lines.Count -and $lines[$j] -match '^\s+') { $inner = $lines[$j].Trim() if ($inner -match '^description\s+(.+)$') { $ifDesc = $Matches[1] } if ($inner -match '^switchport mode\s+(\S+)$') { $ifMode = $Matches[1] } if ($inner -match '^switchport access vlan\s+(\d+)$') { $ifVlan = [int]$Matches[1] } if ($inner -match '^switchport trunk allowed vlan\s+(.+)$') { $ifTrunkVlans = $Matches[1] } if ($inner -match '^channel-group\s+(\d+)') { $ifChannel = "port-channel$($Matches[1])" } if ($inner -eq 'shutdown') { $ifShutdown = $true } $j++ } [void]$interfaces.Add([ordered]@{ name = $ifName description = $ifDesc mode = $ifMode accessVlan = $ifVlan trunkVlans = $ifTrunkVlans portChannel = $ifChannel shutdown = $ifShutdown }) $i = $j continue } # IP access-lists (ACLs) if ($line -match '^ip access-list\s+(\S+)$') { $aclName = $Matches[1] $aclEntries = New-Object System.Collections.ArrayList $j = $i + 1 while ($j -lt $lines.Count -and $lines[$j] -match '^\s+') { $inner = $lines[$j].Trim() if ($inner -match '^\d+\s+(.+)$' -or $inner -match '^(permit|deny)\s+.+$') { [void]$aclEntries.Add($inner) } $j++ } [void]$acls.Add([ordered]@{ name = $aclName entries = @($aclEntries) }) $i = $j continue } $i++ } return [ordered]@{ sourceFile = [System.IO.Path]::GetFileName($FilePath) vendor = 'cisco-nxos' role = $Role parseStatus = 'parsed' vlans = @($vlans) portChannels = @($portChannels) interfaces = @($interfaces) acls = @($acls) } } function ConvertFrom-RangerCiscoIosConfig { <# .SYNOPSIS Parses a Cisco IOS show running-config text dump. .OUTPUTS Ordered hashtable with vlans, portChannels, interfaces, and acls. #> param( [Parameter(Mandatory = $true)] [string]$RawContent, [string]$FilePath, [string]$Role ) # IOS syntax is close enough to NX-OS for the keys we care about. Parse as NX-OS # and override vendor label in the output. $result = ConvertFrom-RangerCiscoNxosConfig -RawContent $RawContent -FilePath $FilePath -Role $Role # override vendor label only — IOS has vlan database blocks starting with "vlan database" # which are already skipped by the NX-OS parser harmlessly. $result['vendor'] = 'cisco-ios' return $result } function Expand-RangerVlanRange { <# .SYNOPSIS Expands a VLAN range string such as "10,20-25,30" into an array of integer VLAN IDs. #> param( [Parameter(Mandatory = $true)] [string]$Range ) $ids = New-Object System.Collections.Generic.List[int] foreach ($segment in ($Range -split ',')) { $segment = $segment.Trim() if ($segment -match '^(\d+)-(\d+)$') { for ($n = [int]$Matches[1]; $n -le [int]$Matches[2]; $n++) { $ids.Add($n) } } elseif ($segment -match '^\d+$') { $ids.Add([int]$segment) } } return @($ids) } |