• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

noironetworks / aci-containers / 7615

pending completion
7615

push

travis-pro

GitHub
Merge pull request #1125 from noironetworks/travis-backport-kmr2

Travis backport kmr2

12040 of 21626 relevant lines covered (55.67%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.67
/pkg/hostagent/setup.go
1
// Copyright 2016-2017 Cisco Systems, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package hostagent
16

17
import (
18
        "errors"
19
        "fmt"
20
        "net"
21
        "os"
22

23
        cnicur "github.com/containernetworking/cni/pkg/types/100"
24
        "github.com/containernetworking/plugins/pkg/ip"
25
        "github.com/containernetworking/plugins/pkg/ipam"
26
        "github.com/containernetworking/plugins/pkg/ns"
27
        "github.com/natefinch/pie"
28
        "github.com/sirupsen/logrus"
29
        "github.com/vishvananda/netlink"
30

31
        md "github.com/noironetworks/aci-containers/pkg/metadata"
32
)
33

34
const (
35
        ipRelevantByteLen      = 4
36
        PrivateMACPrefixString = "0a:58"
37
)
38

39
var (
40
        // private mac prefix safe to use
41
        PrivateMACPrefix = []byte{0x0a, 0x58}
42
)
43

44
func StartPlugin(log *logrus.Logger) {
×
45
        p := pie.NewProvider()
×
46
        if err := p.Register(&ClientRPC{}); err != nil {
×
47
                log.Fatalf("failed to register Plugin: %s", err)
×
48
                return
×
49
        }
×
50

51
        log.Debug("Starting plugin provider")
×
52
        p.Serve()
×
53
}
54

55
// Cloner encapsulate a binary cloner for executing in a different process
56
// context
57
type Cloner struct {
58
        Stub bool
59
}
60

61
var PluginCloner Cloner
62

63
// runPluginCmd runs the command from a cloned instance of the
64
// executable in order to address name space binding needs
65
// func (c *Cloner) runPluginCmd(method, fsuid string, args interface{},
66
func (c *Cloner) runPluginCmd(method string, args interface{},
67
        reply interface{}) error {
1✔
68

1✔
69
        if c.Stub {
2✔
70
                // if we are in stub mode, just return success
1✔
71
                return nil
1✔
72
        }
1✔
73

74
        exe, err := os.Executable()
×
75
        if err != nil {
×
76
                return err
×
77
        }
×
78

79
        client, err := pie.StartProvider(os.Stderr, exe, "-child-mode")
×
80
        if err != nil {
×
81
                return err
×
82
        }
×
83
        defer client.Close()
×
84

×
85
        return client.Call(method, args, reply)
×
86
}
87

88
type ClientRPC struct{}
89

90
type SetupVethArgs struct {
91
        Sandbox string
92
        IfName  string
93
        Mtu     int
94
        Ip      net.IP
95
}
96

97
type SetupVethResult struct {
98
        HostVethName string
99
        Mac          string
100
}
101

102
func runSetupVeth(sandbox string, ifName string,
103
        mtu int, ip net.IP) (string, string, error) {
1✔
104
        result := &SetupVethResult{}
1✔
105
        err := PluginCloner.runPluginCmd("ClientRPC.SetupVeth",
1✔
106
                &SetupVethArgs{sandbox, ifName, mtu, ip}, result)
1✔
107
        return result.HostVethName, result.Mac, err
1✔
108
}
1✔
109

110
// https://github.com/containernetworking/plugins/blob/v0.9.1/pkg/utils/hwaddr/hwaddr.go#L45
111
// Reusing code as the fn is removed in v1.0.0
112
// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input.
113
func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) {
×
114
        switch {
×
115

116
        case ip.To4() == nil:
×
117
                return nil, fmt.Errorf("GenerateHardwareAddr4 only supports valid IPv4 address as input")
×
118

119
        case len(prefix) != len(PrivateMACPrefix):
×
120
                return nil, fmt.Errorf(
×
121
                        "Prefix has length %d instead  of %d", len(prefix), len(PrivateMACPrefix))
×
122
        }
123

124
        ipByteLen := len(ip)
×
125
        return (net.HardwareAddr)(
×
126
                append(
×
127
                        prefix,
×
128
                        ip[ipByteLen-ipRelevantByteLen:ipByteLen]...),
×
129
        ), nil
×
130
}
131

