Files

225 lines
6.5 KiB
PowerShell

<#
DISKSPD - VM Fleet
Copyright(c) Microsoft Corporation
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#>
#
# VM Master Script
#
# This script must be executed on autologon by the VM. It establishes a mapping a location containing the Run Script
# and then repeatedly polls/executes the latest one it finds. This allows the runner to inject new run scripts on the
# fly and/or touch pre-existing ones to bump them to the top of the list.
#
# In the initial form, it assumes the VM is connected by an internal switch and it just polls all the node names to
# find the one which will work. This could be improved by using specialization to inject the specific path to a
# configuration file.
#
param(
[string] $connectuser,
[string] $connectpass
)
$null = net use l: /d
if ($(Get-SmbMapping) -eq $null) {
New-SmbMapping -LocalPath l: -RemotePath \\169.254.1.1\c$\clusterstorage\collect\control -UserName $connectuser -Password $connectpass -ErrorAction SilentlyContinue
}
# update tooling
cp l:\tools\* c:\run -Force
$run = 'c:\run\run.ps1'
$master = 'c:\run\master.ps1'
$mypause = "l:\flag\pause-$(gc c:\vmspec.txt)"
$mydone = "l:\flag\done-$(gc c:\vmspec.txt)"
$done = $false
$donetouched = $false
$pause = $false
$tick = 0
function get-newfile(
$sourcepat,
$dest,
$clean = $false,
$silent = $false
)
{
$sf = dir $sourcepat | sort -Property LastWriteTime -Descending | select -first 1
$df = gi $dest -ErrorAction SilentlyContinue
# no source?
if ($sf -eq $null) {
write-host -fore green NO source present
# clean if needed
if ($clean) {
rm $dest
}
# no new file is an update condition
$true
} elseif ($df -eq $null -or $sf.lastwritetime -gt $df.lastwritetime) {
write-host -fore green NEWER source $sf.fullname '=>' $dest
# update with newer source file and indicate update condition
cp $sf.fullname $dest -Force
$true
} else {
if (-not $silent) {
write-host -fore green NO newer source $sf.lastwritetime $sf.fullname '**' $df.LastWriteTime $df.fullname
}
# already have latest, indicate no new
$false
}
}
function get-flagfile(
[string] $flag,
[switch] $gc = $false
)
{
if ($gc) {
gc "l:\flag\$flag" -ErrorAction SilentlyContinue
} else {
gi "l:\flag\$flag" -ErrorAction SilentlyContinue
}
}
while ($true) {
# update master control?
# assume runners have a simple loop to re-execute on exit
if (get-newfile l:\master*.ps1 $master -silent:$true) {
break
}
# check and acknowledge pause - only drop flag once
$pauseepoch = get-flagfile pause -gc:$true
if ($pauseepoch -ne $null) {
# pause clears done
$done = $false
# drop into pause flagfile if needed
if ($pause -eq $false) {
write-host -fore red PAUSE IN FORCE
$pause = $true
echo $pauseepoch > $mypause
} else {
if ($tick % 10 -eq 0) {
write-host -fore red -NoNewline '.'
}
}
} elseif ($done) {
# drop epoch into done flagfile if needed
if (-not $donetouched) {
echo $goepoch > $mydone
$donetouched = $true
}
# if go flag is now different, release for the next go around
if ($goepoch -ne (get-flagfile go -gc:$true)) {
$done = $false
write-host -fore cyan `nReleasing from Done
} else {
if ($tick % 10 -eq 0) {
write-host -fore yellow -NoNewline '.'
}
}
} else {
# clear pause & donetouched flags
$pause = $false
$donetouched = $false
# update run script?
$null = get-newfile l:\run*.ps1 $run -clean:$true
$runf = gi $run -ErrorAction SilentlyContinue
if ($runf -eq $null) {
write-host -fore yellow no control file found
sleep 30
continue
}
# update go epoch - a change to this (if present) will be what
# allows us to proceed past a done flag
$goepoch = get-flagfile go -gc:$true
if ($goepoch -ne $null) {
write-host -fore cyan Go Epoch acknowledged at: $goepoch
}
# acknowledge script
write-host -fore cyan Run Script $runf.lastwritetime "`n$("*"*20)"
gc $runf
write-host -fore cyan ("*"*20)
# launch and monitor pause and new run file
$j = start-job -arg $run { param($run) & $run }
while (($jf = wait-job $j -Timeout 1) -eq $null) {
$halt = $null
# check pause or new run file: if so, stop and loop
if (get-flagfile pause) {
$halt = "pause set"
}
if (get-newfile l:\run*.ps1 $run -clean:$true -silent:$true) {
$halt = "new run file"
}
if ($halt -ne $null) {
write-host -fore yellow STOPPING CURRENT "(reason: $halt)"
$j | stop-job
$j | remove-job
break
}
}
# job finished?
if ($jf -ne $null) {
$result = $jf | receive-job
if ($result -eq "done") {
write-host -fore yellow DONE CURRENT
$done = $true
}
$jf | remove-job
}
write-host -fore cyan ("*"*20)
}
# force gc to teardown potentially conflicting handles and enforce min pause
[system.gc]::Collect()
sleep 1
$tick += 1
}