A script to quickly configure a host for redundancy

May 22, 2017 / 0 comments / in General  / by CygNet Blog Admin

In preparation for WESC, I had to configure four identical CygNet hosts for redundancy.  Doing the same thing four times didn’t sound like a lot of fun so I decided to write a powershell script!  This is in no way supported or official, but it might help some of you out whenever you find yourself needing to spin up a new CygNet server or configure an existing one for redundancy.  If starting from scratch, you will need to install CygNet via the setup program to get the baseline, and then this script will configure the server to be redundant.  Specifically it will create multiple ARS/RSM service pairs that are properly configured.  For those of you not doing redundancy, you can tweak it to mass modify your config files with specific values or add nonstandard services to your site.

I make no guarantee these techniques will work in perpetuity, but I can say they were at least tested with the 8.5.1 release and I can think of no reason why they wouldn’t work with the previous few releases.  Hopefully it’s helpful, but I make no promises.  If you have any questions or find a problem, feel free to comment and I’ll try to respond.

#Assumptions

#  1) Directory contains a single ARS and RSM service directory

#  2) RSM service is currently configured to own the single ARS

 

#Steps

#  0) If on a host already running CygNet, skip to step #5

#  1) Disable UAC. (Can by done by running this powershell command):

#         Set-ItemProperty -Path HKLM:Software\Microsoft\Windows\CurrentVersion\policies\system -Name EnableLUA -Value 0 -Force;

#  2) Install CygNet

#  3) Start site

#  4) Once Host Manager shows a "RUNNING" status, stop the site

#  5) Remove installed site from Host Manager

#  6) Edit the variables in this script.

#  7) Run script

#  8) Add all the created RSM services to Host Manager

#  9) Start sites

 

 

#Configurable variables

$ServicesDir = "c:\cygnet\services"

$SiteName = "CYGNET"  #This must match the site name you used in the setup program

$RedundantRSM = "RSM_CLP" #Service name of redundant RSM

$SvcPortBase = 6090 #Base SVC_PORT value to use for new non-redundant RSM services

$AssociatedAUD = "[5410]BASTION.AUD"

$AssociatedELS = "[5410]BASTION.ELS"

$NonRedundantPairs = @(

                       (new-object psobject -Property @{rsm="RSMLP1";ars="ARSLP1";arslicensemaster=$false})

                       (new-object psobject -Property @{rsm="RSMLP2";ars="ARSLP2";arslicensemaster=$false})

                       (new-object psobject -Property @{rsm="RSMLP3";ars="ARSLP3";arslicensemaster=$false;domain=5413})

                      ) #Always include service name for non-redundant rsm/ars pairs (Whether there are two or three).  Include 'domain' property only for pair running on ambient domain of this host.  There should always be only one

 

 

###############

## FUNCTIONS

###############

 

##### Example:

#$tokens = @((new-object psobject -Property @{keyword="REPL_SOURCE";value="[5410]CYGNET.ACS";disabled=$false}))

#UpdateConfigFile c:\cygnet\services\acs.cfg $tokens

function UpdateConfigFile([string]$file, $tokens)

{

    (Get-ChildItem $file) |

        Foreach-Object {

            (Get-Content $_.FullName) |

                Foreach-Object {

           

                    $line = $_

                    $tokens | % {

                        if ($line -like ($_.keyword + "*") -or $line -like ("#" + $_.keyword + "*"))

                        {

                            if ($line -match ("(?<flag>[#]?)" + $_.keyword + "(?<spaces>\s*)(?<value>.*)"))

                            {

                                $value = $matches["value"]

                                if ($_.psobject.Properties.Name -contains "value" -eq $true)

                                {

                                    $value = $_.value

                                }

                                elseif ($_.psobject.Properties.Name -contains "find" -eq $true -and $_.psobject.Properties.Name -contains "replace" -eq $true)

                                {

                                    $value = $matches["value"] -replace $_.find, $_.replace

                                }

 

                                if ($_.psobject.Properties.Name -contains "disabled" -eq $false)

                                {

                                    $line = $matches["flag"] + $_.keyword +  $matches["spaces"] + $value

                                }

                                elseif ($_.disabled -eq $true)

                                {

                                    $line = "#" + $_.keyword +  $matches["spaces"] + $value

                                }

                                else

                                {

                                    $line = $_.keyword +  $matches["spaces"] + $value

                                }

                            }

                        }

                    }

 

                   $line  #Push the line back through the pipe

 

               } | Set-Content $_.FullName

         }

}

 

##### Example:

#$tokens = [System.Collections.ArrayList]@()

#$tokens.Add("ACS.EXE#ACSD.exe")