132
// https://github.com/containernetworking/plugins/blob/v0.9.1/pkg/ip/link_linux.go#L228
133
// Reusing code as the fn is removed in v1.0.0
134
func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
×
135
        iface, err := netlink.LinkByName(ifName)
×
136
        if err != nil {
×
137
                return fmt.Errorf("failed to lookup %q: %v", ifName, err)
×
138
        }
×
139

140
        switch {
×
141
        case ip4 == nil && ip6 == nil:
×
142
                return fmt.Errorf("neither ip4 or ip6 specified")
×
143

144
        case ip4 != nil:
×
145
                {
×
146
                        hwAddr, err := GenerateHardwareAddr4(ip4, PrivateMACPrefix)
×
147
                        if err != nil {
×
148
                                return fmt.Errorf("failed to generate hardware addr: %v", err)
×
149
                        }
×
150
                        if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
×
151
                                return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
×
152
                        }
×
153
                }
154
        case ip6 != nil:
×
155
                // TODO: IPv6
156
        }
157

158
        return nil
×
159
}
160

161
func (*ClientRPC) SetupVeth(args *SetupVethArgs, result *SetupVethResult) error {
×
162
        netns, err := ns.GetNS(args.Sandbox)
×
163
        if err != nil {
×
164
                return fmt.Errorf("failed to open netns %q: %v", args.Sandbox, err)
×
165
        }
×
166
        defer netns.Close()
×
167

×
168
        return netns.Do(func(hostNS ns.NetNS) error {
×
169
                // create the veth pair in the container and move host end
×
170
                // into host netns
×
171
                hostVeth, _, err := ip.SetupVeth(args.IfName, args.Mtu, "", hostNS)
×
172
                if err != nil {
×
173
                        return err
×
174
                }
×
175

176
                result.HostVethName = hostVeth.Name
×
177

×
178
                // Force a consistent MAC address based on the IPv4 address
×
179
                // Currently we dont have a support for V6 based mac allocation. Upstream doesn't support yet :-(
×
180

×
181
                if args.Ip.To4() != nil {
×
182
                        if err := SetHWAddrByIP(args.IfName, args.Ip, nil); err != nil {
×
183
                                return fmt.Errorf("failed Ip based MAC address allocation for v4: %v", err)
×
184
                        }
×
185
                }
186

187
                contVeth, err := netlink.LinkByName(args.IfName)
×
188
                if err != nil {
×
189
                        return err
×
190
                }
×
191

192
                result.Mac = contVeth.Attrs().HardwareAddr.String()
×
193
                return nil
×
194
        })
195
}
196

197
type ClearVethArgs struct {
198
        Sandbox string
199
        IfName  string
200
}
201

202
func runClearVeth(sandbox string, ifName string) error {
1✔
203
        ack := false
1✔
204
        err := PluginCloner.runPluginCmd("ClientRPC.ClearVeth",
1✔
205
                &ClearVethArgs{sandbox, ifName}, &ack)
1✔
206
        return err
1✔
207
}
1✔
208

