...

Source file src/github.com/Microsoft/hcsshim/internal/uvm/computeagent.go

Documentation: github.com/Microsoft/hcsshim/internal/uvm

     1  //go:build windows
     2  
     3  package uvm
     4  
     5  import (
     6  	"context"
     7  	"strings"
     8  
     9  	"github.com/Microsoft/go-winio"
    10  	"github.com/containerd/ttrpc"
    11  	"github.com/containerd/typeurl"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    14  	"google.golang.org/grpc/codes"
    15  	"google.golang.org/grpc/status"
    16  
    17  	"github.com/Microsoft/hcsshim/hcn"
    18  	"github.com/Microsoft/hcsshim/internal/computeagent"
    19  	hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
    20  	"github.com/Microsoft/hcsshim/internal/hns"
    21  	"github.com/Microsoft/hcsshim/internal/log"
    22  	ncproxynetworking "github.com/Microsoft/hcsshim/internal/ncproxy/networking"
    23  	"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
    24  	"github.com/Microsoft/hcsshim/pkg/octtrpc"
    25  )
    26  
    27  func init() {
    28  	typeurl.Register(&ncproxynetworking.Endpoint{}, "ncproxy/ncproxynetworking/Endpoint")
    29  	typeurl.Register(&ncproxynetworking.Network{}, "ncproxy/ncproxynetworking/Network")
    30  	typeurl.Register(&hcn.HostComputeEndpoint{}, "ncproxy/hcn/HostComputeEndpoint")
    31  	typeurl.Register(&hcn.HostComputeNetwork{}, "ncproxy/hcn/HostComputeNetwork")
    32  }
    33  
    34  // This file holds the implementation of the Compute Agent service that is exposed for
    35  // external network configuration.
    36  
    37  const ComputeAgentAddrFmt = "\\\\.\\pipe\\computeagent-%s"
    38  
    39  // create an interface here so we can mock out calls to the UtilityVM in our tests
    40  type agentComputeSystem interface {
    41  	AddEndpointToNSWithID(context.Context, string, string, *hns.HNSEndpoint) error
    42  	UpdateNIC(context.Context, string, *hcsschema.NetworkAdapter) error
    43  	RemoveEndpointFromNS(context.Context, string, *hns.HNSEndpoint) error
    44  	AssignDevice(context.Context, string, uint16, string) (*VPCIDevice, error)
    45  	RemoveDevice(context.Context, string, uint16) error
    46  	AddNICInGuest(context.Context, *guestresource.LCOWNetworkAdapter) error
    47  	RemoveNICInGuest(context.Context, *guestresource.LCOWNetworkAdapter) error
    48  }
    49  
    50  var _ agentComputeSystem = &UtilityVM{}
    51  
    52  // mock hcn function for tests
    53  var hnsGetHNSEndpointByName = hns.GetHNSEndpointByName
    54  
    55  // computeAgent implements the ComputeAgent ttrpc service for adding and deleting NICs to a
    56  // Utility VM.
    57  type computeAgent struct {
    58  	uvm agentComputeSystem
    59  }
    60  
    61  var _ computeagent.ComputeAgentService = &computeAgent{}
    62  
    63  func (ca *computeAgent) AssignPCI(ctx context.Context, req *computeagent.AssignPCIInternalRequest) (*computeagent.AssignPCIInternalResponse, error) {
    64  	log.G(ctx).WithFields(logrus.Fields{
    65  		"containerID":          req.ContainerID,
    66  		"deviceID":             req.DeviceID,
    67  		"virtualFunctionIndex": req.VirtualFunctionIndex,
    68  	}).Info("AssignPCI request")
    69  
    70  	if req.DeviceID == "" {
    71  		return nil, status.Error(codes.InvalidArgument, "received empty field in request")
    72  	}
    73  
    74  	dev, err := ca.uvm.AssignDevice(ctx, req.DeviceID, uint16(req.VirtualFunctionIndex), req.NicID)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return &computeagent.AssignPCIInternalResponse{ID: dev.VMBusGUID}, nil
    79  }
    80  
    81  func (ca *computeAgent) RemovePCI(ctx context.Context, req *computeagent.RemovePCIInternalRequest) (*computeagent.RemovePCIInternalResponse, error) {
    82  	log.G(ctx).WithFields(logrus.Fields{
    83  		"containerID": req.ContainerID,
    84  		"deviceID":    req.DeviceID,
    85  	}).Info("RemovePCI request")
    86  
    87  	if req.DeviceID == "" {
    88  		return nil, status.Error(codes.InvalidArgument, "received empty field in request")
    89  	}
    90  	if err := ca.uvm.RemoveDevice(ctx, req.DeviceID, uint16(req.VirtualFunctionIndex)); err != nil {
    91  		return nil, err
    92  	}
    93  	return &computeagent.RemovePCIInternalResponse{}, nil
    94  }
    95  
    96  // AddNIC will add a NIC to the computeagent services hosting UVM.
    97  func (ca *computeAgent) AddNIC(ctx context.Context, req *computeagent.AddNICInternalRequest) (*computeagent.AddNICInternalResponse, error) {
    98  	log.G(ctx).WithFields(logrus.Fields{
    99  		"containerID": req.ContainerID,
   100  		"endpoint":    req.Endpoint,
   101  		"nicID":       req.NicID,
   102  	}).Info("AddNIC request")
   103  
   104  	if req.NicID == "" || req.Endpoint == nil {
   105  		return nil, status.Error(codes.InvalidArgument, "received empty field in request")
   106  	}
   107  
   108  	endpoint, err := typeurl.UnmarshalAny(req.Endpoint)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	switch endpt := endpoint.(type) {
   114  	case *ncproxynetworking.Endpoint:
   115  		cfg := &guestresource.LCOWNetworkAdapter{
   116  			NamespaceID:    endpt.NamespaceID,
   117  			ID:             req.NicID,
   118  			IPAddress:      endpt.Settings.IPAddress,
   119  			PrefixLength:   uint8(endpt.Settings.IPAddressPrefixLength),
   120  			GatewayAddress: endpt.Settings.DefaultGateway,
   121  			VPCIAssigned:   true,
   122  		}
   123  		if err := ca.uvm.AddNICInGuest(ctx, cfg); err != nil {
   124  			return nil, err
   125  		}
   126  	case *hcn.HostComputeEndpoint:
   127  		hnsEndpoint, err := hnsGetHNSEndpointByName(endpt.Name)
   128  		if err != nil {
   129  			return nil, errors.Wrapf(err, "failed to get endpoint with name %q", endpt.Name)
   130  		}
   131  		if err := ca.uvm.AddEndpointToNSWithID(ctx, hnsEndpoint.Namespace.ID, req.NicID, hnsEndpoint); err != nil {
   132  			return nil, err
   133  		}
   134  	default:
   135  		return nil, status.Error(codes.InvalidArgument, "invalid request endpoint type")
   136  	}
   137  
   138  	return &computeagent.AddNICInternalResponse{}, nil
   139  }
   140  
   141  // ModifyNIC will modify a NIC from the computeagent services hosting UVM.
   142  func (ca *computeAgent) ModifyNIC(ctx context.Context, req *computeagent.ModifyNICInternalRequest) (*computeagent.ModifyNICInternalResponse, error) {
   143  	log.G(ctx).WithFields(logrus.Fields{
   144  		"nicID":    req.NicID,
   145  		"endpoint": req.Endpoint,
   146  	}).Info("ModifyNIC request")
   147  
   148  	if req.NicID == "" || req.Endpoint == nil || req.IovPolicySettings == nil {
   149  		return nil, status.Error(codes.InvalidArgument, "received empty field in request")
   150  	}
   151  
   152  	endpoint, err := typeurl.UnmarshalAny(req.Endpoint)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	switch endpt := endpoint.(type) {
   158  	case *ncproxynetworking.Endpoint:
   159  		return nil, errors.New("modifying ncproxy networking endpoints is not supported")
   160  	case *hcn.HostComputeEndpoint:
   161  		hnsEndpoint, err := hnsGetHNSEndpointByName(endpt.Name)
   162  		if err != nil {
   163  			return nil, errors.Wrapf(err, "failed to get endpoint with name `%s`", endpt.Name)
   164  		}
   165  
   166  		moderationValue := hcsschema.InterruptModerationValue(req.IovPolicySettings.InterruptModeration)
   167  		moderationName := hcsschema.InterruptModerationValueToName[moderationValue]
   168  
   169  		iovSettings := &hcsschema.IovSettings{
   170  			OffloadWeight:       &req.IovPolicySettings.IovOffloadWeight,
   171  			QueuePairsRequested: &req.IovPolicySettings.QueuePairsRequested,
   172  			InterruptModeration: &moderationName,
   173  		}
   174  
   175  		nic := &hcsschema.NetworkAdapter{
   176  			EndpointId:  hnsEndpoint.Id,
   177  			MacAddress:  hnsEndpoint.MacAddress,
   178  			IovSettings: iovSettings,
   179  		}
   180  
   181  		if err := ca.uvm.UpdateNIC(ctx, req.NicID, nic); err != nil {
   182  			return nil, errors.Wrap(err, "failed to update UVM's network adapter")
   183  		}
   184  	default:
   185  		return nil, status.Error(codes.InvalidArgument, "invalid request endpoint type")
   186  	}
   187  
   188  	return &computeagent.ModifyNICInternalResponse{}, nil
   189  }
   190  
   191  // DeleteNIC will delete a NIC from the computeagent services hosting UVM.
   192  func (ca *computeAgent) DeleteNIC(ctx context.Context, req *computeagent.DeleteNICInternalRequest) (*computeagent.DeleteNICInternalResponse, error) {
   193  	log.G(ctx).WithFields(logrus.Fields{
   194  		"containerID": req.ContainerID,
   195  		"nicID":       req.NicID,
   196  		"endpoint":    req.Endpoint,
   197  	}).Info("DeleteNIC request")
   198  
   199  	if req.NicID == "" || req.Endpoint == nil {
   200  		return nil, status.Error(codes.InvalidArgument, "received empty field in request")
   201  	}
   202  
   203  	endpoint, err := typeurl.UnmarshalAny(req.Endpoint)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	switch endpt := endpoint.(type) {
   209  	case *ncproxynetworking.Endpoint:
   210  		cfg := &guestresource.LCOWNetworkAdapter{
   211  			ID: req.NicID,
   212  		}
   213  		if err := ca.uvm.RemoveNICInGuest(ctx, cfg); err != nil {
   214  			return nil, err
   215  		}
   216  	case *hcn.HostComputeEndpoint:
   217  		hnsEndpoint, err := hnsGetHNSEndpointByName(endpt.Name)
   218  		if err != nil {
   219  			return nil, errors.Wrapf(err, "failed to get endpoint with name %q", endpt.Name)
   220  		}
   221  		if err := ca.uvm.RemoveEndpointFromNS(ctx, hnsEndpoint.Namespace.ID, hnsEndpoint); err != nil {
   222  			return nil, err
   223  		}
   224  	default:
   225  		return nil, status.Error(codes.InvalidArgument, "invalid request endpoint type")
   226  	}
   227  
   228  	return &computeagent.DeleteNICInternalResponse{}, nil
   229  }
   230  
   231  func setupAndServe(ctx context.Context, caAddr string, vm *UtilityVM) error {
   232  	// Setup compute agent service
   233  	l, err := winio.ListenPipe(caAddr, nil)
   234  	if err != nil {
   235  		return errors.Wrapf(err, "failed to listen on %s", caAddr)
   236  	}
   237  	s, err := ttrpc.NewServer(ttrpc.WithUnaryServerInterceptor(octtrpc.ServerInterceptor()))
   238  	if err != nil {
   239  		return err
   240  	}
   241  	computeagent.RegisterComputeAgentService(s, &computeAgent{vm})
   242  
   243  	log.G(ctx).WithField("address", l.Addr().String()).Info("serving compute agent")
   244  	go func() {
   245  		defer l.Close()
   246  		if err := trapClosedConnErr(s.Serve(ctx, l)); err != nil {
   247  			log.G(ctx).WithError(err).Fatal("compute agent: serve failure")
   248  		}
   249  	}()
   250  
   251  	return nil
   252  }
   253  
   254  func trapClosedConnErr(err error) error {
   255  	if err == nil || strings.Contains(err.Error(), "use of closed network connection") {
   256  		return nil
   257  	}
   258  	return err
   259  }
   260  

View as plain text