tools/e2etesting/DeployPLCs.ps1 (166 lines of code) (raw):
Param(
[string]
$ResourceGroupName,
[int]
$NumberOfSimulations = 10,
[Guid]
$TenantId,
[int]
$NumberOfSlowNodes = 250,
[int]
$SlowNodeRate = 10,
[string]
$SlowNodeType = "uint",
[int]
$NumberOfFastNodes = 50,
[int]
$FastNodeRate = 1,
[string]
$FastNodeType = "uint",
[string]
$PLCImage,
[string]
$ResourcesPrefix = "e2etesting",
[Double]
$MemoryInGb = 0.5,
[int]
$CpuCount = 1,
[bool]
$UsePrivateIp = $false
)
# Stop execution when an error occurs.
$ErrorActionPreference = "Stop"
if (!$PLCImage) {
$PLCImage = "mcr.microsoft.com/iotedge/opc-plc:latest"
}
if (!$ResourceGroupName) {
Write-Error "ResourceGroupName not set."
}
## Check if resource group exists
$resourceGroup = az group show --name $ResourceGroupName | ConvertFrom-Json
if (!$resourceGroup) {
Write-Error "Could not find Resource Group '$($ResourceGroupName)'."
}
Write-Host "Resource Group: $($ResourceGroupName)"
## Determine suffix for testing resources
$testSuffix = $resourceGroup.tags.TestingResourcesSuffix
if (!$testSuffix) {
$testSuffix = Get-Random -Minimum 10000 -Maximum 99999
# TODO: don't override the original tags
az group update --name $resourceGroup --tags TestingResourcesSuffix=$testSuffix
}
## Check if KeyVault exists
$keyVault = "e2etestingkeyVault" + $testSuffix
$keyVaultList = az keyvault list --resource-group $ResourceGroupName | ConvertFrom-Json
if ($keyVaultList.Count -ne 1){
Write-Error "keyVault could not be automatically selected in Resource Group '$($ResourceGroupName)'."
}
$keyVault = $keyVaultList.name
Write-Host "The following Key Vault has been selected: $keyVault"
## Ensure Azure Container Instances ##
$allAciNames = @()
for ($i = 0; $i -lt $NumberOfSimulations; $i++) {
$name = "$($ResourcesPrefix)-simulation-aci-" + $i.ToString().PadLeft(3, "0") + "-" + $testSuffix
$allAciNames += $name
}
$existingACIs = az container list --resource-group $ResourceGroupName --query "[?starts_with(name,'$ResourcesPrefix') ].name" | ConvertFrom-Json
$aciNamesToCreate = @()
$allAciNames | %{
if (!@($existingACIs).Contains($_)) {
$aciNamesToCreate += $_
}
}
Write-Host "Creating container instances with PLC Image $($PLCImage)..."
$jobs = @()
if ($aciNamesToCreate.Length -gt 0) {
if ($UsePrivateIp -eq $false) {
$script = {
Param($Name)
# create
$ports = @(50000, 80)
az container create --resource-group $using:ResourceGroupName --name $Name --image $using:PLCImage --os-type Linux --ports @ports --cpu $using:CpuCount --memory $using:MemoryInGb --ip-address Public --dns-name-label $Name
$container = az container show --resource-group $using:ResourceGroupName --name $Name | ConvertFrom-Json
if (!$container.ipAddress.ip) {
throw "Create container failed with exit code: $LASTEXITCODE"
}
# update
$aciCommand = "/bin/sh -c './opcplc --ph $($container.ipAddress.fqdn) --cdn $($container.ipAddress.ip) --ctb --pn=50000 --autoaccept --nospikes --nodips --nopostrend --nonegtrend --nodatavalues --sph --wp=80 --sn=$($using:NumberOfSlowNodes) --sr=$($using:SlowNodeRate) --st=$($using:SlowNodeType) --fn=$($using:NumberOfFastNodes) --fr=$($using:FastNodeRate) --ft=$($using:FastNodeType)'"
az container create --resource-group $using:ResourceGroupName --name $Name --image $using:PLCImage --os-type Linux --ports @ports --cpu $using:CpuCount --memory $using:MemoryInGb --ip-address Public --dns-name-label $Name --command $aciCommand
if ($LASTEXITCODE -ne 0) {
az container delete -y --resource-group $using:ResourceGroupName --name $Name
throw "Update container failed with exit code: $LASTEXITCODE"
}
}
}
else {
Write-Host "Creating containers with private IP addresses"
## Set vNet and subNet parameters for nested edge
$networkResourceGroup = $ResourceGroupName + "-RG-network"
$vNet = az resource show --name "PurdueNetwork" --resource-group $networkResourceGroup --resource-type "Microsoft.Network/virtualNetworks" --query id
$subNet = "3-L2-OT-AreaSupervisoryControl"
Write-Host "vNet resource id is $vNet"
$script = {
Param($Name)
#create
$ports = @(50000, 80)
az container create --resource-group $using:ResourceGroupName --name $Name --image $using:PLCImage --os-type Linux --ports @ports --cpu $using:CpuCount --memory $using:MemoryInGb --ip-address Private --vnet $using:vNet --subnet $using:subNet
$container = az container show --resource-group $using:ResourceGroupName --name $Name | ConvertFrom-Json
if (!$container.ipAddress.ip) {
throw "Create container failed with exit code: $LASTEXITCODE"
}
# update
$aciCommand = "/bin/sh -c './opcplc --ph $($container.ipAddress.fqdn) --cdn $($container.ipAddress.ip) --ctb --pn=50000 --autoaccept --nospikes --nodips --nopostrend --nonegtrend --nodatavalues --sph --wp=80 --sn=$($using:NumberOfSlowNodes) --sr=$($using:SlowNodeRate) --st=$($using:SlowNodeType) --fn=$($using:NumberOfFastNodes) --fr=$($using:FastNodeRate) --ft=$($using:FastNodeType)'"
az container create --resource-group $using:ResourceGroupName --name $Name --image $using:PLCImage --os-type Linux --ports @ports --cpu $using:CpuCount --memory $using:MemoryInGb --ip-address Private --vnet $using:vNet --subnet $using:subNet --command $aciCommand
if ($LASTEXITCODE -ne 0) {
az container delete -y --resource-group $using:ResourceGroupName --name $Name
throw "Update container failed with exit code: $LASTEXITCODE"
}
}
}
foreach ($aciNameToCreate in $aciNamesToCreate) {
Write-Host "Creating ACI $($aciNameToCreate)..."
$job = Start-Job -Scriptblock $script -ArgumentList $aciNameToCreate
$jobs += $job
}
Write-Host "Waiting for deployments to finish..."
$working = $true
while($working) {
$working = $false
foreach ($job in $jobs) {
if ($job.JobStateInfo.State -eq 'Running') {
$working = $true
break
}
}
Start-Sleep -Seconds 1
}
Write-Host "Deployment finished."
Wait-Job -Job $jobs | Out-Null
foreach ($job in $jobs) {
if ($job.JobStateInfo.State -ne 'Completed') {
Receive-Job -Job $job
Write-Error "Error while deploying ACI."
}
}
}
## Write ACI FQDNs and ips to KeyVault ##
Write-Host
Write-Host "Getting IPs of ACIs for simulated PLCs..."
$fqdnList = az container list --resource-group $ResourceGroupName --query "[?starts_with(name,'$ResourcesPrefix') ].ipAddress.fqdn" | ConvertFrom-Json
$ipList = az container list --resource-group $ResourceGroupName --query "[?starts_with(name,'$ResourcesPrefix') ].ipAddress.ip" | ConvertFrom-Json
if ($ipList.Count -eq 0) {
Write-Error "No Azure Container Instances have been deployed. Please check that quota is not exceeded for this region."
}
foreach ($ip in $ipList) {
Write-Host $ip
$plcSimIps += $ip + ";"
}
foreach ($fqdn in $fqdnList) {
Write-Host $ip
$plcSimNames += $fqdn + ";"
}
Write-Host "Adding/Updating KeyVault-Secret 'plc-simulation-urls' with value '$($plcSimNames)'..."
az keyvault secret set --vault-name $keyVault --name "plc-simulation-urls" --value $plcSimNames > $null
Write-Host "Adding/Updating KeyVault-Secret 'plc-simulation-ips' with value '$($plcSimIps)'..."
az keyvault secret set --vault-name $keyVault --name "plc-simulation-ips" --value $plcSimIps > $null
Write-Host "Deployment finished."