209
func (c *ClientRPC) ClearVeth(args *ClearVethArgs, ack *bool) error {
×
210
        netns, err := ns.GetNS(args.Sandbox)
×
211
        if err != nil {
×
212
                return fmt.Errorf("failed to open netns %q: %v", args.Sandbox, err)
×
213
        }
×
214
        defer netns.Close()
×
215

×
216
        *ack = false
×
217
        if err := netns.Do(func(_ ns.NetNS) error {
×
218
                iface, err := netlink.LinkByName(args.IfName)
×
219
                if err != nil {
×
220
                        return fmt.Errorf("failed to lookup %q: %v", args.IfName, err)
×
221
                }
×
222

223
                err = netlink.LinkDel(iface)
×
224
                if err != nil {
×
225
                        return fmt.Errorf("failed to delete %q: %v", args.IfName, err)
×
226
                }
×
227

228
                return nil
×
229
        }); err != nil {
×
230
                return err
×
231
        }
×
232

233
        *ack = true
×
234
        return nil
×
235
}
236

237
type SetupNetworkArgs struct {
238
        Sandbox string
239
        IfName  string
240
        Result  *cnicur.Result
241
}
242

243
func runSetupNetwork(sandbox string, ifName string,
244
        result *cnicur.Result) error {
1✔
245

1✔
246
        ack := false
1✔
247
        err := PluginCloner.runPluginCmd("ClientRPC.SetupNetwork",
1✔
248
                &SetupNetworkArgs{sandbox, ifName, result}, &ack)
1✔
249
        return err
1✔
250
}
1✔
251

252
func (*ClientRPC) SetupNetwork(args *SetupNetworkArgs, ack *bool) error {
×
253
        netns, err := ns.GetNS(args.Sandbox)
×
254
        if err != nil {
×
255
                return fmt.Errorf("failed to open netns %q: %v", args.Sandbox, err)
×
256
        }
×
257
        defer netns.Close()
×
258

×
259
        // in gob encoding, pointer to 0 gets turned into a nil pointer.
×
260
        // This is a bug in the design of gob that will not be fixed:
×
261
        // See https://github.com/golang/go/issues/4609
×
262
        // Fix it to workaround I guess.
×
263
        index := 0
×
264
        for _, ip := range args.Result.IPs {
×
265
                if ip.Interface == nil {
×
266
                        ip.Interface = &index
×
267
                }
×
268
        }
269

270
        *ack = false
×
271
        if err := netns.Do(func(_ ns.NetNS) error {
×
272
                return ipam.ConfigureIface(args.IfName, args.Result)
×
273
        }); err != nil {
×
274
                return err
×
275
        }
×
276

277
        *ack = true
×
278
        return nil
×
279
}
280

281
func (agent *HostAgent) addToResult(iface *md.ContainerIfaceMd,
282
        index int, result *cnicur.Result) {
1✔
283

1✔
284
        result.Interfaces = append(result.Interfaces,
1✔
285
                &cnicur.Interface{
1✔
286
                        Name:    iface.Name,
1✔
287
                        Sandbox: iface.Sandbox,
1✔
288
                        Mac:     iface.Mac,
1✔
289
                })
1✔
290
        for _, ip := range iface.IPs {
2✔
291
                if ip.Address.IP == nil {
1✔
292
                        continue
×
293
                }
294
                if !(ip.Address.IP.To4() != nil || ip.Address.IP.To16() != nil) {
1✔
295
                        continue
×
296
                }
297

298
                ind := index
1✔
299
                result.IPs = append(result.IPs,
1✔
300
                        &cnicur.IPConfig{
1✔
301
                                Interface: &ind,
1✔
302
                                Address:   ip.Address,
1✔
303
                                Gateway:   ip.Gateway,
1✔
304
                        })
1✔
305
        }
306

307
}
308

