//go:build windows package remotevm import ( "context" "encoding/json" "fmt" "strings" "github.com/Microsoft/go-winio/pkg/guid" "github.com/Microsoft/hcsshim/hcn" "github.com/Microsoft/hcsshim/internal/vmservice" "github.com/pkg/errors" ) func getSwitchID(endpointID, portID string) (string, error) { // Get updated endpoint with new fields (need switch ID) ep, err := hcn.GetEndpointByID(endpointID) if err != nil { return "", errors.Wrapf(err, "failed to get endpoint %q", endpointID) } type ExtraInfo struct { Allocators []struct { SwitchID string `json:"SwitchId"` EndpointPortGUID string `json:"EndpointPortGuid"` } } var exi ExtraInfo if err := json.Unmarshal(ep.Health.Extra.Resources, &exi); err != nil { return "", errors.Wrapf(err, "failed to unmarshal resource data from endpoint %q", endpointID) } if len(exi.Allocators) == 0 { return "", errors.New("no resource data found for endpoint") } // NIC should only ever belong to one switch but there are cases where there's more than one allocator // in the returned data. It seems they only ever contain empty strings so search for the first entry // that actually contains a switch ID and that has the matching port GUID we made earlier. var switchID string for _, allocator := range exi.Allocators { if allocator.SwitchID != "" && strings.ToLower(allocator.EndpointPortGUID) == portID { switchID = allocator.SwitchID break } } return switchID, nil } func (uvm *utilityVM) AddNIC(ctx context.Context, nicID, endpointID, macAddr string) error { portID, err := guid.NewV4() if err != nil { return errors.Wrap(err, "failed to generate guid for port") } vmEndpointRequest := hcn.VmEndpointRequest{ PortId: portID, VirtualNicName: fmt.Sprintf("%s--%s", nicID, portID), VirtualMachineId: guid.GUID{}, } m, err := json.Marshal(vmEndpointRequest) if err != nil { return errors.Wrap(err, "failed to marshal endpoint request json") } if err := hcn.ModifyEndpointSettings(endpointID, &hcn.ModifyEndpointSettingRequest{ ResourceType: hcn.EndpointResourceTypePort, RequestType: hcn.RequestTypeAdd, Settings: json.RawMessage(m), }); err != nil { return errors.Wrap(err, "failed to configure switch port") } switchID, err := getSwitchID(endpointID, portID.String()) if err != nil { return err } nic := &vmservice.NICConfig{ NicID: nicID, MacAddress: macAddr, PortID: portID.String(), SwitchID: switchID, } if _, err := uvm.client.ModifyResource(ctx, &vmservice.ModifyResourceRequest{ Type: vmservice.ModifyType_ADD, Resource: &vmservice.ModifyResourceRequest_NicConfig{ NicConfig: nic, }, }, ); err != nil { return errors.Wrap(err, "failed to add network adapter") } return nil } func (uvm *utilityVM) RemoveNIC(ctx context.Context, nicID, endpointID, macAddr string) error { nic := &vmservice.NICConfig{ NicID: nicID, MacAddress: macAddr, } if _, err := uvm.client.ModifyResource(ctx, &vmservice.ModifyResourceRequest{ Type: vmservice.ModifyType_REMOVE, Resource: &vmservice.ModifyResourceRequest_NicConfig{ NicConfig: nic, }, }, ); err != nil { return errors.Wrap(err, "failed to remove network adapter") } return nil }