#FindAndReplace "C:\test.txt" $tokens

function FindAndReplace([string]$file, [System.Collections.ArrayList]$tokens)

{

    (Get-ChildItem $file) |

        Foreach-Object {

            (Get-Content $_.FullName) |

                Foreach-Object {

           

                   foreach ($element in $Tokens)

                   {

                        if ($element -ne "")

                        {

                           $tmp = $element.split("#")

                           $_ = ($_ -replace $tmp[0], $tmp[1])

                        }

                   }

                   $_  #Push the line back through the pipe

 

               } | Set-Content $_.FullName

         }

}

 

###############

## SCRIPT

###############

 

cd $ServicesDir

 

#Verify input

if ((Get-ChildItem | ? {$_.Name -match "RSM" -or $_.Name -match "ARS"}).Count -ne 2)

{

    Write-output "Directory appears malformed"

    exit;

}

 

#Generate non-redundant RSM/ARS pairs

$NonRedundantPairs | % { Copy-Item RSM $_.rsm -recurse; Copy-Item ARS $_.ars -recurse }

remove-item "ARS" -recurse

 

### Update ARS config files

$NonRedundantPairs | % {

        $tokens = @(

                    (new-object psobject -Property @{keyword="BACKUP_PATH";find="\\ARS";replace="\$($_.ars)"})

                    (new-object psobject -Property @{keyword="SERVICE";value="$SiteName.$($_.ars)"})

                    (new-object psobject -Property @{keyword="LICENSE_MASTER";value=(@{$true="YES";$false="NO"}[$_.arslicensemaster]);disabled=$false})

                    (new-object psobject -Property @{keyword="REDUNDANT";disabled=$true})

                    (new-object psobject -Property @{keyword="AUD";disabled=$false;value=$AssociatedAUD})

                    (new-object psobject -Property @{keyword="ELS";disabled=$false;value=$AssociatedELS})

                    )

        UpdateConfigFile "$($_.ars)\Ars.cfg" $tokens

}

 

### Update ARS ServiceDefs files

$NonRedundantPairs | % {

    $ServiceDefsFile = Get-ChildItem $_.ars | ? { $_.Name -like "ServiceDefs.*.db" }

    if ($ServiceDefsFile.Count -eq 0)

    {

        Write-output "Failed to add services to " $_.ars

    }

    else

    {

        $data = (Get-content $ServiceDefsFile[0].FullName -encoding byte)

        $ServiceDefsFile | Remove-item

 

        if ($_.psobject.Properties.Name -contains "domain" -eq $true)

        {

            $path = "$ServicesDir\$($_.ars)\ServiceDefs.$($_.domain).db"

            new-item $path | out-null

            $br = New-Object System.IO.BinaryWriter([System.IO.File]::Open($path, [System.IO.FileMode]::Create))

            $br.Write([uint16]47940)

            $br.Write([int16]$_.domain)

            $br.Write($data, 4, ($data.length-4))

            $br.flush()

            $br.close()

        }

    }

}

 

### Update non-redundant RSM config files

$SvcPortIndex = 0

$NonRedundantPairs | % {

        $tokens = @(

                    (new-object psobject -Property @{keyword="BACKUP_PATH";find="\\RSM";replace="\$($_.rsm)"})

                    (new-object psobject -Property @{keyword="SERVICE";value="$SiteName.$($_.rsm)"})

                    (new-object psobject -Property @{keyword="SVC_PORT";value=$SvcPortBase + $SvcPortIndex++})

                    (new-object psobject -Property @{keyword="RSM_NAME";value=$_.rsm})

                    (new-object psobject -Property @{keyword="REDUNDANT";disabled=$true})

                    (new-object psobject -Property @{keyword="AUD";disabled=$false;value=$AssociatedAUD})

                    (new-object psobject -Property @{keyword="ELS";disabled=$false;value=$AssociatedELS})

                    )

        UpdateConfigFile "$($_.rsm)\Rsm.cfg" $tokens }

 

### Update non-redundant RSM service defs

$NonRedundantPairs | % {

    $rsmName = $_.rsm

    $arsName = $_.ars

    $xml = New-Object -TypeName XML

    $xml.Load("$ServicesDir\$rsmName\SvcDef.rsx")

    $xml.rsmdata.servicedefs.SelectNodes("//servicedef") | ? { $_.service -notmatch "ARS" } | % {$_.ParentNode.RemoveChild($_) | out-null }

    $xml.rsmdata.servicedefs.SelectNodes("//servicedef") | ? { $_.service -match "ARS" } | % {

            $_.service = $_.service -replace "ARS", $arsName;

            $_.exedir = $_.exedir -replace "ARS", $arsName;

            $_.datadir = $_.datadir -replace "ARS", $arsName }

    $xml.Save("$ServicesDir\$rsmName\SvcDef.rsx")

}

 