309
func (agent *HostAgent) configureContainerIfaces(metadata *md.ContainerMetadata) (*cnicur.Result, error) {
1✔
310
        logger := agent.log.WithFields(logrus.Fields{
1✔
311
                "pod":       metadata.Id.Pod,
1✔
312
                "namespace": metadata.Id.Namespace,
1✔
313
                "container": metadata.Id.ContId,
1✔
314
        })
1✔
315

1✔
316
        podKey := makePodKey(metadata.Id.Namespace, metadata.Id.Pod)
1✔
317
        logger.Debug("Setting up veth")
1✔
318
        if len(metadata.Ifaces) == 0 {
1✔
319
                return nil, errors.New("No interfaces specified")
×
320
        }
×
321
        result := &cnicur.Result{}
1✔
322

1✔
323
        //deallocate IP's incase if container interface creation fails
1✔
324
        deallocIP := func(iface *md.ContainerIfaceMd, err error) {
1✔
325
                logger.Infof("Deallocating IP address(es).")
×
326
                logger.Infof("Error: %+v", err)
×
327
                agent.ipamMutex.Lock()
×
328
                agent.deallocateIpsLocked(iface)
×
329
                agent.ipamMutex.Unlock()
×
330
        }
×
331

332
        for _, nc := range agent.config.NetConfig {
2✔
333
                result.Routes =
1✔
334
                        append(result.Routes, convertRoutes(nc.Routes)...)
1✔
335
        }
1✔
336

337
        for ifaceind, iface := range metadata.Ifaces {
2✔
338
                var err error
1✔
339
                var mtu int
1✔
340
                if agent.config.InterfaceMtu == 0 {
2✔
341
                        // MTU not explicitly set in config or discovered
1✔
342
                        mtu = 1500
1✔
343
                } else {
1✔
344
                        mtu = agent.config.InterfaceMtu
×
345
                }
×
346

347
                if len(iface.IPs) == 0 {
2✔
348
                        // We're doing ip address management
1✔
349

1✔
350
                        logger.Debugf("Allocating IP address(es) for %v", iface.Name)
1✔
351
                        err = agent.allocateIps(iface, podKey)
1✔
352
                        if err != nil {
1✔
353
                                return nil, err
×
354
                        }
×
355
                }
356
                for _, ip := range iface.IPs {
2✔
357
                        //There are 4 cases: IPv4-only, IPv6-only, dual stack with either IPv4 or IPv6 as the first address.
1✔
358
                        //We are guaranteed to derive the MAC address from IPv4 if it is assigned
1✔
359
                        if ip.Address.IP != nil && ip.Address.IP.To4() != nil {
2✔
360
                                iface.HostVethName, iface.Mac, err =
1✔
361
                                        runSetupVeth(iface.Sandbox, iface.Name, mtu, ip.Address.IP)
1✔
362
                                if err != nil {
1✔
363
                                        deallocIP(iface, err)
×
364
                                        return nil, err
×
365
                                } else {
1✔
366
                                        break
1✔
367
                                }
368
                        }
369
                }
370
                // if no mac is assigned, set it to the default Mac.
371
                if len(iface.Mac) == 0 {
2✔
372
                        iface.HostVethName, iface.Mac, err =
1✔
373
                                runSetupVeth(iface.Sandbox, iface.Name, agent.config.InterfaceMtu, nil)
1✔
374
                        if err != nil {
1✔
375
                                deallocIP(iface, err)
×
376
                                return nil, err
×
377
                        }
×
378
                }
379
                //Todo: Remove dependency on integ_test flag.
380
                if agent.integ_test == nil {
1✔
381
                        if len(iface.HostVethName) == 0 || len(iface.Mac) == 0 {
×
382
                                l := fmt.Sprintf("Failed to setup Veth.{ContainerName= %v, HostVethName: { Name=%v, Lenght=%v} ,MAC: {Name=%v, Length=%v}}", metadata.Id.ContId, iface.HostVethName, len(iface.HostVethName), iface.Mac, len(iface.Mac))
×
383
                                er := fmt.Errorf("Unable to Configure Container Interface, Error: %v", l)
×
384
                                deallocIP(iface, er)
×
385
                                return nil, er
×
386
                        }
×
387
                }
388

389
                agent.addToResult(iface, ifaceind, result)
1✔
390

1✔
391
                logger.Debug("Configuring network for ", iface.Name, ": ", *result)
1✔
392
                err = runSetupNetwork(iface.Sandbox, iface.Name, result)
1✔
393
                if err != nil {
1✔
394
                        deallocIP(iface, err)
×
395
                        return nil, err
×
396
                }
×
397
        }
398
        var StaleContMetadata []md.ContainerMetadata
1✔
399
        podid := metadata.Id.Namespace + "/" + metadata.Id.Pod
1✔
400
        agent.indexMutex.Lock()
1✔
401
        if len(agent.epMetadata[podid]) > 1 {
1✔
402
                logger.Warnf("There is a Stale metadata present for the pod")
×
403
        }
×
404
        if val, ok := agent.epMetadata[podid]; ok {
2✔
405
                // check for any stale entry present.
1✔
406
                // this is possible if we miss any register events for unconfiguring the POD
1✔
407
                //  ideally epMetadata[podid] Map should contain only one entry of containerID
1✔
408
                // As every pod contains one network namespace for all the containers with in the pod
1✔
409
                // (i.e pause container ==> metadata.Id.ContId)
1✔
410
                // if there are any stale which doesn't match the ContainerID remove it
1✔
411
                for key, v := range val {
2✔
412
                        if metadata.Id.ContId != key {
2✔
413
                                logger.Warnf("Stale metadata present clean the entry: %s", key)
1✔
414
                                StaleContMetadata = append(StaleContMetadata, *v)
1✔
415
                        }
1✔
416
                }
417
        }
418
        agent.indexMutex.Unlock()
1✔
419

1✔
420
        for _, v := range StaleContMetadata {
2✔
421
                err := agent.cleanStatleMetadata(v.Id.ContId)
1✔
422
                if err == nil {
2✔
423
                        agent.deallocateMdIps(&v)
1✔
424
                        agent.ipamMutex.Lock()
1✔
425
                        delete(agent.epMetadata[podid], v.Id.ContId)
1✔
426
                        agent.ipamMutex.Unlock()
1✔
427
                }
1✔
428
        }
429

430
        err := md.RecordMetadata(agent.config.CniMetadataDir,
1✔
431
                agent.config.CniNetwork, *metadata)
1✔
432
        if err != nil {
1✔
433
                logger.Debug("ERROR RecordMetadata")
×
434
                return nil, err
×
435
        }
×
436

437
        agent.indexMutex.Lock()
1✔
438
        if _, ok := agent.epMetadata[podid]; !ok {
2✔
439
                agent.epMetadata[podid] =
1✔
440
                        make(map[string]*md.ContainerMetadata)
1✔
441
        }
1✔
442
        agent.epMetadata[podid][metadata.Id.ContId] = metadata
1✔
443
        agent.indexMutex.Unlock()
1✔
444

1✔
445
        agent.env.CniDeviceChanged(&podid, &metadata.Id)
1✔
446

1✔
447
        logger.Info("Successfully configured container interface")
1✔
448
        return result, nil
1✔
449
}
450

