761 lines
29 KiB
PowerShell
761 lines
29 KiB
PowerShell
###############################################################################
|
|
# #
|
|
# Name WinBGP-CLI #
|
|
# #
|
|
# Description WinBGP CLI to manage WinBGP engine #
|
|
# #
|
|
# Notes Pipe control is based on JFLarvoire service example #
|
|
# (https://github.com/JFLarvoire/SysToolsLib) #
|
|
# #
|
|
# #
|
|
# Copyright (c) 2024 Alexandre JARDON | Webalex System. #
|
|
# All rights reserved.' #
|
|
# LicenseUri https://github.com/webalexeu/winbgp/blob/master/LICENSE #
|
|
# ProjectUri https://github.com/webalexeu/winbgp #
|
|
# #
|
|
###############################################################################
|
|
|
|
#Requires -version 5.1
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
WinBGP CLI local management.
|
|
|
|
.DESCRIPTION
|
|
This script manage locally WinBGP.
|
|
|
|
.PARAMETER Start
|
|
Start the service.
|
|
|
|
.PARAMETER Stop
|
|
Stop the service.
|
|
|
|
.PARAMETER Restart
|
|
Stop then restart the service.
|
|
|
|
.PARAMETER Status
|
|
Get the current service status: Not installed / Stopped / Running
|
|
|
|
.PARAMETER Control
|
|
Send a control message to the service thread.
|
|
|
|
.PARAMETER Version
|
|
Display this script version and exit.
|
|
|
|
.EXAMPLE
|
|
# Setup the service and run it for the first time
|
|
C:\PS>.\PSService.ps1 -Status
|
|
Not installed
|
|
C:\PS>.\PSService.ps1 -Setup
|
|
C:\PS># At this stage, a copy of PSService.ps1 is present in the path
|
|
C:\PS>PSService -Status
|
|
Stopped
|
|
C:\PS>PSService -Start
|
|
C:\PS>PSService -Status
|
|
Running
|
|
C:\PS># Load the log file in Notepad.exe for review
|
|
C:\PS>notepad ${ENV:windir}\Logs\PSService.log
|
|
|
|
.EXAMPLE
|
|
# Stop the service and uninstall it.
|
|
C:\PS>PSService -Stop
|
|
C:\PS>PSService -Status
|
|
Stopped
|
|
C:\PS>PSService -Remove
|
|
C:\PS># At this stage, no copy of PSService.ps1 is present in the path anymore
|
|
C:\PS>.\PSService.ps1 -Status
|
|
Not installed
|
|
|
|
.EXAMPLE
|
|
# Configure the service to run as a different user
|
|
C:\PS>$cred = Get-Credential -UserName LAB\Assistant
|
|
C:\PS>.\PSService -Setup -Credential $cred
|
|
|
|
.EXAMPLE
|
|
# Send a control message to the service, and verify that it received it.
|
|
C:\PS>PSService -Control Hello
|
|
C:\PS>Notepad C:\Windows\Logs\PSService.log
|
|
# The last lines should contain a trace of the reception of this Hello message
|
|
#>
|
|
[CmdletBinding(DefaultParameterSetName='BGPStatus')]
|
|
Param(
|
|
[Parameter(ParameterSetName='Start', Mandatory=$true)]
|
|
[Switch]$Start, # Start the service
|
|
|
|
[Parameter(ParameterSetName='Stop', Mandatory=$true)]
|
|
[Switch]$Stop, # Stop the service
|
|
|
|
[Parameter(ParameterSetName='Restart', Mandatory=$true)]
|
|
[Switch]$Restart, # Restart the service
|
|
|
|
[Parameter(ParameterSetName='Status', Mandatory=$false)]
|
|
[Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'), # Get the current service status
|
|
|
|
[Parameter(ParameterSetName='Control', Mandatory=$true)]
|
|
[String]$Control = $null, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='Reload', Mandatory=$false)]
|
|
[Switch]$Reload = $($PSCmdlet.ParameterSetName -eq 'reload'), # Reload configuration
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$true)]
|
|
[ArgumentCompleter( {
|
|
param ( $CommandName,
|
|
$ParameterName,
|
|
$WordToComplete,
|
|
$CommandAst,
|
|
$FakeBoundParameters )
|
|
# Dynamically generate routes array
|
|
# TO BE IMPROVED - Set to static temporary
|
|
$configuration=Get-Content 'C:\Program Files\WinBGP\winbgp.json' | ConvertFrom-Json
|
|
[Array] $routes = ($configuration.routes).RouteName
|
|
return $routes
|
|
})]
|
|
[String]$RouteName = $null, # Select route to control
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$StartMaintenance, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$StopMaintenance, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$StartRoute, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$StopRoute, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$StartHealthCheck, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$StopHealthCheck, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='RouteName', Mandatory=$false)]
|
|
[Switch]$RestartHealthCheck, # Control message to send to the service
|
|
|
|
[Parameter(ParameterSetName='BGPStatus', Mandatory=$false)]
|
|
[Switch]$BGPStatus = $($PSCmdlet.ParameterSetName -eq 'BGPStatus'), # Get the current service status
|
|
|
|
[Parameter(ParameterSetName='Config', Mandatory=$false)]
|
|
[Switch]$Config = $($PSCmdlet.ParameterSetName -eq 'Config'), # Get the current configuration
|
|
|
|
[Parameter(ParameterSetName='Logs', Mandatory=$false)]
|
|
[Switch]$Logs = $($PSCmdlet.ParameterSetName -eq 'Logs'), # Get the last logs
|
|
|
|
[Parameter(ParameterSetName='Logs', Mandatory=$false)]
|
|
[Int]$Last = 20, # Define the last logs number
|
|
|
|
[Parameter(ParameterSetName='RestartAPI', Mandatory=$false)]
|
|
[Switch]$RestartAPI, # RestartAPI
|
|
|
|
[Parameter(ParameterSetName='Version', Mandatory=$true)]
|
|
[Switch]$Version # Get this script version
|
|
)
|
|
|
|
# Don't forget to increment version when updating engine
|
|
$scriptVersion = '1.0.2'
|
|
|
|
# This script name, with various levels of details
|
|
# Ex: PSService
|
|
$scriptFullName = 'C:\Program Files\WinBGP\WinBGP.ps1' # Ex: C:\Temp\PSService.ps1
|
|
|
|
# Global settings
|
|
$serviceName = "WinBGP" # A one-word name used for net start commands
|
|
$serviceDisplayName = "WinBGP"
|
|
$pipeName = "Service_$serviceName" # Named pipe name. Used for sending messages to the service task
|
|
$installDir = "${ENV:ProgramW6432}\$serviceDisplayName" # Where to install the service files
|
|
$configfile = "$serviceDisplayName.json"
|
|
$configdir = "$installDir\$configfile"
|
|
$FunctionCliXml="$installDir\$serviceDisplayName.xml" # Used to stored Maintenance variable
|
|
$logName = "Application" # Event Log name (Unrelated to the logFile!)
|
|
|
|
# If the -Version switch is specified, display the script version and exit.
|
|
if ($Version) {
|
|
return $scriptVersion
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
# #
|
|
# Function Now #
|
|
# #
|
|
# Description Get a string with the current time. #
|
|
# #
|
|
# Notes The output string is in the ISO 8601 format, except for #
|
|
# a space instead of a T between the date and time, to #
|
|
# improve the readability. #
|
|
# #
|
|
# History #
|
|
# 2015-06-11 JFL Created this routine. #
|
|
# #
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
Function Now {
|
|
Param (
|
|
[Switch]$ms, # Append milliseconds
|
|
[Switch]$ns # Append nanoseconds
|
|
)
|
|
$Date = Get-Date
|
|
$now = ""
|
|
$now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
|
|
$now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
|
|
$nsSuffix = ""
|
|
if ($ns) {
|
|
if ("$($Date.TimeOfDay)" -match "\.\d\d\d\d\d\d") {
|
|
$now += $matches[0]
|
|
$ms = $false
|
|
} else {
|
|
$ms = $true
|
|
$nsSuffix = "000"
|
|
}
|
|
}
|
|
if ($ms) {
|
|
$now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
|
|
}
|
|
return $now
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
# #
|
|
# Function Log #
|
|
# #
|
|
# Description Log a string into the PSService.log file #
|
|
# #
|
|
# Arguments A string #
|
|
# #
|
|
# Notes Prefixes the string with a timestamp and the user name. #
|
|
# (Except if the string is empty: Then output a blank line.)#
|
|
# #
|
|
# History #
|
|
# 2016-06-05 JFL Also prepend the Process ID. #
|
|
# 2016-06-08 JFL Allow outputing blank lines. #
|
|
# #
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
#Logging function
|
|
function Write-Log {
|
|
<#
|
|
.Synopsis
|
|
Write-Log writes a message to a specified log file with the current time stamp.
|
|
.DESCRIPTION
|
|
The Write-Log function is designed to add logging capability to other scripts.
|
|
In addition to writing output and/or verbose you can write to a log file for
|
|
later debugging.
|
|
.NOTES
|
|
Created by: Jason Wasser @wasserja
|
|
Modified: 11/24/2015 09:30:19 AM
|
|
|
|
Changelog:
|
|
* Code simplification and clarification - thanks to @juneb_get_help
|
|
* Added documentation.
|
|
* Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks
|
|
* Revised the Force switch to work as it should - thanks to @JeffHicks
|
|
|
|
To Do:
|
|
* Add error handling if trying to create a log file in a inaccessible location.
|
|
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
|
|
duplicates.
|
|
.PARAMETER Message
|
|
Message is the content that you wish to add to the log file.
|
|
.PARAMETER Level
|
|
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
|
|
.PARAMETER NoClobber
|
|
Use NoClobber if you do not wish to overwrite an existing file.
|
|
.EXAMPLE
|
|
Write-Log -Message 'Log message'
|
|
Writes the message to c:\Logs\PowerShellLog.log.
|
|
.EXAMPLE
|
|
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
|
|
Writes the content to the specified log file and creates the path and file specified.
|
|
.EXAMPLE
|
|
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
|
|
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
|
|
.LINK
|
|
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
|
|
#>
|
|
[CmdletBinding()]
|
|
Param
|
|
(
|
|
[Parameter(Mandatory=$true,
|
|
ValueFromPipelineByPropertyName=$true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias("LogContent")]
|
|
[string]$Message,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[ValidateSet("Error","Warning","Information")]
|
|
[string]$Level="Information",
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$EventLogName=$logName,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$EventLogSource=$serviceName,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$EventLogId=1006,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$EventLogCategory=0,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[Array]$AdditionalFields=$null,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[switch]$NoClobber
|
|
)
|
|
|
|
Begin
|
|
{
|
|
}
|
|
Process
|
|
{
|
|
# Manage AdditionalFields (Not by default with PowerShell function)
|
|
if ($AdditionalFields) {
|
|
$EventInstance = [System.Diagnostics.EventInstance]::new($EventLogId, $EventLogCategory, $Level)
|
|
$NewEvent = [System.Diagnostics.EventLog]::new()
|
|
$NewEvent.Log = $EventLogName
|
|
$NewEvent.Source = $EventLogSource
|
|
[Array] $JoinedMessage = @(
|
|
$Message
|
|
$AdditionalFields | ForEach-Object { $_ }
|
|
)
|
|
$NewEvent.WriteEvent($EventInstance, $JoinedMessage)
|
|
} else {
|
|
#Write log to event viewer (Enabled by default)
|
|
Write-EventLog -LogName $EventLogName -Source $EventLogSource -EventId $EventLogId -EntryType $Level -Category $EventLogCategory -Message "$Message"
|
|
}
|
|
}
|
|
End
|
|
{
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
# #
|
|
# Function Send-PipeMessage #
|
|
# #
|
|
# Description Send a message to a named pipe #
|
|
# #
|
|
# Arguments See the Param() block #
|
|
# #
|
|
# Notes #
|
|
# #
|
|
# History #
|
|
# 2016-05-25 JFL Created this function #
|
|
# #
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
Function Send-PipeMessage () {
|
|
Param(
|
|
[Parameter(Mandatory=$true)]
|
|
[String]$PipeName, # Named pipe name
|
|
[Parameter(Mandatory=$true)]
|
|
[String]$Message # Message string
|
|
)
|
|
$PipeDir = [System.IO.Pipes.PipeDirection]::Out
|
|
$PipeOpt = [System.IO.Pipes.PipeOptions]::Asynchronous
|
|
|
|
$pipe = $null # Named pipe stream
|
|
$sw = $null # Stream Writer
|
|
try {
|
|
$pipe = new-object System.IO.Pipes.NamedPipeClientStream(".", $PipeName, $PipeDir, $PipeOpt)
|
|
$sw = new-object System.IO.StreamWriter($pipe)
|
|
$pipe.Connect(1000)
|
|
if (!$pipe.IsConnected) {
|
|
throw "Failed to connect client to pipe $pipeName"
|
|
}
|
|
$sw.AutoFlush = $true
|
|
$sw.WriteLine($Message)
|
|
} catch {
|
|
Write-Log "Error sending pipe $pipeName message: $_" -Level Error
|
|
} finally {
|
|
if ($sw) {
|
|
$sw.Dispose() # Release resources
|
|
$sw = $null # Force the PowerShell garbage collector to delete the .net object
|
|
}
|
|
if ($pipe) {
|
|
$pipe.Dispose() # Release resources
|
|
$pipe = $null # Force the PowerShell garbage collector to delete the .net object
|
|
}
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
# #
|
|
# Function Test-ConfigurationFile #
|
|
# #
|
|
# Description Test WinBGP configuration file #
|
|
# #
|
|
# Arguments See the Param() block at the top of this script #
|
|
# #
|
|
# Notes #
|
|
# #
|
|
# History #
|
|
# #
|
|
#-----------------------------------------------------------------------------#
|
|
function Test-ConfigurationFile()
|
|
{
|
|
Param
|
|
(
|
|
[Parameter(Mandatory=$false)]
|
|
$Path=$configdir
|
|
)
|
|
|
|
# Json validation
|
|
try {
|
|
$configuration = Get-Content -Path $Path | ConvertFrom-Json
|
|
$validJson = $true
|
|
} catch {
|
|
$validJson = $false
|
|
}
|
|
|
|
if ($validJson) {
|
|
$ValidConfig=$true
|
|
# Global
|
|
if ($configuration.global.Interval -isnot [Int32]) {$ValidConfig=$false}
|
|
if ($configuration.global.Timeout -isnot [Int32]) {$ValidConfig=$false}
|
|
if ($configuration.global.Rise -isnot [Int32]) {$ValidConfig=$false}
|
|
if ($configuration.global.Fall -isnot [Int32]) {$ValidConfig=$false}
|
|
if ($configuration.global.Metric -isnot [Int32]) {$ValidConfig=$false}
|
|
if ($configuration.global.Api -isnot [Boolean]) {$ValidConfig=$false}
|
|
|
|
# Api (Check only if Api is enabled)
|
|
if ($configuration.global.Api) {
|
|
if ($configuration.api -isnot [array]) {$ValidConfig=$false}
|
|
}
|
|
|
|
# Router
|
|
if ([string]::IsNullOrEmpty($configuration.router.BgpIdentifier)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($configuration.router.LocalASN)) {$ValidConfig=$false}
|
|
|
|
# Peers
|
|
if ($configuration.peers -is [array]) {
|
|
foreach ($peer in $configuration.peers) {
|
|
if ([string]::IsNullOrEmpty($peer.PeerName)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($peer.LocalIP)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($peer.PeerIP)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($peer.LocalASN)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($peer.PeerASN)) {$ValidConfig=$false}
|
|
}
|
|
} else {
|
|
$ValidConfig=$false
|
|
}
|
|
|
|
# Routes
|
|
if ($configuration.routes -is [array]) {
|
|
foreach ($route in $configuration.routes) {
|
|
if ([string]::IsNullOrEmpty($route.RouteName)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($route.Network)) {$ValidConfig=$false}
|
|
if ([string]::IsNullOrEmpty($route.Interface)) {$ValidConfig=$false}
|
|
if ($route.DynamicIpSetup -isnot [Boolean]) {$ValidConfig=$false}
|
|
if ($route.WithdrawOnDown -isnot [Boolean]) {$ValidConfig=$false}
|
|
# Only if WithdrawOnDown is enabled
|
|
if ($route.WithdrawOnDown) {
|
|
if ([string]::IsNullOrEmpty($route.WithdrawOnDownCheck)) {$ValidConfig=$false}
|
|
}
|
|
if ([string]::IsNullOrEmpty($route.NextHop)) {$ValidConfig=$false}
|
|
# Community
|
|
if ($route.Community -is [array]) {
|
|
# Parsing all Community
|
|
foreach ($community in $route.Community) {
|
|
if ([string]::IsNullOrEmpty($community)) {$ValidConfig=$false}
|
|
}
|
|
} else {
|
|
$ValidConfig=$false
|
|
}
|
|
}
|
|
} else {
|
|
$ValidConfig=$false
|
|
}
|
|
}
|
|
|
|
# If Json type and content are valid
|
|
if (($validJson) -and ($ValidConfig)) {
|
|
return $true
|
|
} else {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
# #
|
|
# Function Main #
|
|
# #
|
|
# Description Execute the specified actions #
|
|
# #
|
|
# Arguments See the Param() block at the top of this script #
|
|
# #
|
|
# Notes #
|
|
# #
|
|
# History #
|
|
# #
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
# Identify the user name. We use that for logging.
|
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$currentUserName = $identity.Name # Ex: "NT AUTHORITY\SYSTEM" or "Domain\Administrator"
|
|
|
|
# Workaround for PowerShell v2 bug: $PSCmdlet Not yet defined in Param() block
|
|
$Status = ($PSCmdlet.ParameterSetName -eq 'Status')
|
|
|
|
if ($Start) { # The user tells us to start the service
|
|
Write-Verbose "Starting service $serviceName"
|
|
Write-Log -Message "Starting service $serviceName"
|
|
Start-Service $serviceName # Ask Service Control Manager to start it
|
|
return
|
|
}
|
|
|
|
if ($Stop) { # The user tells us to stop the service
|
|
Write-Verbose "Stopping service $serviceName"
|
|
Write-Log -Message "Stopping service $serviceName"
|
|
Stop-Service $serviceName # Ask Service Control Manager to stop it
|
|
return
|
|
}
|
|
|
|
if ($Restart) { # Restart the service
|
|
& $scriptFullName -Stop
|
|
& $scriptFullName -Start
|
|
return
|
|
}
|
|
|
|
if ($Status) { # Get the current service status
|
|
$spid = $null
|
|
$processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
|
|
$_.CommandLine -match ".*$scriptCopyCname.*-Service"
|
|
})
|
|
foreach ($process in $processes) { # There should be just one, but be prepared for surprises.
|
|
$spid = $process.ProcessId
|
|
Write-Verbose "$serviceName Process ID = $spid"
|
|
}
|
|
# if (Test-Path "HKLM:\SYSTEM\CurrentControlSet\services\$serviceName") {}
|
|
try {
|
|
$pss = Get-Service $serviceName -ea stop # Will error-out if not installed
|
|
} catch {
|
|
"Not Installed"
|
|
return
|
|
}
|
|
$pss.Status
|
|
if (($pss.Status -eq "Running") -and (!$spid)) { # This happened during the debugging phase
|
|
Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
|
|
exit 1
|
|
}
|
|
return
|
|
}
|
|
|
|
if ($Control) { # Send a control message to the service
|
|
Send-PipeMessage $pipeName $control
|
|
}
|
|
|
|
#Reload control
|
|
if ($Reload) {
|
|
$control='reload'
|
|
Send-PipeMessage $pipeName $control
|
|
|
|
# If Json is valid, reloading
|
|
if (Test-ConfigurationFile) {
|
|
return 'Success'
|
|
} else {
|
|
return "Configuration file '$($configdir)' is not valid"
|
|
}
|
|
}
|
|
|
|
# Restart API
|
|
if ($RestartAPI) {
|
|
$control='restart api'
|
|
Send-PipeMessage $pipeName $control
|
|
# Output message to be improved
|
|
return 'Success'
|
|
}
|
|
|
|
# Start/stop control or Maintenance control
|
|
if ($StartRoute -or $StopRoute -or $StartMaintenance -or $StopMaintenance -or $StartHealthCheck -or $StopHealthCheck -or $RestartHealthCheck) {
|
|
if ($StartRoute -or $StopRoute -or $StartMaintenance -or $StopMaintenance ) {
|
|
# Logging
|
|
Write-Log "Operation for route '$RouteName' triggered by '$currentUserName'"
|
|
}
|
|
|
|
# Read configuration
|
|
$configuration = Get-Content -Path $configdir | ConvertFrom-Json
|
|
$routeCheck=$null
|
|
$routeCheck=$configuration.routes | Where-Object {$_.RouteName -eq $RouteName}
|
|
# Start/stop control
|
|
if ($StartRoute -or $StopRoute) {
|
|
# START
|
|
if ($StartRoute) {
|
|
$control="route $RouteName start"
|
|
}
|
|
# STOP
|
|
if ($StopRoute) {
|
|
$control="route $RouteName stop"
|
|
}
|
|
}
|
|
# Maintenance control
|
|
if ($StartMaintenance -or $StopMaintenance) {
|
|
# START
|
|
if ($StartMaintenance) {
|
|
$control="maintenance $RouteName start"
|
|
}
|
|
# STOP
|
|
if ($StopMaintenance) {
|
|
$control="maintenance $RouteName stop"
|
|
}
|
|
}
|
|
# Start/stop control HealthCheck
|
|
if ($StartHealthCheck -or $StopHealthCheck -or $RestartHealthCheck) {
|
|
# START
|
|
if ($StartHealthCheck) {
|
|
$control="healthcheck $RouteName start"
|
|
}
|
|
# STOP
|
|
if ($StopHealthCheck) {
|
|
$control="healthcheck $RouteName stop"
|
|
}
|
|
# RESTART
|
|
if ($RestartHealthCheck) {
|
|
$control="healthcheck $RouteName restart"
|
|
}
|
|
}
|
|
if($routeCheck) {
|
|
$PipeStatus=$null
|
|
# Performing Action
|
|
try {
|
|
Send-PipeMessage $pipeName $control
|
|
}
|
|
catch {
|
|
$PipeStatus=($_).ToString()
|
|
}
|
|
if ($PipeStatus -like "*Pipe hasn't been connected yet*") {
|
|
return "WinBGP not ready"
|
|
} else {
|
|
# TO BE IMPROVED to get status
|
|
return "Success"
|
|
}
|
|
} else {
|
|
# Logging
|
|
Write-Log "Received control message: $control"
|
|
Write-Log "Control return: Route '$RouteName' not found" -Level Warning
|
|
return "Route '$RouteName' not found"
|
|
}
|
|
}
|
|
|
|
# Get the current BGP status
|
|
if ($BGPStatus) {
|
|
# Read configuration
|
|
$configuration = Get-Content -Path $configdir | ConvertFrom-Json
|
|
# Read maintenance
|
|
#If there is a maintenance, import it
|
|
if(Test-Path -Path $FunctionCliXml) {
|
|
#Import variable
|
|
$maintenance=Import-CliXml -Path $FunctionCliXml
|
|
} else {
|
|
#Otherwise, initialize variable
|
|
$maintenance = @{}
|
|
}
|
|
|
|
# Read BGP routes and policy (To optimize query)
|
|
$BGPRoutes=$null
|
|
$BGPPolicies=$null
|
|
try {
|
|
# Use CIM query to improve performance
|
|
$BGPRoutes=(Invoke-CimMethod -ClassName "PS_BgpCustomRoute" -Namespace 'ROOT\Microsoft\Windows\RemoteAccess' -MethodName Get).cmdletoutput.Network
|
|
$BGPPolicies=(Invoke-CimMethod -ClassName "PS_BgpRoutingPolicy" -Namespace 'ROOT\Microsoft\Windows\RemoteAccess' -MethodName Get).cmdletoutput.PolicyName
|
|
}
|
|
catch {
|
|
}
|
|
|
|
# Read IP Addresses (To optimize query)
|
|
$IPAddresses=(Get-NetIPAddress -AddressFamily IPv4).IPAddress
|
|
|
|
#Parse all routes
|
|
$Routes=@()
|
|
ForEach ($route in $configuration.routes) {
|
|
$RouteStatus=$null
|
|
$RouteStatusDetailled=$null
|
|
# Check if route is in maintenance mode
|
|
if ($maintenance.($route.RouteName)) {
|
|
$RouteStatus='maintenance'
|
|
# Check if route is up (Only if BGP service is configured)
|
|
} else {
|
|
if ($BGPRoutes -contains "$($route.Network)") {
|
|
# Check route policy
|
|
if ($BGPPolicies -contains "$($route.RouteName)") {
|
|
# Check IP
|
|
if ($route.DynamicIpSetup) {
|
|
if ($IPAddresses -contains "$($route.Network.split('/')[0])") {
|
|
$RouteStatus='up'
|
|
} else {
|
|
$RouteStatus='warning'
|
|
$RouteStatusDetailled='IP Address not mounted'
|
|
}
|
|
} else {
|
|
$RouteStatus='up'
|
|
}
|
|
} else {
|
|
$RouteStatus='warning'
|
|
$RouteStatusDetailled='No routing policy defined'
|
|
}
|
|
# Route down
|
|
} else {
|
|
# Check IP
|
|
if ($route.DynamicIpSetup) {
|
|
if ($IPAddresses -contains "$($route.Network.split('/')[0])") {
|
|
$RouteStatus='warning'
|
|
$RouteStatusDetailled='IP Address still mounted'
|
|
} else {
|
|
$RouteStatus='down'
|
|
}
|
|
} else {
|
|
$RouteStatus='down'
|
|
}
|
|
}
|
|
}
|
|
$RouteProperties=[PSCustomObject]@{
|
|
Name = $route.RouteName;
|
|
Network = $route.Network;
|
|
Status = $RouteStatus;
|
|
MaintenanceTimestamp = $maintenance.($route.RouteName);
|
|
RouteStatusDetailled = $RouteStatusDetailled;
|
|
}
|
|
# Add route to array
|
|
$Routes += $RouteProperties
|
|
}
|
|
# Select default properties to display
|
|
$defaultDisplaySet = 'Name','Network','Status','MaintenanceTimestamp'
|
|
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet)
|
|
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
|
|
$Routes | Add-Member MemberSet PSStandardMembers $PSStandardMembers
|
|
|
|
return $Routes
|
|
}
|
|
|
|
if ($Config) {
|
|
# If Json is valid, reloading
|
|
if (Test-ConfigurationFile) {
|
|
$configuration = Get-Content -Path $configdir | ConvertFrom-Json
|
|
return $configuration
|
|
} else {
|
|
return "Configuration file '$($configdir)' is not valid"
|
|
}
|
|
}
|
|
|
|
if ($Logs) {
|
|
$EventLogs=Get-EventLog -LogName Application -Source WinBGP -Newest $Last | Select-Object Index,TimeGenerated,EntryType,Message,ReplacementStrings
|
|
$DisplayLogs=@()
|
|
foreach ($log in $EventLogs) {
|
|
if($log.ReplacementStrings -gt 1) {
|
|
$log | Add-Member -MemberType NoteProperty -Name 'RouteName' -Value $log.ReplacementStrings[1]
|
|
}
|
|
$log.PsObject.Members.Remove('ReplacementStrings')
|
|
$DisplayLogs+=$log
|
|
}
|
|
|
|
# Select default properties to display
|
|
$defaultDisplaySet = 'TimeGenerated','EntryType','Message','RouteName'
|
|
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet)
|
|
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
|
|
$DisplayLogs | Add-Member MemberSet PSStandardMembers $PSStandardMembers
|
|
|
|
return $DisplayLogs
|
|
}
|