### Rename redundant RSM

Rename-Item RSM $RedundantRSM

 

### Update redundant RSM config file

$tokens = @(

            (new-object psobject -Property @{keyword="BACKUP_PATH";find="\\RSM";replace="\$RedundantRSM"})

            (new-object psobject -Property @{keyword="SERVICE";value="$SiteName.$RedundantRSM"})

            (new-object psobject -Property @{keyword="RSM_NAME";value=$RedundantRSM})

            (new-object psobject -Property @{keyword="REDUNDANT";value="TRUE";disabled=$false})

            (new-object psobject -Property @{keyword="AUD";disabled=$false;value=$AssociatedAUD})

            (new-object psobject -Property @{keyword="ELS";disabled=$false;value=$AssociatedELS})

            )

UpdateConfigFile "$RedundantRSM\Rsm.cfg" $tokens

 

### Update redundant RSM service defs

$xml = New-Object -TypeName XML

$xml.Load("$ServicesDir\$RedundantRSM\SvcDef.rsx")

$xml.rsmdata.servicedefs.SelectNodes("//servicedef") | ? { $_.service -match "ARS" } | % {$_.ParentNode.RemoveChild($_) | out-null }

$xml.Save("$ServicesDir\$RedundantRSM\SvcDef.rsx")

 

### Enable redundancy for remaining services

Get-ChildItem | ? { ($NonRedundantPairs | % { $_.rsm}) -notcontains $_.Name -and ($NonRedundantPairs | % { $_.ars}) -notcontains $_.Name } | Get-ChildItem -recurse | ? { $_.Name -like "*.cfg" } | % {

    $tokens = @(

                (new-object psobject -Property @{keyword="REDUNDANT";value="TRUE";disabled=$false})

                )

    UpdateConfigFile $_.FullName $tokens }

 

### Update site name for all config files (If using default of XXX)

$tokens = [System.Collections.ArrayList]@()

$tokens.Add("XXX#$SiteName")

get-childitem -recurse |? {$_.Extension -eq ".cfg"} | % { FindAndReplace $_.FullName $tokens }

 

 

Write-output "Done"

Share this entry
Share by Mail



Comments

Blog post currently doesn't have any comments.
{{com.name}}           {{com.blogCommentDateFormatted}}

Leave Comment

Please correct the following error(s):

  • {{ error }}

Subscribe to this blog post

Enter your email address to subscribe to this post and receive notifications of new comments by email.


Search the CygNet Blog

Subscribe to the CygNet Blog

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Tags

.NET ‘Sparkline” “.NET “64-bit” “Allen “Azure “Canvas” “CygNet “Data “Database “DDS-based “Dispatch” “DNP3 “Facility “FWRD” “Geometric “HMI “IoT “Lufkin “MQTT “Note “OpenStreetMaps” “Relative “Replication” “SCADA” “Security” “SVG” “Telerik” “Thin “Totalflow “Weatherford 8.5.1 9.3 9.4 9.9” Ad hoc chart Alarms API API” ASR Authentication auto-failover automatic failover automatic service recovery beta program Bradley breakouts Bridge Bridge API broadcast browser-based Bytes Canvas CIP Client Client” Clients” Comm Config File Manager Configuration Context menu Controls controls” CRC custom database indexes Custom Events CygNet CygNet 9.0 CygNet 9.4 CygNet 9.6 CygNet 9.7 CygNet 9.8 CygNet messaging CygNet. Data Data Visualization DEIDs Depot Designer device mapping Diagnostics disaster recovery Dynagraph EAC EIE EIE” email Emerson emitter” Enhanced Enhanced Alarm Configuration Excel Facilities” failover feedback Files first look FMS Full FWRD Get Latest Get New GitHub gns Grid” group group grid Heat Map HMI HSS IoT iPhone Job Runner Link Link” Maps” Measurement Measurement” Messaging” Mobile Mode” Navigation OAuth ODBC On-demand chart OPC OPC UA Polling recovery redundancy reference facilities Reference Packages Release Remote replication Report Module SAM Samples SCADA screen performance Scripting Security Server” Shapes” slides Sniffer SQL Server survey Sync” Tabs Tech Bulletin Template Test” TFA Thin Web Client Token Tools transactions” Tree Map trend Troubleshooting Two-factor UA UIS video View Group Web web-based Well WESC 2016 WESC 2018 WESC 2020 WESC2019