451
func (agent *HostAgent) cleanStatleMetadata(id string) error {
1✔
452
        _, err := md.GetMetadata(agent.config.CniMetadataDir,
1✔
453
                agent.config.CniNetwork, id)
1✔
454
        if err == nil {
2✔
455
                err := md.ClearMetadata(agent.config.CniMetadataDir,
1✔
456
                        agent.config.CniNetwork, id)
1✔
457
                if err != nil {
1✔
458
                        return err
×
459
                }
×
460
        }
461
        return err
1✔
462
}
463

464
func (agent *HostAgent) unconfigureContainerIfaces(id *md.ContainerId) error {
1✔
465
        logger := agent.log.WithFields(logrus.Fields{
1✔
466
                "ContId":    id.ContId,
1✔
467
                "Pod":       id.Pod,
1✔
468
                "Namespace": id.Namespace,
1✔
469
        })
1✔
470

1✔
471
        podid := id.Namespace + "/" + id.Pod
1✔
472

1✔
473
        agent.indexMutex.Lock()
1✔
474
        mdmap, ok := agent.epMetadata[podid]
1✔
475
        if !ok {
2✔
476
                logger.Info("Unconfigure called for pod with no metadata")
1✔
477
                // Assume container is already unconfigured
1✔
478
                agent.indexMutex.Unlock()
1✔
479
                return nil
1✔
480
        }
1✔
481
        metadata, ok := mdmap[id.ContId]
1✔
482
        if !ok {
2✔
483
                logger.Error("Unconfigure called for container with no metadata")
1✔
484
                // Assume container is already unconfigured
1✔
485
                agent.indexMutex.Unlock()
1✔
486
                return nil
1✔
487
        }
1✔
488
        delete(mdmap, id.ContId)
1✔
489
        if len(mdmap) == 0 {
2✔
490
                delete(agent.epMetadata, podid)
1✔
491
        }
1✔
492
        agent.indexMutex.Unlock()
1✔
493

1✔
494
        err := md.ClearMetadata(agent.config.CniMetadataDir,
1✔
495
                agent.config.CniNetwork, id.ContId)
1✔
496
        if err != nil {
2✔
497
                return err
1✔
498
        }
1✔
499

500
        agent.cniEpDelete(podid)
1✔
501
        logger.Debug("Deallocating IP address(es)")
1✔
502
        agent.deallocateMdIps(metadata)
1✔
503

1✔
504
        logger.Debug("Clearing container interface")
1✔
505
        for _, iface := range metadata.Ifaces {
2✔
506
                err = runClearVeth(iface.Sandbox, iface.Name)
1✔
507
                if err != nil {
1✔
508
                        logger.Error("Could not clear Veth ports: ", err)
×
509
                }
×
510
        }
511

512
        agent.env.CniDeviceDeleted(&podid, id)
1✔
513

1✔
514
        logger.Info("Successfully unconfigured container interface")
1✔
515
        return nil
1✔
516
}
517

