1
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
35
36
37 const ComputeAgentAddrFmt = "\\\\.\\pipe\\computeagent-%s"
38
39
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
53 var hnsGetHNSEndpointByName = hns.GetHNSEndpointByName
54
55
56
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
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
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
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
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