diff --git a/build/charts/antrea-windows/conf/Run-AntreaAgent.ps1 b/build/charts/antrea-windows/conf/Run-AntreaAgent.ps1 index bf86ccb5c80..2e1d9a8b987 100644 --- a/build/charts/antrea-windows/conf/Run-AntreaAgent.ps1 +++ b/build/charts/antrea-windows/conf/Run-AntreaAgent.ps1 @@ -1,5 +1,5 @@ $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') -$env:PATH = $env:PATH + ";$mountPath/Windows/System32;$mountPath/k/antrea/bin;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" +$env:PATH = $env:PATH + ";$mountPath/k/antrea/bin;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" & antrea-agent --config=$mountPath/etc/antrea/antrea-agent.conf --logtostderr=false --log_dir=c:/var/log/antrea --alsologtostderr --log_file_max_size=100 --log_file_max_num=4 --v=0 diff --git a/build/charts/antrea-windows/conf/ovs/Install-OVSDriver.ps1 b/build/charts/antrea-windows/conf/ovs/Install-OVSDriver.ps1 index 63dd1089e9b..152b485ca62 100644 --- a/build/charts/antrea-windows/conf/ovs/Install-OVSDriver.ps1 +++ b/build/charts/antrea-windows/conf/ovs/Install-OVSDriver.ps1 @@ -1,37 +1,17 @@ $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') -$OVSDriverDir = "$mountPath\openvswitch\driver" - -# Check if OVSExt driver is already installed -$driverStatus = netcfg -q ovsext -if ($driverStatus -like '*not installed*') { - # Install OVS Driver - $result = netcfg -l $OVSDriverDir/ovsext.inf -c s -i OVSExt - if ($result -like '*failed*') { - Write-Host "Failed to install OVSExt driver: $result" - exit 1 - } - Write-Host "OVSExt driver has been installed" +$OVSInstallScript = "$mountPath\k\antrea\Install-OVS.ps1" +if (-not (Test-Path $OVSInstallScript)) { + Write-Host "Installation script not found: $OVSInstallScript, you may be using an invalid antrea-windows container image" + exit 1 } - -# Check if the VC redistributable is already installed. -$OVSRedistDir="$mountPath\openvswitch\redist" -if (Test-Path $OVSRedistDir) { - $dllFound = $false - $paths = $env:PATH -split ';' - foreach ($path in $paths) { - $dllFiles = Get-ChildItem -Path $path -Filter "vcruntime*.dll" -File -ErrorAction SilentlyContinue - if ($dllFiles.Count -gt 0) { - $dllFound = $true - break - } - } - - # vcruntime dlls are not installed on the host, then install the binaries. - if (-not $dllFound) { - Get-ChildItem $OVSRedistDir -Filter *.exe | ForEach-Object { - Start-Process -FilePath $_.FullName -Args '/install /passive /norestart' -Verb RunAs -Wait - } - } +$installOVSJob = Start-Job -FilePath $OVSInstallScript -ArgumentList "","","$mountPath/openvswitch",$false,"" +$installOVSJob | Receive-Job -Wait +Remove-Job -Id $installOVSJob.Id +$state = $installOVSJob.State +if ($state -ne "Completed") { + Write-Host "Failed to install OVS with state: $state" + exit 1 } +Write-Host "Completed to install OVS" diff --git a/build/charts/antrea-windows/conf/ovs/Run-AntreaOVS.ps1 b/build/charts/antrea-windows/conf/ovs/Run-AntreaOVS.ps1 index ee0747ada51..50032c29604 100644 --- a/build/charts/antrea-windows/conf/ovs/Run-AntreaOVS.ps1 +++ b/build/charts/antrea-windows/conf/ovs/Run-AntreaOVS.ps1 @@ -1,7 +1,7 @@ $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') -$env:PATH = $env:PATH + ";$mountPath/Windows/System32;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" +$env:PATH = $env:PATH + ";$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" $OVSDriverDir = "$mountPath\openvswitch\driver" # Configure OVS processes diff --git a/build/images/Dockerfile.build.windows b/build/images/Dockerfile.build.windows index 24cab356715..730cfdc4aef 100644 --- a/build/images/Dockerfile.build.windows +++ b/build/images/Dockerfile.build.windows @@ -40,7 +40,8 @@ RUN --mount=type=cache,target=/go/pkg/mod/ \ RUN mkdir -p /go/k/antrea/bin && \ cp /antrea/bin/antrea-agent.exe /go/k/antrea/bin/ && \ cp /antrea/bin/antctl.exe /go/k/antrea/bin/ && \ - cp /antrea/bin/antrea-cni.exe /go/k/antrea/cni/antrea.exe + cp /antrea/bin/antrea-cni.exe /go/k/antrea/cni/antrea.exe && \ + cp /antrea/hack/windows/Install-OVS.ps1 /go/k/antrea/ FROM antrea/windows-ovs:${OVS_VERSION} as antrea-ovs diff --git a/build/yamls/antrea-windows-with-ovs.yml b/build/yamls/antrea-windows-with-ovs.yml index fb4d7d69748..9e4ca2cb43f 100644 --- a/build/yamls/antrea-windows-with-ovs.yml +++ b/build/yamls/antrea-windows-with-ovs.yml @@ -40,51 +40,31 @@ data: $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') - $env:PATH = $env:PATH + ";$mountPath/Windows/System32;$mountPath/k/antrea/bin;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" + $env:PATH = $env:PATH + ";$mountPath/k/antrea/bin;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" & antrea-agent --config=$mountPath/etc/antrea/antrea-agent.conf --logtostderr=false --log_dir=c:/var/log/antrea --alsologtostderr --log_file_max_size=100 --log_file_max_num=4 --v=0 Install-OVSDriver.ps1: | $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') - $OVSDriverDir = "$mountPath\openvswitch\driver" - - # Check if OVSExt driver is already installed - $driverStatus = netcfg -q ovsext - if ($driverStatus -like '*not installed*') { - # Install OVS Driver - $result = netcfg -l $OVSDriverDir/ovsext.inf -c s -i OVSExt - if ($result -like '*failed*') { - Write-Host "Failed to install OVSExt driver: $result" - exit 1 - } - Write-Host "OVSExt driver has been installed" + $OVSInstallScript = "$mountPath\k\antrea\Install-OVS.ps1" + if (-not (Test-Path $OVSInstallScript)) { + Write-Host "Installation script not found: $OVSInstallScript, you may be using an invalid antrea-windows container image" + exit 1 } - - # Check if the VC redistributable is already installed. - $OVSRedistDir="$mountPath\openvswitch\redist" - if (Test-Path $OVSRedistDir) { - $dllFound = $false - $paths = $env:PATH -split ';' - foreach ($path in $paths) { - $dllFiles = Get-ChildItem -Path $path -Filter "vcruntime*.dll" -File -ErrorAction SilentlyContinue - if ($dllFiles.Count -gt 0) { - $dllFound = $true - break - } - } - - # vcruntime dlls are not installed on the host, then install the binaries. - if (-not $dllFound) { - Get-ChildItem $OVSRedistDir -Filter *.exe | ForEach-Object { - Start-Process -FilePath $_.FullName -Args '/install /passive /norestart' -Verb RunAs -Wait - } - } + $installOVSJob = Start-Job -FilePath $OVSInstallScript -ArgumentList "","","$mountPath/openvswitch",$false,"" + $installOVSJob | Receive-Job -Wait + Remove-Job -Id $installOVSJob.Id + $state = $installOVSJob.State + if ($state -ne "Completed") { + Write-Host "Failed to install OVS with state: $state" + exit 1 } + Write-Host "Completed to install OVS" Run-AntreaOVS.ps1: | $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') - $env:PATH = $env:PATH + ";$mountPath/Windows/System32;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" + $env:PATH = $env:PATH + ";$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" $OVSDriverDir = "$mountPath\openvswitch\driver" # Configure OVS processes @@ -328,7 +308,7 @@ spec: template: metadata: annotations: - checksum/agent-windows: 5efe6525007ef87c58914b37d190f84bc93b8cf081d204979dffce0859ee2da3 + checksum/agent-windows: 81b816813eb399e0c0d53fb7b9fb75883e244c1ab8418c1bdf29ccd21a981fe6 checksum/windows-config: 10ad2be0a04b1752abc224fed0124f7b1da36efc5e7323e193eb38e11b25e798 microsoft.com/hostprocess-inherit-user: "true" labels: diff --git a/build/yamls/antrea-windows.yml b/build/yamls/antrea-windows.yml index 447cea20b6d..00d330405b7 100644 --- a/build/yamls/antrea-windows.yml +++ b/build/yamls/antrea-windows.yml @@ -40,7 +40,7 @@ data: $ErrorActionPreference = "Stop" $mountPath = $env:CONTAINER_SANDBOX_MOUNT_POINT $mountPath = ($mountPath.Replace('\', '/')).TrimEnd('/') - $env:PATH = $env:PATH + ";$mountPath/Windows/System32;$mountPath/k/antrea/bin;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" + $env:PATH = $env:PATH + ";$mountPath/k/antrea/bin;$mountPath/openvswitch/usr/bin;$mountPath/openvswitch/usr/sbin" & antrea-agent --config=$mountPath/etc/antrea/antrea-agent.conf --logtostderr=false --log_dir=c:/var/log/antrea --alsologtostderr --log_file_max_size=100 --log_file_max_num=4 --v=0 kind: ConfigMap metadata: @@ -233,7 +233,7 @@ spec: template: metadata: annotations: - checksum/agent-windows: adb135c962fe85e0a2bc86a45f4b8c72d89b09a1da35bb16775e547813295679 + checksum/agent-windows: 63f16e1fadb6b1354efda21c73702b4290400181136d4d47d4b1cd6a5f82d037 checksum/windows-config: 10ad2be0a04b1752abc224fed0124f7b1da36efc5e7323e193eb38e11b25e798 microsoft.com/hostprocess-inherit-user: "true" labels: diff --git a/docs/windows.md b/docs/windows.md index 810903d2c66..aec473aabec 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -170,12 +170,12 @@ depending on whether you are using your own [signed](https://docs.microsoft.com/ OVS kernel driver or you want to use the test-signed driver provided by Antrea, you will need to invoke the `Install-OVS.ps1` script differently (or not at all). -| Containerized OVS daemons? | Test-signed OVS driver? | Run this command | -| -------------------------- | ----------------------- | ---------------- | -| Yes | Yes | `.\Install-OVS.ps1 -InstallUserspace $false` | -| Yes | No | N/A | -| No | Yes | `.\Install-OVS.ps1` | -| No | No | `.\Install-OVS.ps1 -ImportCertificate $false -Local -LocalFile ` | +| Containerized OVS daemons? | Test-signed OVS driver? | Run this command | +| -------------------------- | ----------------------- |---------------------------------------------------------------------------| +| Yes | Yes | N/A | +| Yes | No | N/A | +| No | Yes | `.\Install-OVS.ps1 -InstallUserspace $true` | +| No | No | `.\Install-OVS.ps1 -InstallUserspace $true -LocalFile ` | If you used `antrea-windows-with-ovs.yml` to create the antrea-agent Windows DaemonSet, then you are using "Containerized OVS daemons". For all other @@ -208,7 +208,7 @@ your own OVS package with a signed OVS kernel driver, you would run: ```powershell curl.exe -LO https://raw.githubusercontent.com/antrea-io/antrea/main/hack/windows/Install-OVS.ps1 -.\Install-OVS.ps1 -ImportCertificate $false -Local -LocalFile +.\Install-OVS.ps1 -InstallUserspace $true -LocalFile # verify that the OVS services are installed get-service ovsdb-server diff --git a/hack/windows/Install-OVS.ps1 b/hack/windows/Install-OVS.ps1 index 1086fc22d81..c47a39b366a 100644 --- a/hack/windows/Install-OVS.ps1 +++ b/hack/windows/Install-OVS.ps1 @@ -5,22 +5,12 @@ .PARAMETER DownloadURL The URL of the OpenvSwitch package to be downloaded. - .PARAMETER DownloadDir - The path of the directory to be used to download OpenvSwitch package. The default path is the working directory. - .PARAMETER OVSInstallDir The target installation directory. The default path is "C:\openvswitch". - .PARAMETER CheckFileHash - Skips checking file hash. The default value is true. - .PARAMETER LocalFile Specifies the path of a local OpenvSwitch package to be used for installation. - When the param is used, "DownloadURL" and "DownloadDir" params will be ignored. - - .PARAMETER ImportCertificate - Specifies if a certificate file is needed for OVS package. If true, certificate - will be retrieved from OVSExt.sys and a package.cer file will be generated. + When the param is used, "DownloadURL" is ignored. .PARAMETER InstallUserspace Specifies whether OVS userspace processes are included in the installation. If false, these processes will not @@ -30,64 +20,45 @@ Specifies the path of a local SSL package to be used for installation. #> Param( - [parameter(Mandatory = $false)] [string] $DownloadDir, [parameter(Mandatory = $false)] [string] $DownloadURL, [parameter(Mandatory = $false)] [string] $OVSInstallDir = "C:\openvswitch", - [parameter(Mandatory = $false)] [bool] $CheckFileHash = $true, [parameter(Mandatory = $false)] [string] $LocalFile, - [parameter(Mandatory = $false)] [bool] $ImportCertificate = $true, [parameter(Mandatory = $false)] [bool] $InstallUserspace = $true, [parameter(Mandatory = $false)] [string] $LocalSSLFile ) $ErrorActionPreference = "Stop" -$OVSDownloadURL = "https://downloads.antrea.io/ovs/ovs-3.0.5-antrea.1-win64.zip" +$DefaultOVSDownloadURL = "https://downloads.antrea.io/ovs/ovs-3.0.5-antrea.1-win64.zip" # Use a SHA256 hash to ensure that the downloaded archive is correct. -$OVSPublishedHash = '813a0c32067f40ce4aca9ceb7cd745a120e26906e9266d13cc8bf75b147bb6a5' -$WorkDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition) -$OVSDownloadDir = $WorkDir -$PowerShellModuleBase = "C:\Windows\System32\WindowsPowerShell\v1.0\Modules" - -if (!$LocalFile) { - $OVSZip = "$OVSDownloadDir\ovs-win64.zip" -} else { - $OVSZip = $LocalFile - $DownloadDir = Split-Path -Path $LocalFile -} - -if ($DownloadDir -ne "") { - $OVSDownloadDir = $DownloadDir -} - -$InstallLog = "$OVSDownloadDir\install_ovs.log" - -if ($DownloadURL -ne "") { - $OVSDownloadURL = $DownloadURL - # For user-provided URLs, do not verify the hash for the archive. - $OVSPublishedHash = "" +$DefaultOVSPublishedHash = '813a0c32067f40ce4aca9ceb7cd745a120e26906e9266d13cc8bf75b147bb6a5' +# $MininalVCRedistVersion is the minimal version required by the provided Windows OVS binary. If a higher +# version of VC redistributable file exists on the Windows host, we can skip the installation. +$MininalVCRedistVersion="14.12.25810" + +$invocationName=$($myInvocation.MyCommand.Name) +# If the script is called via a Windows Job, the $myInvocation.MyCommand.Definition is the script block. +# Then it may throw exceptions when called by GetDirectoryName. Use host temp directory as "$WorkDir" instead. +try { + $WorkDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition) +} catch { + Write-Host "Failed to get the script locations, use temp path as instead" + $WorkDir = $env:Temp } +$InstallLog = "$WorkDir\install_ovs.log" +$PowerShellModuleBase = "C:\Windows\System32\WindowsPowerShell\v1.0\Modules" +$OVSZip="" +$DefaultOVSRunDir = "C:\openvswitch\var\run\openvswitch" +# $global:updateDriver is a flag to mark if the OVSext driver needs to update on the target Windows host or not. +# We also use it as a flag to copy directory "openvswitch/usr/bin" from container to host path c:/openvswitch/, +# in which the OVS utilities are provided, e.g., ovs-vsctl.exe, ovs-ofctl.exe, ovs-appctl.exe. These OVS +# utilitiese are needed with local trouble shooting. +$global:updateDriver=$false function Log($Info) { $time = $(get-date -Format g) "$time $Info `n`r" | Tee-Object $InstallLog -Append | Write-Host } -function CreatePath($Path){ - if ($(Test-Path $Path)) { - mv $Path $($Path + "_bak") - } - mkdir -p $Path | Out-Null -} - -function SetEnvVar($key, $value) { - [Environment]::SetEnvironmentVariable($key, $value, [EnvironmentVariableTarget]::Machine) -} - -function WaitExpandFiles($Src, $Dest) { - Log "Extract $Src to $Dest" - Expand-Archive -Path $Src -DestinationPath $Dest | Out-Null -} - function ServiceExists($ServiceName) { If (Get-Service $ServiceName -ErrorAction SilentlyContinue) { return $true @@ -107,24 +78,21 @@ function CheckIfOVSInstalled() { } function DownloadOVS() { - if ($LocalFile -ne "") { - Log "Skipping OVS download, using local file: $LocalFile" - return - } - - If (!(Test-Path $OVSDownloadDir)) { - mkdir -p $OVSDownloadDir - } - Log "Downloading OVS package from $OVSDownloadURL to $OVSZip" - curl.exe -sLo $OVSZip $OVSDownloadURL + param ( + [parameter(Mandatory = $true)] [string] $localZipFile, + [parameter(Mandatory = $true)] [string] $downloadURL, + [parameter(Mandatory = $true)] [string] $desiredHash + ) + Log "Downloading OVS package from $downloadURL to $localZipFile" + curl.exe -sLo $localZipFile $downloadURL If (!$?) { - Log "Download OVS failed, URL: $OVSDownloadURL" + Log "Download OVS failed, URL: $downloadURL" exit 1 } - if ($CheckFileHash) { - $FileHash = Get-FileHash $OVSZip - If ($OVSPublishedHash -ne "" -And $FileHash.Hash -ne $OVSPublishedHash) { + if ($desiredHash -ne "invalid") { + $fileHash = Get-FileHash $localZipFile + If ($fileHash.Hash -ne $desiredHash) { Log "SHA256 mismatch for OVS download" exit 1 } @@ -133,51 +101,25 @@ function DownloadOVS() { Log "Download OVS package success." } -function InstallOVS() { - # unzip OVS. - WaitExpandFiles $OVSZip $OVSDownloadDir - # Copy OVS package to target dir. - Log "Copying OVS package from $OVSDownloadDir\openvswitch to $OVSInstallDir" - mv "$OVSDownloadDir\openvswitch" $OVSInstallDir - if (!$LocalFile) { - rm $OVSZip - } - # Create log and run dir. - $OVS_LOG_PATH = $OVSInstallDir + "\var\log\openvswitch" - CreatePath $OVS_LOG_PATH - $OVSRunDir = $OVSInstallDir + "\var\run\openvswitch" - CreatePath $OVSRunDir - $OVSDriverDir = "$OVSInstallDir\driver" - - # Install OVS driver certificate. - $DriverFile="$OVSDriverDir\OVSExt.sys" - if ($ImportCertificate) { - $CertificateFile = "$OVSDriverDir\package.cer" - if (!(Test-Path $CertificateFile)) { - Log "No existing OVS driver certificate found, generating a new one." - $ExportType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert; - $Cert = (Get-AuthenticodeSignature $DriverFile).SignerCertificate; - [System.IO.File]::WriteAllBytes($CertificateFile, $Cert.Export($ExportType)); - } - Log "Installing OVS driver certificate." - Import-Certificate -FilePath "$CertificateFile" -CertStoreLocation cert:\LocalMachine\TrustedPublisher - Import-Certificate -FilePath "$CertificateFile" -CertStoreLocation cert:\LocalMachine\Root - } - - # Install Microsoft Visual C++ Redistributable Package. - if (Test-Path $OVSInstallDir\redist) { - Log "Installing Microsoft Visual C++ Redistributable Package." - $RedistFiles = Get-ChildItem "$OVSInstallDir\redist" -Filter *.exe - $RedistFiles | ForEach-Object { - Log "Installing $_" - Start-Process -FilePath $_.FullName -Args '/install /passive /norestart' -Verb RunAs -Wait - } +function AddToEnvPath(){ + param ( + [Parameter(Mandatory = $true)] [String]$path + ) + $envPaths = $env:Path -split ";" | Select-Object -Unique + if (-not $envPaths.Contains($path)) { + $envPaths += $path } + $env:Path = [system.String]::Join(";", $envPaths) + [Environment]::SetEnvironmentVariable("Path", $env:Path, [EnvironmentVariableTarget]::Machine) +} - # Install powershell modules - if (Test-Path $OVSInstallDir\scripts) { +function CheckAndInstallScripts { + param ( + [Parameter(Mandatory = $true)] [String]$OVSScriptsPath + ) + if (Test-Path $OVSScriptsPath) { Log "Installing powershell modules." - $PSModuleFiles = Get-ChildItem "$OVSInstallDir\scripts" -Filter *.psm1 + $PSModuleFiles = Get-ChildItem "$OVSScriptsPath" -Filter *.psm1 $PSModuleFiles | ForEach-Object { $PSModulePath = Join-Path -Path $PowerShellModuleBase -ChildPath $_.BaseName if (!(Test-Path $PSModulePath)) { @@ -187,33 +129,268 @@ function InstallOVS() { } } } +} - # Install OVS kernel driver. - Log "Installing OVS kernel driver" - $VMMSStatus = $(Get-Service vmms -ErrorAction SilentlyContinue).Status - if (!$VMMSStatus) { - $VMMSStatus = "not exist" +function CheckAndInstallVCRedists { + param ( + [Parameter(Mandatory = $true)] [String]$VCRedistPath, + [Parameter(Mandatory = $true)] [String]$VCRedistsVersion + ) + $mininalVersion = [version]$VCRedistsVersion + $existingVCRedists = getInstalledVcRedists + foreach ($redist in $existingVCRedists) { + $installedVersion = [version]$redist.Version + # VC redists files with a higher version are installed, return. + if ($installedVersion -ge $mininalVersion) { + return + } } - Log "Hyper-V Virtual Machine Management service status: $VMMSStatus" - if ($VMMSStatus -eq "Running") { - cmd /c "cd $OVSDriverDir && install.cmd" - } else { - cd $OVSDriverDir ; netcfg -l .\ovsext.inf -c s -i OVSExt; cd $WorkDir + # Install the provided VC redistributable files. + Get-ChildItem $VCRedistPath -Filter *.exe | ForEach-Object { + Start-Process -FilePath $_.FullName -Args '/install /passive /norestart' -Verb RunAs -Wait } - if (!$?) { - Log "Install OVS kernel driver failed, exit" +} + +function CheckAndInstallOVSDriver { + param ( + [Parameter(Mandatory = $true)] + [String]$OVSDriverPath + ) + + $expVersion = [version]$(Get-Item $OVSDriverPath\ovsext.sys).VersionInfo.ProductVersion + $ovsInstalled = $(netcfg -q ovsext) -like "*is installed*" + $installedDrivers = getInstalledOVSDrivers + + # OVSext driver with the desired version is already installed, return + if ($ovsInstalled -and (@($installedDrivers).Length -eq 1) -and ($installedDrivers[0].DriverVersion -eq $expVersion)){ + return + } + + $global:updateDriver = $true + # Uninstall the existing driver which is with a different version. + if ($ovsInstalled) { + netcfg -u ovsext + } + + # Clean up the installed ovsext drivers packages. + foreach ($driver in $installedDrivers) { + $publishdName = $driver.PublishedName + pnputil.exe -d $publishdName + } + + # Import OVSext driver certificate to TrustedPublisher and Root. + $DriverFile="$OVSDriverPath\ovsext.sys" + $CertificateFile = "$OVSDriverPath\package.cer" + $ExportType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert + $Cert = (Get-AuthenticodeSignature $DriverFile).SignerCertificate + [System.IO.File]::WriteAllBytes($CertificateFile, $Cert.Export($ExportType)) + Import-Certificate -FilePath "$CertificateFile" -CertStoreLocation cert:\LocalMachine\TrustedPublisher + Import-Certificate -FilePath "$CertificateFile" -CertStoreLocation cert:\LocalMachine\Root + + # Install the OVSext driver with the desired version + # Copy $OVSDriverPath to host path "c:/openvwitch/driver", and then install ovsext.inf with the host path. + # This is a workaround for error code "0x80070003" with containerd v1.7+. The error happens when Windows + # utitilty netcfg tries to access the container's mount path "C:/hpc/". + $hostDriverPath="C:\openvswitch\driver" + if ($OVSDriverPath -ne $hostDriverPath){ + if (Test-Path $hostDriverPath) { + rm -r $hostDriverPath + } + mkdir -p $hostDriverPath + cp -r $OVSDriverPath\* $hostDriverPath\ + } + $result = netcfg -l $hostDriverPath\ovsext.inf -c s -i OVSExt + + if ($result -like '*failed*') { + Write-Host "Failed to install OVSExt driver: $result" exit 1 } - $OVS_BIN_PATH="$OVSInstallDir\usr\bin;$OVSInstallDir\usr\sbin" - $env:Path += ";$OVS_BIN_PATH" - SetEnvVar "Path" $env:Path + Log "OVSExt driver has been installed" +} + +function getInstalledVcRedists { + # Get all installed Visual C++ Redistributables installed components + $VcRedists = listInstalledSoftware -SoftwareLike 'Microsoft Visual C++' + + # Add Architecture property to each entry + $VcRedists | ForEach-Object { If ( $_.Name.ToLower().Contains("x64") ) ` + { $_ | Add-Member -NotePropertyName "Architecture" -NotePropertyValue "x64" } } + + return $vcRedists } -function InstallDependency() { +function listInstalledSoftware { + param ( + [parameter(Mandatory = $false)] [string] $SoftwareLike + ) + Begin { + $SoftwareOutput = @() + $InstalledSoftware = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*) + } + Process { + Try + { + if ($SoftwareLike -ne "") { + $nameFilter = "${SoftwareLike}*" + $InstalledSoftware = $InstalledSoftware | + Where-Object {$_.DisplayName -like "$nameFilter"} + } + + $SoftwareOutput = $InstalledSoftware | + Select-Object -Property @{ + Name = 'Date Installed' + Exp = { + $_.Installdate + } + }, @{ + Name = 'Version' + Exp = { + $_.DisplayVersion + } + }, @{ + Name = 'Name' + Exp = { + $_.DisplayName + } + }, UninstallString + } + Catch + { + # get error record + [Management.Automation.ErrorRecord]$e = $_ + + # retrieve information about runtime error + $info = New-Object -TypeName PSObject -Property @{ + Exception = $e.Exception.Message + Reason = $e.CategoryInfo.Reason + Target = $e.CategoryInfo.TargetName + Script = $e.InvocationInfo.ScriptName + Line = $e.InvocationInfo.ScriptLineNumber + Column = $e.InvocationInfo.OffsetInLine + } + + # output information. Post-process collected info, and log info (optional) + $info + } + } + + End{ + $SoftwareOutput | Sort-Object -Property Name + } +} + +# getInstalledOVSDrivers lists the existing drivers on Windows host, and uses "ovsext" as a filter +# on the "OriginalName" field of the drivers. As the output of "pnputil.exe" is not structured, the +# function translates to structured objects first, and then apply the filter. +# +# A sample of the command output is like this, +# +# $ pnputil.exe /enum-drivers +# Microsoft PnP Utility +# +# Published Name: oem3.inf +# Original Name: efifw.inf +# Provider Name: VMware, Inc. +# Class Name: Firmware +# Class GUID: {f2e7dd72-6468-4e36-b6f1-6488f42c1b52} +# Driver Version: 04/24/2017 1.0.0.0 +# Signer Name: Microsoft Windows Hardware Compatibility Publisher +# +# Published Name: oem5.inf +# Original Name: pvscsi.inf +# Provider Name: VMware, Inc. +# Class Name: Storage controllers +# Class GUID: {4d36e97b-e325-11ce-bfc1-08002be10318} +# Driver Version: 04/06/2018 1.3.10.0 +# Signer Name: Microsoft Windows Hardware Compatibility Publishe +# +# Published Name: oem9.inf +# Original Name: vmci.inf +# Provider Name: VMware, Inc. +# Class Name: System devices +# Class GUID: {4d36e97d-e325-11ce-bfc1-08002be10318} +# Driver Version: 07/11/2019 9.8.16.0 +# Signer Name: Microsoft Windows Hardware Compatibility Publisher +# +function getInstalledOVSDrivers { + $pnputilOutput = pnputil.exe /enum-drivers + $drivers = @() + $lines = $pnputilOutput -split "`r`n" + $driverlines = @() + foreach ($line in $lines) { + # Ignore the title line "Microsoft PnP Utility" from the output. + if ($line -like "*Microsoft PnP Utility*") { + continue + } + if ($line.Trim() -eq "") { + if ($driverlines.Count -gt 0) { + $driver = $(parseDriver $driverlines) + $drivers += $driver + $driverlines = @() + } + continue + } + $driverlines += $line + } + if ($driverlines.Count -gt 0) { + $driver = parseDriver $driverlines + $drivers += $driver + } + $drivers = $drivers | Where-Object { $_.OriginalName -like "ovsext*"} + return $drivers +} + +function parseDriver { + param ( + [String[]]$driverlines + ) + $driver = [PSCustomObject]@{ + PublishedName = $null + ProviderName = $null + ClassName = $null + DriverVersion = $null + InstalledDate = $null + SignerName = $null + ClassGUID = $null + OriginalName = $null + } + $driverlines | ForEach-Object { + if ($_ -match "Published Name\s*:\s*(.+)") { + $driver.PublishedName = $matches[1].Trim() + } + elseif ($_ -match "Provider Name\s*:\s*(.+)") { + $driver.ProviderName = $matches[1].Trim() + } + elseif ($_ -match "Class Name\s*:\s*(.+)") { + $driver.ClassName = $matches[1].Trim() + } + elseif ($_ -match "Driver Version\s*:\s*(.+)") { + $dateAndVersion = $matches[1].Trim() -split " " + $driver.DriverVersion = [version]$dateAndVersion[1] + $driver.InstalledDate = $dateAndVersion[0] + } + elseif ($_ -match "Signer Name\s*:\s*(.+)") { + $driver.SignerName = $matches[1].Trim() + } + elseif ($_ -match "Class GUID\s*:\s*(.+)") { + $driver.ClassGUID = $matches[1].Trim() + } + elseif ($_ -match "Original Name\s*:\s*(.+)") { + $driver.OriginalName = $matches[1].Trim() + } + } + return $driver +} + +function InstallOpenSSLFiles { + param ( + [parameter(Mandatory = $true)] [string] $destinationPaths + ) + # Check if SSL library has been installed - $paths = $env:Path.Split(";") + $paths = $destinationPaths.Split(";") foreach($path in $paths) { - if ((Test-Path "$path/ssleay32.dll" -PathType Leaf) -and (Test-Path "$path/libeay32.dll" -PathType Leaf)) { + if ((Test-Path "$path\ssleay32.dll" -PathType Leaf) -and (Test-Path "$path\libeay32.dll" -PathType Leaf)) { Log "Found existing SSL library." return } @@ -244,49 +421,199 @@ function InstallDependency() { Expand-Archive $SSLZip -DestinationPath openssl rm $SSLZip } - cp -Force openssl/*.dll $OVSInstallDir\usr\sbin\ + $destinationPaths -Split ";" | Foreach-Object { + cp -Force openssl\*.dll $_\ + } rm -Recurse -Force openssl } function ConfigOVS() { + param ( + [parameter(Mandatory = $true)] [string] $OVSInstallPath + ) + # Create log dir. + $OVSLogDir = "${OVSInstallPath}\var\log\openvswitch" + if (-not (Test-Path $OVSLogDir)) { + mkdir -p $OVSLogDir | Out-Null + } + + # Create OVS run dir + $OVSRunDir = "${OVSInstallPath}\var\run\openvswitch" + if (-not (Test-Path $OVSRunDir)) { + mkdir -p $OVSRunDir | Out-Null + } + $OVSRunDirPath = $(Get-Item -Path $OVSRunDir).FullName + if ($OVSRunDirPath -ne $DefaultOVSRunDir) { + $env:OVS_RUNDIR = $OVSRunDirPath + [Environment]::SetEnvironmentVariable("OVS_RUNDIR", $env:OVS_RUNDIR, [EnvironmentVariableTarget]::Machine) + } + + $OVSUsrBinDir = $(Get-Item "$OVSInstallPath\usr\bin").FullName # Create ovsdb config file - $OVS_DB_SCHEMA_PATH = "$OVSInstallDir\usr\share\openvswitch\vswitch.ovsschema" - $OVS_DB_PATH = "$OVSInstallDir\etc\openvswitch\conf.db" + $OVSDBDir = "$OVSInstallPath\etc\openvswitch" + if (-not (Test-Path $OVSDBDir)) { + mkdir -p $OVSDBDir | Out-Null + } + $OVS_DB_SCHEMA_PATH = "$OVSInstallPath\usr\share\openvswitch\vswitch.ovsschema" + $OVS_DB_PATH = "$OVSInstallPath\etc\openvswitch\conf.db" if ($(Test-Path $OVS_DB_SCHEMA_PATH) -and !$(Test-Path $OVS_DB_PATH)) { Log "Creating ovsdb file" - ovsdb-tool create "$OVS_DB_PATH" "$OVS_DB_SCHEMA_PATH" + & $OVSUsrBinDir\ovsdb-tool.exe create "$OVS_DB_PATH" "$OVS_DB_SCHEMA_PATH" } + # Create and start ovsdb-server service. + $OVSUsrSbinDir = $(Get-Item "$OVSInstallPath\usr\sbin").FullName Log "Create and start ovsdb-server service" - sc.exe create ovsdb-server binPath= "$OVSInstallDir\usr\sbin\ovsdb-server.exe $OVSInstallDir\etc\openvswitch\conf.db -vfile:info --remote=punix:db.sock --remote=ptcp:6640 --log-file --pidfile --service" start= auto + sc.exe create ovsdb-server binPath= "$OVSUsrSbinDir\ovsdb-server.exe $OVS_DB_PATH -vfile:info --remote=punix:db.sock --remote=ptcp:6640 --log-file=$OVSLogDir\ovsdb-server.log --pidfile --service" start= auto sc.exe failure ovsdb-server reset= 0 actions= restart/0/restart/0/restart/0 Start-Service ovsdb-server # Create and start ovs-vswitchd service. Log "Create and start ovs-vswitchd service." - sc.exe create ovs-vswitchd binpath="$OVSInstallDir\usr\sbin\ovs-vswitchd.exe --pidfile -vfile:info --log-file --service" start= auto depend= "ovsdb-server" + sc.exe create ovs-vswitchd binpath="$OVSUsrSbinDir\ovs-vswitchd.exe --pidfile -vfile:info --log-file=$OVSLogDir\ovs-vswitchd.log --service" start= auto depend= "ovsdb-server" sc.exe failure ovs-vswitchd reset= 0 actions= restart/0/restart/0/restart/0 Start-Service ovs-vswitchd + # Set OVS version. - $OVS_VERSION=$(Get-Item $OVSInstallDir\driver\OVSExt.sys).VersionInfo.ProductVersion + $OVS_VERSION=$(Get-Item $OVSInstallPath\driver\OVSExt.sys).VersionInfo.ProductVersion Log "Set OVS version to: $OVS_VERSION" - ovs-vsctl --no-wait set Open_vSwitch . ovs_version=$OVS_VERSION + & $OVSUsrBinDir\ovs-vsctl.exe --no-wait set Open_vSwitch . ovs_version=$OVS_VERSION + + # Add OVS usr/bin and usr/sbin to the environment path. + AddToEnvPath($OVSUsrSbinDir) + AddToEnvPath($OVSUsrBinDir) + + # Antrea Pod runs as NT AUTHORITY\SYSTEM user on Windows, antrea-ovs container writes + # PID and conf.db files to $OVSInstallDir on Windows Node when it is running. + icacls $OVSInstallPath /grant "NT AUTHORITY\SYSTEM:(OI)(CI)F" /T } -Log "Installation log location: $InstallLog" +function InstallOVSServices() { + param ( + [parameter(Mandatory = $true)] [string] $OVSInstallPath + ) -CheckIfOVSInstalled + # Remove the existing OVS Services to avoid issues. + If (ServiceExists("ovs-vswitchd")) { + stop-service ovs-vswitchd + sc.exe delete ovs-vswitchd + } + if (ServiceExists("ovsdb-server")) { + stop-service ovsdb-server + sc.exe delete ovsdb-server + } -DownloadOVS + # Install OpenSSL dependencies. + $OVSBinPaths="${OVSInstallPath}\usr\bin;${OVSInstallPath}\usr\sbin" + InstallOpenSSLFiles "$OVSBinPaths" + + # Install OVS Services and configure OVSDB. + ConfigOVS($OVSInstallPath) +} -InstallOVS +function PrepareOVSLocalFiles() { + $OVSDownloadURL = $DefaultOVSDownloadURL + $desiredOVSPublishedHash = $DefaultOVSPublishedHash + if ($LocalFile -ne "") { + if (-not (Test-Path $LocalFile)){ + Log "Path $LocalFile doesn't exist, exit" + exit 1 + } -if ($InstallUserspace -eq $true) { - InstallDependency + $ovsFile = Get-Item $LocalFile + if ($ovsFile -is [System.IO.DirectoryInfo]) { + return $ovsFile.FullName + } - ConfigOVS + # $ovsFile as a zip file is supported + $attributes = $ovsFile.Attributes + if (("$attributes" -eq "Archive") -and ($ovsFile.Extension -eq ".zip" ) ) { + $OVSZip = $LocalFile + $OVSDownloadURL = "" + $OVSPublishedHash = "" + } else { + Log "Unsupported local file $LocalFile with attributes '$attributes'" + exit 1 + } + } else { + $OVSZip = "$WorkDir\ovs-win64.zip" + if ($DownloadURL -ne "" -and $DownloadURL -ne "$OVSDownloadURL") { + $OVSDownloadURL = $DownloadURL + $desiredOVSPublishedHash = "invalid" + } + } + + # Extract zip file to $OVSInstallDir + if (Test-Path -Path $OVSInstallDir) { + Log "$OVSInstallDir already exists, exit OVS installation." + exit 1 + } + $removeZipFile = $false + if ($OVSDownloadURL -ne "") { + DownloadOVS -localZipFile $OVSZip -downloadURL $OVSDownloadURL -desiredHash $desiredOVSPublishedHash + $removeZipFile = $true + } + $ovsInstallParentPath = Split-Path -Path $OVSInstallDir -Parent + Expand-Archive -Path $OVSZip -DestinationPath $ovsInstallParentPath | Out-Null + if ($removeZipFile) { + rm $OVSZip + } + return $OVSInstallDir +} + +function CopyOVSUtilities() { + param ( + [parameter(Mandatory = $true)] [string] $OVSInstallPath + ) + $hostUsrBinPath = "C:\openvswitch\usr\bin" + $usrBinPath="${OVSInstallPath}\usr\bin" + if ("$usrBinPath" -ne "$hostUsrBinPath") { + if ((-not $global:updateDriver) -and (Test-Path $hostUsrBinPath)) { + return + } + if (Test-Path $hostUsrBinPath) { + rm -r $hostUsrBinPath + } + mkdir -p $hostUsrBinPath + cp -r $usrBinPath\* $hostUsrBinPath + } + AddToEnvPath($hostUsrBinPath) } -# Antrea Pod runs as NT AUTHORITY\SYSTEM user on Windows, antrea-ovs container writes -# pid and conf.db files to $OVSInstallDir on Windows host Node during runtime. -icacls $OVSInstallDir /grant "NT AUTHORITY\SYSTEM:(OI)(CI)F" /T +function InstallOVS() { + param ( + [parameter(Mandatory = $true)] [string] $OVSInstallPath + ) + # Install powershell modules + $OVSScriptsPath = "${OVSInstallPath}\scripts" + CheckAndInstallScripts($OVSScriptsPath) + + # Install VC redistributables. + $OVSRedistDir="${OVSInstallPath}\redist" + # Check if the VC redistributable is already installed. If not installed, or the installed version + # is lower than $MininalVCRedistVersion, install VC redistributable files provided in the container. + CheckAndInstallVCRedists -VCRedistPath $OVSRedistDir -VCRedistsVersion $MininalVCRedistVersion + + # Install OVS driver. + $OVSDriverDir = "${OVSInstallPath}\driver" + Log "Installing OVS kernel driver" + CheckAndInstallOVSDriver($OVSDriverDir) + + if ($InstallUserspace -eq $true) { + InstallOVSServices($OVSInstallPath) + } else { + # Copy OVS utilities from container path "openvswitch/usr/bin" to host path "c:/openvswitch/" + CopyOVSUtilities($OVSInstallPath) + } +} + +if (($LocalFile -ne "") -and ($DownloadURL -ne "")) { + Log "LocalFile and DownloadURL are mutually exclusive, exit" + exit 1 +} +Log "Installation log location: $InstallLog" + +$OVSPath = PrepareOVSLocalFiles +$OVSFulPath = $(Get-Item -Path $OVSPath).FullName +InstallOVS($OVSFulPath) + Log "OVS Installation Complete!" diff --git a/hack/windows/Uninstall-OVS.ps1 b/hack/windows/Uninstall-OVS.ps1 index b52f477bd51..1f6db906c0e 100644 --- a/hack/windows/Uninstall-OVS.ps1 +++ b/hack/windows/Uninstall-OVS.ps1 @@ -5,15 +5,16 @@ Param( ) $ErrorActionPreference = "Continue" -$OVSBinDir = "$OVSInstallDir\usr\bin" -$OVSDriverDir = "$OVSInstallDir\driver\" - +$usrBinPath="$OVSInstallDir\usr\bin" if ($DeleteBridges) { - $brList = ovs-vsctl.exe list-br - foreach ($br in $brList) { - Write-Host "Delete OVS Bridge: %s $br" - $cmd = "$OVSBinDir\ovs-vsctl.exe --no-wait del-br $br" - Invoke-Expression $cmd + $ovsDbSock="$OVSInstallDir\var\run\openvswitch\db.sock" + if ((Test-Path $usrBinPath) -and (Test-Path $ovsDbSock)){ + $brList = ${usrBinPath}\ovs-vsctl.exe list-br + foreach ($br in $brList) { + Write-Host "Delete OVS Bridge: %s $br" + $cmd = "$usrBinPath\ovs-vsctl.exe --no-wait del-br $br" + Invoke-Expression $cmd + } } } @@ -37,14 +38,17 @@ if (Get-Service ovsdb-server -ErrorAction SilentlyContinue) { } } # Uninstall OVS kernel driver -cmd /c "cd $OVSDriverDir && uninstall.cmd" +$ovsInstalled = $(netcfg -q ovsext) -like "*is installed*" +if ($ovsInstalled) { + netcfg -u ovsext +} if (!$?) { Write-Host "Failed to uninstall OVS kernel driver." exit 1 } # Remove OVS installation dir -if ($RemoveDir) { +if ($RemoveDir -and (Test-Path $OVSInstallDir)) { Remove-Item -Recurse $OVSInstallDir if (!$?) { Write-Host "Failed to remove OVS dir: $OVSInstallDir." @@ -52,4 +56,15 @@ if ($RemoveDir) { } } +# Remove OVS bin paths from environment variables. +$envPaths = @() +$usrSbinPath="$OVSInstallDir\usr\sbin" +foreach ($item in $($env:Path -split ";" | Select-Object -Unique)) { + if (($item -ne "$usrBinPath") -and ($item -ne "$usrSbinPath")) { + $envPaths += $item + } +} +$env:Path =$envPaths -join ";" +[Environment]::SetEnvironmentVariable("Path", $env:Path, [EnvironmentVariableTarget]::Machine) + Write-Host "Uninstall OVS success."