441 строка
14 KiB
Go
441 строка
14 KiB
Go
// Copyright 2017 Microsoft. All rights reserved.
|
|
// MIT License
|
|
|
|
package platform
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/Azure/azure-container-networking/log"
|
|
"github.com/Azure/azure-container-networking/platform/windows/adapter"
|
|
"github.com/Azure/azure-container-networking/platform/windows/adapter/mellanox"
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
const (
|
|
// CNMRuntimePath is the path where CNM state files are stored.
|
|
CNMRuntimePath = ""
|
|
|
|
// CNIRuntimePath is the path where CNI state files are stored.
|
|
CNIRuntimePath = ""
|
|
|
|
// CNILockPath is the path where CNI lock files are stored.
|
|
CNILockPath = ""
|
|
|
|
// CNIStateFilePath is the path to the CNI state file
|
|
CNIStateFilePath = "C:\\k\\azure-vnet.json"
|
|
|
|
// CNIIpamStatePath is the name of IPAM state file
|
|
CNIIpamStatePath = "C:\\k\\azure-vnet-ipam.json"
|
|
|
|
// CNIBinaryPath is the path to the CNI binary
|
|
CNIBinaryPath = "C:\\k\\azurecni\\bin\\azure-vnet.exe"
|
|
|
|
// CNI runtime path on a Kubernetes cluster
|
|
K8SCNIRuntimePath = "C:\\k\\azurecni\\bin"
|
|
|
|
// Network configuration file path on a Kubernetes cluster
|
|
K8SNetConfigPath = "C:\\k\\azurecni\\netconf"
|
|
|
|
// CNSRuntimePath is the path where CNS state files are stored.
|
|
CNSRuntimePath = ""
|
|
|
|
// NPMRuntimePath is the path where NPM state files are stored.
|
|
NPMRuntimePath = ""
|
|
|
|
// DNCRuntimePath is the path where DNC state files are stored.
|
|
DNCRuntimePath = ""
|
|
|
|
// SDNRemoteArpMacAddress is the registry key for the remote arp mac address.
|
|
// This is set for multitenancy to get arp response from within VM
|
|
// for vlan tagged arp requests
|
|
SDNRemoteArpMacAddress = "12-34-56-78-9a-bc"
|
|
|
|
// Command to get SDNRemoteArpMacAddress registry key
|
|
GetSdnRemoteArpMacAddressCommand = "(Get-ItemProperty " +
|
|
"-Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State -Name SDNRemoteArpMacAddress).SDNRemoteArpMacAddress"
|
|
|
|
// Command to set SDNRemoteArpMacAddress registry key
|
|
SetSdnRemoteArpMacAddressCommand = "Set-ItemProperty " +
|
|
"-Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State -Name SDNRemoteArpMacAddress -Value \"12-34-56-78-9a-bc\""
|
|
|
|
// Command to check if system has hns state path or not
|
|
CheckIfHNSStatePathExistsCommand = "Test-Path " +
|
|
"-Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State"
|
|
|
|
// Command to fetch netadapter and pnp id
|
|
GetMacAddressVFPPnpIDMapping = "Get-NetAdapter | Select-Object MacAddress, PnpDeviceID| Format-Table -HideTableHeaders"
|
|
|
|
// Command to restart HNS service
|
|
RestartHnsServiceCommand = "Restart-Service -Name hns"
|
|
|
|
// Interval between successive checks for mellanox adapter's PriorityVLANTag value
|
|
defaultMellanoxMonitorInterval = 30 * time.Second
|
|
|
|
// Value for reg key: PriorityVLANTag for adapter
|
|
// reg key value for PriorityVLANTag = 3 --> Packet priority and VLAN enabled
|
|
// for more details goto https://learn.microsoft.com/en-us/windows-hardware/drivers/network/standardized-inf-keywords-for-ndis-qos
|
|
desiredVLANTagForMellanox = 3
|
|
// Powershell command timeout
|
|
ExecTimeout = 10 * time.Second
|
|
)
|
|
|
|
// Flag to check if sdnRemoteArpMacAddress registry key is set
|
|
var sdnRemoteArpMacAddressSet = false
|
|
|
|
// GetOSInfo returns OS version information.
|
|
func GetOSInfo() string {
|
|
return "windows"
|
|
}
|
|
|
|
func GetProcessSupport() error {
|
|
p := NewExecClient(nil)
|
|
cmd := fmt.Sprintf("Get-Process -Id %v", os.Getpid())
|
|
_, err := p.ExecutePowershellCommand(cmd)
|
|
return err
|
|
}
|
|
|
|
var tickCount = syscall.NewLazyDLL("kernel32.dll").NewProc("GetTickCount64")
|
|
|
|
// GetLastRebootTime returns the last time the system rebooted.
|
|
func (p *execClient) GetLastRebootTime() (time.Time, error) {
|
|
currentTime := time.Now()
|
|
output, _, err := tickCount.Call()
|
|
if errno, ok := err.(syscall.Errno); !ok || errno != 0 {
|
|
if p.logger != nil {
|
|
p.logger.Error("Failed to call GetTickCount64", zap.Error(err))
|
|
} else {
|
|
log.Printf("Failed to call GetTickCount64, err: %v", err)
|
|
}
|
|
return time.Time{}.UTC(), err
|
|
}
|
|
rebootTime := currentTime.Add(-time.Duration(output) * time.Millisecond).Truncate(time.Second)
|
|
if p.logger != nil {
|
|
p.logger.Info("Formatted Boot", zap.String("time", rebootTime.Format(time.RFC3339)))
|
|
} else {
|
|
log.Printf("Formatted Boot time: %s", rebootTime.Format(time.RFC3339))
|
|
}
|
|
return rebootTime.UTC(), nil
|
|
}
|
|
|
|
// Deprecated: ExecuteRawCommand is deprecated, it is recommended to use ExecuteCommand when possible
|
|
func (p *execClient) ExecuteRawCommand(command string) (string, error) {
|
|
if p.logger != nil {
|
|
p.logger.Info("[Azure-Utils]", zap.String("ExecuteRawCommand", command))
|
|
} else {
|
|
log.Printf("[Azure-Utils] ExecuteRawCommand: %q", command)
|
|
}
|
|
|
|
var stderr, stdout bytes.Buffer
|
|
|
|
cmd := exec.Command("cmd", "/c", command)
|
|
cmd.Stderr = &stderr
|
|
cmd.Stdout = &stdout
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return "", errors.Wrapf(err, "ExecuteRawCommand failed. stdout: %q, stderr: %q", stdout.String(), stderr.String())
|
|
}
|
|
|
|
return stdout.String(), nil
|
|
}
|
|
|
|
// ExecuteCommand passes its parameters to an exec.CommandContext, runs the command, and returns its output, or an error if the command fails or times out
|
|
func (p *execClient) ExecuteCommand(ctx context.Context, command string, args ...string) (string, error) {
|
|
if p.logger != nil {
|
|
p.logger.Info("[Azure-Utils]", zap.String("ExecuteCommand", command), zap.Strings("args", args))
|
|
} else {
|
|
log.Printf("[Azure-Utils] ExecuteCommand: %q %v", command, args)
|
|
}
|
|
|
|
var stderr, stdout bytes.Buffer
|
|
|
|
// Create a new context and add a timeout to it
|
|
derivedCtx, cancel := context.WithTimeout(ctx, p.Timeout)
|
|
defer cancel() // The cancel should be deferred so resources are cleaned up
|
|
|
|
cmd := exec.CommandContext(derivedCtx, command, args...)
|
|
cmd.Stderr = &stderr
|
|
cmd.Stdout = &stdout
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return "", errors.Wrapf(err, "ExecuteCommand failed. stdout: %q, stderr: %q", stdout.String(), stderr.String())
|
|
}
|
|
|
|
return stdout.String(), nil
|
|
}
|
|
|
|
func SetOutboundSNAT(subnet string) error {
|
|
return nil
|
|
}
|
|
|
|
// ClearNetworkConfiguration clears the azure-vnet.json contents.
|
|
// This will be called only when reboot is detected - This is windows specific
|
|
func (p *execClient) ClearNetworkConfiguration() (bool, error) {
|
|
jsonStore := CNIRuntimePath + "azure-vnet.json"
|
|
p.logger.Info("Deleting the json", zap.String("store", jsonStore))
|
|
cmd := exec.Command("cmd", "/c", "del", jsonStore)
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
p.logger.Info("Error deleting the json", zap.String("store", jsonStore))
|
|
return true, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (p *execClient) KillProcessByName(processName string) error {
|
|
cmd := fmt.Sprintf("taskkill /IM %v /F", processName)
|
|
_, err := p.ExecuteRawCommand(cmd)
|
|
return err // nolint
|
|
}
|
|
|
|
// ExecutePowershellCommand executes powershell command
|
|
// Deprecated: ExecutePowershellCommand is deprecated, it is recommended to use ExecuteCommand when possible
|
|
func (p *execClient) ExecutePowershellCommand(command string) (string, error) {
|
|
ps, err := exec.LookPath("powershell.exe")
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to find powershell executable")
|
|
}
|
|
|
|
if p.logger != nil {
|
|
p.logger.Info("[Azure-Utils]", zap.String("command", command))
|
|
} else {
|
|
log.Printf("[Azure-Utils] %s", command)
|
|
}
|
|
|
|
cmd := exec.Command(ps, command)
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
return "", fmt.Errorf("%s:%s", err.Error(), stderr.String())
|
|
}
|
|
|
|
return strings.TrimSpace(stdout.String()), nil
|
|
}
|
|
|
|
// ExecutePowershellCommandWithContext executes powershell command wth context
|
|
// Deprecated: ExecutePowershellCommandWithContext is deprecated, it is recommended to use ExecuteCommand when possible
|
|
func (p *execClient) ExecutePowershellCommandWithContext(ctx context.Context, command string) (string, error) {
|
|
ps, err := exec.LookPath("powershell.exe")
|
|
if err != nil {
|
|
return "", errors.New("failed to find powershell executable")
|
|
}
|
|
|
|
if p.logger != nil {
|
|
p.logger.Info("[Azure-Utils]", zap.String("command", command))
|
|
} else {
|
|
log.Printf("[Azure-Utils] %s", command)
|
|
}
|
|
|
|
cmd := exec.CommandContext(ctx, ps, command)
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
ErrPowershellExecution := errors.New("failed to execute powershell command")
|
|
return "", fmt.Errorf("%w:%s", ErrPowershellExecution, stderr.String())
|
|
}
|
|
|
|
return strings.TrimSpace(stdout.String()), nil
|
|
}
|
|
|
|
// SetSdnRemoteArpMacAddress sets the regkey for SDNRemoteArpMacAddress needed for multitenancy if hns is enabled
|
|
func SetSdnRemoteArpMacAddress(execClient ExecClient) error {
|
|
exists, err := execClient.ExecutePowershellCommand(CheckIfHNSStatePathExistsCommand)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("Failed to check the existent of hns state path due to error %s", err.Error())
|
|
log.Printf(errMsg)
|
|
return errors.Errorf(errMsg)
|
|
}
|
|
if strings.EqualFold(exists, "false") {
|
|
log.Printf("hns state path does not exist, skip setting SdnRemoteArpMacAddress")
|
|
return nil
|
|
}
|
|
if sdnRemoteArpMacAddressSet == false {
|
|
result, err := execClient.ExecutePowershellCommand(GetSdnRemoteArpMacAddressCommand)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set the reg key if not already set or has incorrect value
|
|
if result != SDNRemoteArpMacAddress {
|
|
if _, err = execClient.ExecutePowershellCommand(SetSdnRemoteArpMacAddressCommand); err != nil {
|
|
log.Printf("Failed to set SDNRemoteArpMacAddress due to error %s", err.Error())
|
|
return err
|
|
}
|
|
|
|
log.Printf("[Azure CNS] SDNRemoteArpMacAddress regKey set successfully. Restarting hns service.")
|
|
if _, err := execClient.ExecutePowershellCommand(RestartHnsServiceCommand); err != nil {
|
|
log.Printf("Failed to Restart HNS Service due to error %s", err.Error())
|
|
return err
|
|
}
|
|
}
|
|
|
|
sdnRemoteArpMacAddressSet = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func HasMellanoxAdapter() bool {
|
|
m := &mellanox.Mellanox{}
|
|
return hasNetworkAdapter(m)
|
|
}
|
|
|
|
func hasNetworkAdapter(na adapter.NetworkAdapter) bool {
|
|
adapterName, err := na.GetAdapterName()
|
|
if err != nil {
|
|
log.Errorf("Error while getting network adapter name: %v", err)
|
|
return false
|
|
}
|
|
log.Printf("Name of the network adapter : %v", adapterName)
|
|
return true
|
|
}
|
|
|
|
// Regularly monitors the Mellanox PriorityVLANGTag registry value and sets it to desired value if needed
|
|
func MonitorAndSetMellanoxRegKeyPriorityVLANTag(ctx context.Context, intervalSecs int) {
|
|
m := &mellanox.Mellanox{}
|
|
interval := defaultMellanoxMonitorInterval
|
|
if intervalSecs > 0 {
|
|
interval = time.Duration(intervalSecs) * time.Second
|
|
}
|
|
err := updatePriorityVLANTagIfRequired(m, desiredVLANTagForMellanox)
|
|
if err != nil {
|
|
log.Errorf("Error while monitoring mellanox, continuing: %v", err)
|
|
}
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
log.Printf("context cancelled, stopping Mellanox Monitoring: %v", ctx.Err())
|
|
return
|
|
case <-ticker.C:
|
|
err := updatePriorityVLANTagIfRequired(m, desiredVLANTagForMellanox)
|
|
if err != nil {
|
|
log.Errorf("Error while monitoring mellanox, continuing: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Updates the priority VLAN Tag of mellanox adapter if not already set to the desired value
|
|
func updatePriorityVLANTagIfRequired(na adapter.NetworkAdapter, desiredValue int) error {
|
|
currentVal, err := na.GetPriorityVLANTag()
|
|
if err != nil {
|
|
return fmt.Errorf("error while getting Priority VLAN Tag value: %w", err)
|
|
}
|
|
|
|
if currentVal == desiredValue {
|
|
log.Printf("Adapter's PriorityVLANTag is already set to %v, skipping reset", desiredValue)
|
|
return nil
|
|
}
|
|
|
|
err = na.SetPriorityVLANTag(desiredValue)
|
|
if err != nil {
|
|
return fmt.Errorf("error while setting Priority VLAN Tag value: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetOSDetails() (map[string]string, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func GetProcessNameByID(pidstr string) (string, error) {
|
|
pidstr = strings.Trim(pidstr, "\r\n")
|
|
cmd := fmt.Sprintf("Get-Process -Id %s|Format-List", pidstr)
|
|
p := NewExecClient(nil)
|
|
out, err := p.ExecutePowershellCommand(cmd)
|
|
if err != nil {
|
|
log.Printf("Process is not running. Output:%v, Error %v", out, err)
|
|
return "", err
|
|
}
|
|
|
|
if len(out) <= 0 {
|
|
log.Printf("Output length is 0")
|
|
return "", fmt.Errorf("get-process output length is 0")
|
|
}
|
|
|
|
lines := strings.Split(out, "\n")
|
|
for _, line := range lines {
|
|
if strings.Contains(line, "Name") {
|
|
pName := strings.Split(line, ":")
|
|
if len(pName) > 1 {
|
|
return strings.TrimSpace(pName[1]), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("Process not found")
|
|
}
|
|
|
|
func PrintDependencyPackageDetails() {
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw
|
|
func ReplaceFile(source, destination string) error {
|
|
src, err := syscall.UTF16PtrFromString(source)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dest, err := syscall.UTF16PtrFromString(destination)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return windows.MoveFileEx(src, dest, windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH)
|
|
}
|
|
|
|
/*
|
|
Output:
|
|
6C-A1-00-50-E4-2D PCI\VEN_8086&DEV_2723&SUBSYS_00808086&REV_1A\4&328243d9&0&00E0
|
|
80-6D-97-1E-CF-4E USB\VID_17EF&PID_A359\3010019E3
|
|
*/
|
|
func FetchMacAddressPnpIDMapping(ctx context.Context, execClient ExecClient) (map[string]string, error) {
|
|
ctx, cancel := context.WithTimeout(ctx, ExecTimeout)
|
|
defer cancel() // The cancel should be deferred so resources are cleaned up
|
|
output, err := execClient.ExecutePowershellCommandWithContext(ctx, GetMacAddressVFPPnpIDMapping)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to fetch VF mapping")
|
|
}
|
|
result := make(map[string]string)
|
|
if output != "" {
|
|
// Split the output based on new line characters
|
|
lines := strings.Split(output, "\n")
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
// Split based on " " to fetch the macaddress and pci id
|
|
parts := strings.Split(line, " ")
|
|
// Changing the format of macaddress from xx-xx-xx-xx to xx:xx:xx:xx
|
|
formattedMacaddress, err := net.ParseMAC(parts[0])
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to fetch MACAddressPnpIDMapping")
|
|
}
|
|
key := formattedMacaddress.String()
|
|
value := parts[1]
|
|
result[key] = value
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|