518
func (agent *HostAgent) cleanupSetup() {
1✔
519
        agent.log.Info("Checking for stale container setup")
1✔
520

1✔
521
        agent.indexMutex.Lock()
1✔
522
        if !agent.syncEnabled {
2✔
523
                agent.indexMutex.Unlock()
1✔
524
                agent.log.Info("Sync not enabled, skipping stale container setup")
1✔
525
                return
1✔
526
        }
1✔
527
        mdcopy := agent.epMetadata
1✔
528
        agent.indexMutex.Unlock()
1✔
529

1✔
530
        for podkey, mdmap := range mdcopy {
1✔
531
                logger := agent.log.WithFields(logrus.Fields{
×
532
                        "podkey": podkey,
×
533
                })
×
534

×
535
                logger.Debug("Checking")
×
536
                exists, err := agent.env.CheckPodExists(&podkey)
×
537
                if err != nil {
×
538
                        logger.Error("Could not lookup pod: ", err)
×
539
                        continue
×
540
                }
541

542
                if !exists {
×
543
                        for _, metadata := range mdmap {
×
544
                                logger := agent.log.WithFields(logrus.Fields{
×
545
                                        "namespace": metadata.Id.Namespace,
×
546
                                        "pod":       metadata.Id.Pod,
×
547
                                        "contid":    metadata.Id.ContId,
×
548
                                })
×
549
                                logger.Info("Unconfiguring stale container configuration")
×
550

×
551
                                err := agent.unconfigureContainerIfaces(&metadata.Id)
×
552
                                if err != nil {
×
553
                                        logger.Error("Could not unconfigure container: ", err)
×
554
                                }
×
555
                        }
556
                }
557
        }
558

559
        err := agent.syncPorts(agent.config.OvsDbSock)
1✔
560
        if err != nil {
2✔
561
                agent.log.Error("Could not sync OVS ports: ", err)
1✔
562
        }
1✔
563

564
        agent.log.Debug("Done stale check")
1✔
565
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc