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

noironetworks / aci-containers / 10987

22 Sep 2025 03:29PM UTC coverage: 65.826% (-0.3%) from 66.078%
10987

push

travis-pro

web-flow
Merge pull request #1573 from noironetworks/fix-log-rh

Fix logging: resilient hashing

1 of 5 new or added lines in 1 file covered. (20.0%)

230 existing lines in 3 files now uncovered.

13368 of 20308 relevant lines covered (65.83%)

0.75 hits per line

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

32.96
/pkg/hostagent/opflex.go
1
// Copyright 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
        "bufio"
19
        "bytes"
20
        "encoding/json"
21
        "errors"
22
        "fmt"
23
        "io/fs"
24
        "net"
25
        "os"
26
        "os/exec"
27
        "path/filepath"
28
        "reflect"
29
        "regexp"
30
        "strings"
31
        "text/template"
32
        "time"
33

34
        uuid "github.com/google/uuid"
35
        "github.com/sirupsen/logrus"
36
        "github.com/vishvananda/netlink"
37
)
38

39
const (
40
        DHCLIENT_CONF    = "/usr/local/etc/dhclient.conf"
41
        MCAST_ROUTE_DEST = "224.0.0.0/4"
42
        dhclientLeaseDir = "/usr/local/var/lib/dhclient"
43
)
44

45
type opflexFault struct {
46
        FaultUUID   string `json:"fault_uuid"`
47
        Severity    string `json:"severity"`
48
        Description string `json:"description"`
49
        FaultCode   int    `json:"faultCode"`
50
}
51

52
func writeFault(faultfile string, ep *opflexFault) (bool, error) {
1✔
53
        newdata, err := json.MarshalIndent(ep, "", "  ")
1✔
54
        if err != nil {
1✔
55
                return true, err
×
UNCOV
56
        }
×
57
        existingdata, err := os.ReadFile(faultfile)
1✔
58
        if err == nil && reflect.DeepEqual(existingdata, newdata) {
1✔
UNCOV
59
                return false, nil
×
UNCOV
60
        }
×
61
        err = os.WriteFile(faultfile, newdata, 0644)
1✔
62
        return true, err
1✔
63
}
64

65
func (agent *HostAgent) createFaultOnAgent(description string, faultCode int) {
1✔
66
        if agent.config.OpFlexFaultDir == "" {
2✔
67
                agent.log.Error("OpFlex Fault directory not set")
1✔
68
                return
1✔
69
        }
1✔
70
        Uuid := uuid.New().String()
1✔
71
        faultFilePath := filepath.Join(agent.config.OpFlexFaultDir, description+".fs")
1✔
72
        faultFileExists := fileExists(faultFilePath)
1✔
73
        if faultFileExists {
1✔
UNCOV
74
                agent.log.Debug("Fault file exist at: ", faultFilePath)
×
UNCOV
75
                return
×
UNCOV
76
        }
×
77
        desc := strings.Replace(description, "_", " ", -1)
1✔
78
        fault := &opflexFault{
1✔
79
                FaultUUID:   Uuid,
1✔
80
                Severity:    "critical",
1✔
81
                Description: desc,
1✔
82
                FaultCode:   faultCode,
1✔
83
        }
1✔
84
        wrote, err := writeFault(faultFilePath, fault)
1✔
85
        if err != nil {
1✔
UNCOV
86
                agent.log.Warn("Unable to write fault file: ", err.Error())
×
87
        } else if wrote {
2✔
88
                agent.log.Debug("Created fault files at the location: ", faultFilePath)
1✔
89
        }
1✔
90
}
91

92
func (agent *HostAgent) isIpV4Present(iface string) bool {
×
93
        link, err := netlink.LinkByName(iface)
×
94
        if err != nil {
×
95
                agent.log.Error("Could not enumerate interfaces: ", err)
×
96
                return false
×
97
        }
×
98
        if nlLink, ok := link.(*netlink.Vlan); ok {
×
99
                addrs, err := netlink.AddrList(nlLink, netlink.FAMILY_V4)
×
100
                if err != nil {
×
101
                        agent.log.Error("Could not enumerate addresses: ", err)
×
102
                        return false
×
UNCOV
103
                }
×
104
                if len(addrs) > 0 {
×
UNCOV
105
                        agent.log.Info("vlan interface ip address: ", addrs)
×
UNCOV
106
                        return true
×
UNCOV
107
                }
×
108
        }
UNCOV
109
        return false
×
110
}
111

112
func (agent *HostAgent) isIpSameSubnet(iface, subnet string) bool {
1✔
113
        link, err := netlink.LinkByName(iface)
1✔
114
        if err != nil {
2✔
115
                agent.log.Error("Could not enumerate interfaces: ", err)
1✔
116
                return false
1✔
117
        }
1✔
118
        if nlLink, ok := link.(*netlink.Vlan); ok {
×
119
                addrs, err := netlink.AddrList(nlLink, netlink.FAMILY_V4)
×
UNCOV
120
                if err != nil {
×
121
                        agent.log.Error("Could not enumerate addresses: ", err)
×
UNCOV
122
                        return false
×
UNCOV
123
                }
×
UNCOV
124
                return agent.checkIfAnyIpsInSubnet(subnet, addrs)
×
125
        }
UNCOV
126
        return false
×
127
}
128

129
func (agent *HostAgent) checkIfAnyIpsInSubnet(subnet string, addrs []netlink.Addr) bool {
1✔
130
        _, ipnet, err := net.ParseCIDR(subnet)
1✔
131
        if err != nil {
1✔
UNCOV
132
                agent.log.Error("Failed to parse subnet: ", subnet, " ", err.Error())
×
UNCOV
133
                return false
×
UNCOV
134
        }
×
135
        for _, addr := range addrs {
2✔
136
                agent.log.Info("vlan interface ip address: ", addr.String())
1✔
137
                ipAddr := addr.IP.To4()
1✔
138
                if ipAddr == nil {
2✔
139
                        ipAddr = addr.IP.To16()
1✔
140
                }
1✔
141
                if ipAddr != nil && ipnet.Contains(ipAddr) {
2✔
142
                        return true
1✔
143
                }
1✔
144
        }
145
        return false
1✔
146
}
147

148
func (agent *HostAgent) updateResetConfFile() error {
×
UNCOV
149
        resetFile := "/usr/local/var/lib/opflex-agent-ovs/reboot-conf.d/reset.conf"
×
150
        t := time.Now()
×
151
        err := os.WriteFile(resetFile, []byte(t.String()), 0644)
×
152
        return err
×
153
}
×
154

155
func (agent *HostAgent) isMultiCastRoutePresent(link netlink.Link) bool {
×
156
        routes, err := netlink.RouteList(link, netlink.FAMILY_V4)
×
157
        if err != nil {
×
158
                agent.log.Error("Failed to list routes: ", err)
×
159
                return false
×
UNCOV
160
        }
×
161
        for _, route := range routes {
×
UNCOV
162
                if route.Dst != nil && route.Dst.String() == MCAST_ROUTE_DEST {
×
UNCOV
163
                        return true
×
164
                }
×
165
        }
166
        return false
×
167
}
168

169
func (agent *HostAgent) addMultiCastRoute(link netlink.Link, name string) {
×
170
        if agent.isMultiCastRoutePresent(link) {
×
171
                agent.log.Info("Multicast route already present for interface ", name)
×
172
                return
×
173
        }
×
174
        retryCount := agent.config.DhcpRenewMaxRetryCount
×
175
        for i := 0; i < retryCount; i++ {
×
176
                cmd := exec.Command("ip", "route", "add", MCAST_ROUTE_DEST, "dev", name, "proto", "static", "scope", "link", "metric", "401")
×
177
                agent.log.Info("Executing command:", cmd.String())
×
178
                opt, err := cmd.Output()
×
179
                if err != nil {
×
180
                        agent.log.Error("Failed to add multicast route : ", err.Error(), " ", string(opt))
×
181
                        continue
×
182
                } else {
×
183
                        agent.log.Info(string(opt))
×
184
                }
×
UNCOV
185
                if agent.isMultiCastRoutePresent(link) {
×
UNCOV
186
                        agent.log.Info("Added Multicast route successfully ")
×
UNCOV
187
                        return
×
188
                }
×
189
                agent.log.Error("Failed to add Multicast route...iteration:", i+1)
×
190
        }
191
}
192

193
func (agent *HostAgent) releaseVlanIp(name string) bool {
×
194
        released := false
×
195
        retryCount := agent.config.DhcpRenewMaxRetryCount
×
196
        dhcpDelay := time.Duration(agent.config.DhcpDelay)
×
197
        for i := 0; i < retryCount; i++ {
×
198
                time.Sleep(dhcpDelay * time.Second)
×
199
                cmd := exec.Command("dhclient", "-r", name, "--timeout", "30", "-cf", DHCLIENT_CONF)
×
200
                agent.log.Info("Executing command:", cmd.String())
×
201
                opt, err := cmd.Output()
×
202
                if err != nil {
×
203
                        agent.log.Error("Failed to release ip : ", err.Error(), " ", string(opt))
×
204
                        continue
×
205
                } else {
×
206
                        agent.log.Info(string(opt))
×
UNCOV
207
                }
×
208
                if !agent.isIpV4Present(name) {
×
UNCOV
209
                        agent.log.Info("vlan interface ip released")
×
210
                        released = true
×
UNCOV
211
                        break
×
212
                }
213
                agent.log.Info("vlan interface ip not released..iteration: ", i+1)
×
214
        }
215
        return released
×
216
}
217

218
func (agent *HostAgent) renewVlanIp(name string) bool {
×
219
        renewed := false
×
220
        retryCount := agent.config.DhcpRenewMaxRetryCount
×
221
        dhcpDelay := time.Duration(agent.config.DhcpDelay)
×
222
        for i := 0; i < retryCount; i++ {
×
223
                time.Sleep(dhcpDelay * time.Second)
×
224
                cmd := exec.Command("dhclient", name, "--timeout", "30", "-cf", DHCLIENT_CONF)
×
225
                agent.log.Info("Executing command:", cmd.String())
×
226
                opt, err := cmd.Output()
×
227
                if err != nil {
×
228
                        agent.log.Error("Failed to renew ip : ", err.Error(), " ", string(opt))
×
229
                        continue
×
230
                } else {
×
UNCOV
231
                        agent.log.Info(string(opt))
×
232
                }
×
233
                if !agent.isIpV4Present(name) {
×
UNCOV
234
                        agent.log.Info("vlan interface ip is not renewed..iteration:", i+1)
×
235
                        continue
×
236
                }
UNCOV
237
                renewed = true
×
UNCOV
238
                break
×
239
        }
UNCOV
240
        return renewed
×
241
}
242

UNCOV
243
func (agent *HostAgent) dhclientOneshotRenew(name string) bool {
×
UNCOV
244
        cmd := exec.Command("dhclient", name, "-1", "--timeout", "30", "-cf", DHCLIENT_CONF)
×
UNCOV
245
        agent.log.Info("Executing command:", cmd.String())
×
246
        opt, err := cmd.Output()
×
247
        if err != nil {
×
UNCOV
248
                agent.log.Error("Failed to renew ip : ", err.Error(), " ", string(opt))
×
UNCOV
249
                return false
×
250
        }
×
251
        agent.log.Info(string(opt))
×
252
        return true
×
253
}
254

UNCOV
255
func (agent *HostAgent) checkDhclientLease(interfaceName string) bool {
×
UNCOV
256
        pattern := fmt.Sprintf(`^\s*interface\s+"%s"\s*;\s*$`, regexp.QuoteMeta(interfaceName))
×
257
        re, _ := regexp.Compile(pattern)
×
258
        var dhcpLeaseFound bool
×
UNCOV
259
        walkErr := filepath.WalkDir(dhclientLeaseDir, func(path string, d fs.DirEntry, err error) error {
×
UNCOV
260
                if err != nil {
×
UNCOV
261
                        return err
×
UNCOV
262
                }
×
UNCOV
263
                if d.IsDir() {
×
264
                        return nil
×
265
                }
×
UNCOV
266
                if !strings.HasSuffix(path, ".leases") {
×
UNCOV
267
                        return nil
×
UNCOV
268
                }
×
269
                file, err := os.Open(path)
×
270
                if err != nil {
×
271
                        agent.log.Warnf("Could not open file %s: %v", path, err)
×
272
                        return nil
×
UNCOV
273
                }
×
274
                defer file.Close()
×
275
                scanner := bufio.NewScanner(file)
×
276
                for scanner.Scan() {
×
277
                        if re.MatchString(scanner.Text()) {
×
UNCOV
278
                                dhcpLeaseFound = true
×
UNCOV
279
                                return fs.SkipAll
×
280
                        }
×
281
                }
282
                if err := scanner.Err(); err != nil {
×
283
                        agent.log.Warnf("Error reading file %s: %v", path, err)
×
284
                }
×
UNCOV
285
                return nil
×
286
        })
287
        switch {
×
288
        case dhcpLeaseFound:
×
UNCOV
289
                return true
×
290
        case walkErr != nil:
×
291
                agent.log.Error("Failed to check dhclient lease: ", walkErr)
×
292
                return false
×
293
        default:
×
294
                return false
×
295
        }
296
}
297

298
func (agent *HostAgent) ensureDhclientLease() {
×
299
        agent.dhcpMutex.Lock()
×
300
        defer agent.dhcpMutex.Unlock()
×
301
        links, err := netlink.LinkList()
×
UNCOV
302
        if err != nil {
×
303
                agent.log.Error("Could not enumerate interfaces: ", err)
×
304
                return
×
305
        }
×
306
        for _, link := range links {
×
UNCOV
307
                link, ok := link.(*netlink.Vlan)
×
308
                if !ok || link.VlanId != int(agent.config.AciInfraVlan) {
×
309
                        continue
×
310
                }
311
                agent.log.Infof("Checking dhclient lease for interface %s", link.Name)
×
UNCOV
312
                if agent.checkDhclientLease(link.Name) {
×
UNCOV
313
                        agent.log.Infof("Lease found for interface %s", link.Name)
×
UNCOV
314
                        return
×
UNCOV
315
                }
×
UNCOV
316
                retryCount := agent.config.DhcpRenewMaxRetryCount
×
UNCOV
317
                dhcpDelay := time.Duration(agent.config.DhcpDelay)
×
UNCOV
318
                for i := range retryCount {
×
UNCOV
319
                        agent.log.Infof("Renewing dhclient lease for interface %s", link.Name)
×
UNCOV
320
                        if agent.dhclientOneshotRenew(link.Name) {
×
UNCOV
321
                                if agent.checkDhclientLease(link.Name) {
×
UNCOV
322
                                        agent.log.Infof("Lease populated for interface %s", link.Name)
×
UNCOV
323
                                        return
×
UNCOV
324
                                } else {
×
UNCOV
325
                                        agent.log.Error("dhclient lease not found..iteration:", i+1)
×
UNCOV
326
                                }
×
UNCOV
327
                        } else {
×
UNCOV
328
                                agent.log.Errorf("Failed to renew ip for interface %s.. iteration:%d", link.Name, i+1)
×
UNCOV
329
                        }
×
UNCOV
330
                        if i == retryCount-1 {
×
UNCOV
331
                                agent.log.Errorf("Failed to get dhclient lease for interface %s. Stopped trying after %d iterations", link.Name, retryCount)
×
UNCOV
332
                                return
×
UNCOV
333
                        }
×
UNCOV
334
                        time.Sleep(dhcpDelay * time.Second)
×
335
                }
336
        }
337
}
338

339
func (agent *HostAgent) doDhcpRenew(aciPodSubnet string) {
1✔
340
        retryCount := agent.config.DhcpRenewMaxRetryCount
1✔
341
        agent.log.Info("old aci-pod annotiation for multipod ", agent.aciPodAnnotation)
1✔
342
        agent.log.Info("new aci-pod annotiation for multipod ", aciPodSubnet)
1✔
343
        // no dhcp release-renew for none to pod-<id>-subnet case
1✔
344
        // as this is an odev connect case
1✔
345
        if agent.aciPodAnnotation == "none" &&
1✔
346
                aciPodSubnet != "" && aciPodSubnet != "none" {
1✔
347
                return
×
348
        }
×
349
        agent.dhcpMutex.Lock()
1✔
350
        defer agent.dhcpMutex.Unlock()
1✔
351
        links, err := netlink.LinkList()
1✔
352
        if err != nil {
1✔
353
                agent.log.Error("Could not enumerate interfaces: ", err)
×
354
                return
×
355
        }
×
356
        var subnet string
1✔
357
        if aciPodSubnet != "none" {
2✔
358
                subnetSlice := strings.Split(aciPodSubnet, "-")
1✔
359
                if len(subnetSlice) > 2 {
1✔
360
                        subnet = subnetSlice[2]
×
361
                }
×
362
        }
363
        var oldsubnet string
1✔
364
        if agent.aciPodAnnotation != "none" && agent.aciPodAnnotation != "" {
2✔
365
                subnetSlice := strings.Split(agent.aciPodAnnotation, "-")
1✔
366
                if len(subnetSlice) > 2 {
1✔
367
                        oldsubnet = subnetSlice[2]
×
UNCOV
368
                }
×
369
        }
370
        for _, link := range links {
2✔
371
                switch link := link.(type) {
1✔
372
                case *netlink.Vlan:
×
373
                        // find link with matching vlan
×
UNCOV
374
                        if link.VlanId != int(agent.config.AciInfraVlan) {
×
UNCOV
375
                                continue
×
376
                        }
377
                        if aciPodSubnet != "none" {
×
378
                                if agent.isIpSameSubnet(link.Name, subnet) {
×
379
                                        agent.log.Info("Ip already from same subnet ", subnet)
×
380
                                        break
×
381
                                }
382
                        }
383
                        success := false
×
384
                        for i := 0; i < retryCount; i++ {
×
385
                                if !agent.releaseVlanIp(link.Name) {
×
386
                                        agent.log.Error("FAILURE: Failed to release vlan interface ip, stopped retrying")
×
UNCOV
387
                                        break
×
388
                                }
389
                                if !agent.renewVlanIp(link.Name) {
×
390
                                        agent.log.Error("FAILURE: Failed to renew vlan interface ip, stopped retrying")
×
391
                                        break
×
392
                                }
393
                                if aciPodSubnet != "none" {
×
394
                                        if agent.isIpSameSubnet(link.Name, subnet) {
×
395
                                                success = true
×
396
                                                break
×
UNCOV
397
                                        } else {
×
UNCOV
398
                                                agent.log.Info("Interface ip is not from the subnet ", subnet, " iteration:", i+1)
×
399
                                        }
×
400
                                } else if oldsubnet != "" {
×
401
                                        if !agent.isIpSameSubnet(link.Name, oldsubnet) {
×
402
                                                success = true
×
403
                                                agent.log.Info("Interface ip is not from old subnet ", oldsubnet)
×
404
                                                break
×
405
                                        }
406
                                        agent.log.Info("Interface ip is of old pod subnet ", oldsubnet, " iteration:", i+1)
×
407
                                } else {
×
408
                                        agent.log.Info("dhcp release and renew done. Iteration : ", i+1)
×
409
                                }
×
410
                        }
411
                        if (aciPodSubnet != "none" && !success) || (aciPodSubnet == "none" && oldsubnet != "" && !success) {
×
412
                                agent.log.Error("FAILURE: Failed to assign an ip from new pod subnet to vlan interface")
×
UNCOV
413
                        }
×
414
                        agent.addMultiCastRoute(link, link.Name)
×
415
                }
416
        }
417

418
}
419

420
func (agent *HostAgent) discoverHostConfig() (conf *HostAgentNodeConfig) {
1✔
421
        if agent.config.OpflexMode == "overlay" {
2✔
422
                conf = &HostAgentNodeConfig{}
1✔
423
                conf.OpflexPeerIp = "127.0.0.1"
1✔
424
                agent.log.Debug("\n  == Opflex: Running in overlay mode ==\n")
1✔
425
                return
1✔
426
        } else if agent.config.OpflexMode == "dpu" {
3✔
427
                conf = &HostAgentNodeConfig{}
1✔
428
                conf.VxlanIface = "bond0.4093"
1✔
429
                conf.UplinkIface = "bond0"
1✔
430
                conf.VxlanAnycastIp = "10.0.0.32"
1✔
431
                conf.OpflexPeerIp = "10.0.0.30"
1✔
432
                if agent.config.InterfaceMtu == 0 {
2✔
433
                        agent.config.InterfaceMtu = 1500 - agent.config.InterfaceMtuHeadroom
1✔
434
                }
1✔
435
                agent.log.Debug("\n == Opflex: Running on dpu ==\n")
1✔
436
                return
1✔
437
        }
438

439
        links, err := netlink.LinkList()
1✔
440
        if err != nil {
1✔
441
                agent.log.Error("Could not enumerate interfaces: ", err)
×
442
                description := "Could_not_enumerate_interfaces"
×
443
                agent.createFaultOnAgent(description, 3)
×
444
                return
×
445
        }
×
446

447
        for _, link := range links {
2✔
448
                switch link := link.(type) {
1✔
UNCOV
449
                case *netlink.Vlan:
×
UNCOV
450
                        // find link with matching vlan
×
UNCOV
451
                        if link.VlanId != int(agent.config.AciInfraVlan) {
×
UNCOV
452
                                continue
×
453
                        }
454
                        // if the interface MTU was not explicitly set by
455
                        // the user, use the link MTU
UNCOV
456
                        if agent.config.InterfaceMtu == 0 {
×
UNCOV
457
                                agent.config.InterfaceMtu = link.MTU - agent.config.InterfaceMtuHeadroom
×
UNCOV
458
                        }
×
459
                        // giving extra headroom of 100 bytes unless specified otherwise
UNCOV
460
                        configMtu := agent.config.InterfaceMtuHeadroom + agent.config.InterfaceMtu
×
UNCOV
461
                        if link.MTU < configMtu {
×
UNCOV
462
                                agent.log.WithFields(logrus.Fields{
×
UNCOV
463
                                        "name": link.Name,
×
UNCOV
464
                                        "vlan": agent.config.AciInfraVlan,
×
UNCOV
465
                                        "mtu":  link.MTU,
×
UNCOV
466
                                }).Error("OpFlex link MTU must be >= ", configMtu)
×
UNCOV
467
                                description := "User_configured_MTU_exceeds_opflex_MTU"
×
UNCOV
468
                                agent.createFaultOnAgent(description, 4)
×
UNCOV
469
                                return
×
UNCOV
470
                        }
×
471

472
                        // find parent link
UNCOV
473
                        var parent netlink.Link
×
UNCOV
474
                        for _, plink := range links {
×
UNCOV
475
                                if plink.Attrs().Index != link.ParentIndex {
×
UNCOV
476
                                        continue
×
477
                                }
478

UNCOV
479
                                parent = plink
×
UNCOV
480
                                if parent.Attrs().MTU < configMtu {
×
UNCOV
481
                                        agent.log.WithFields(logrus.Fields{
×
UNCOV
482
                                                "name": parent.Attrs().Name,
×
UNCOV
483
                                                "vlan": agent.config.AciInfraVlan,
×
UNCOV
484
                                                "mtu":  parent.Attrs().MTU,
×
UNCOV
485
                                        }).Error("Uplink MTU must be >= ", configMtu)
×
UNCOV
486
                                        description := "User_configured_MTU_exceed_uplink_MTU"
×
UNCOV
487
                                        agent.createFaultOnAgent(description, 5)
×
UNCOV
488
                                        return
×
UNCOV
489
                                }
×
490
                        }
UNCOV
491
                        if parent == nil {
×
UNCOV
492
                                agent.log.WithFields(logrus.Fields{
×
UNCOV
493
                                        "index": link.ParentIndex,
×
UNCOV
494
                                        "name":  link.Name,
×
UNCOV
495
                                }).Error("Could not find parent link for OpFlex interface")
×
UNCOV
496
                                description := "Could_not_find_parent_link_for_OpFlex_interface"
×
UNCOV
497
                                agent.createFaultOnAgent(description, 6)
×
UNCOV
498
                                return
×
UNCOV
499
                        }
×
500

501
                        // Find address of link to compute anycast and peer IPs
UNCOV
502
                        addrs, err := netlink.AddrList(link, 2)
×
UNCOV
503
                        if err != nil {
×
UNCOV
504
                                agent.log.WithFields(logrus.Fields{
×
UNCOV
505
                                        "name": link.Name,
×
UNCOV
506
                                }).Error("Could not enumerate link addresses: ", err)
×
UNCOV
507
                                description := "Could_not_enumerate_link_addresses"
×
UNCOV
508
                                agent.createFaultOnAgent(description, 7)
×
UNCOV
509
                                return
×
UNCOV
510
                        }
×
UNCOV
511
                        var anycast net.IP
×
UNCOV
512
                        var peerIp net.IP
×
UNCOV
513
                        for _, addr := range addrs {
×
UNCOV
514
                                if addr.IP.To4() == nil || addr.IP.IsLoopback() {
×
UNCOV
515
                                        continue
×
516
                                }
UNCOV
517
                                anycast = addr.IP.Mask(addr.Mask)
×
UNCOV
518
                                anycast[len(anycast)-1] = 32
×
UNCOV
519
                                peerIp = addr.IP.Mask(addr.Mask)
×
UNCOV
520
                                peerIp[len(peerIp)-1] = 30
×
521
                        }
522

UNCOV
523
                        if anycast == nil {
×
UNCOV
524
                                agent.log.WithFields(logrus.Fields{
×
UNCOV
525
                                        "name": link.Name,
×
UNCOV
526
                                        "vlan": agent.config.AciInfraVlan,
×
UNCOV
527
                                }).Error("IP address not set for OpFlex link")
×
UNCOV
528
                                description := "IP_address_not_set_for_OpFlex_link"
×
UNCOV
529
                                agent.createFaultOnAgent(description, 8)
×
UNCOV
530
                                return
×
UNCOV
531
                        }
×
532

UNCOV
533
                        conf = &HostAgentNodeConfig{}
×
UNCOV
534
                        conf.VxlanIface = link.Name
×
UNCOV
535
                        conf.UplinkIface = parent.Attrs().Name
×
UNCOV
536
                        conf.VxlanAnycastIp = anycast.String()
×
UNCOV
537
                        conf.OpflexPeerIp = peerIp.String()
×
538
                }
539
        }
540

541
        if conf != nil {
1✔
UNCOV
542
                intf, err := net.InterfaceByName(conf.UplinkIface)
×
UNCOV
543
                if err == nil {
×
UNCOV
544
                        conf.UplinkMacAdress = intf.HardwareAddr.String()
×
UNCOV
545
                        if conf.UplinkMacAdress != agent.config.UplinkMacAdress {
×
UNCOV
546
                                agent.log.Info("UplinkMacAdress updated from ", agent.config.UplinkMacAdress, " to ", conf.UplinkMacAdress)
×
UNCOV
547
                                agent.scheduleSyncNodeInfo()
×
UNCOV
548
                        }
×
UNCOV
549
                        return
×
550
                }
551
        }
552

553
        agent.log.WithFields(logrus.Fields{"vlan": agent.config.AciInfraVlan}).
1✔
554
                Error("Could not find suitable host uplink interface for vlan")
1✔
555
        description := "Could_not_find_suitable_host_uplink_interface_for_vlan"
1✔
556
        agent.createFaultOnAgent(description, 9)
1✔
557
        return
1✔
558
}
559

560
var opflexConfigBase = initTempl("opflex-config-base", `{
561
    "opflex": {
562
        "name": "{{.NodeName | js}}",
563
        "domain": "{{print "comp/prov-" .AciVmmDomainType "/ctrlr-[" .AciVmmDomain "]-" .AciVmmController "/sw-InsiemeLSOid" | js}}",
564
        "peers": [
565
            {"hostname": "{{.OpflexPeerIp | js}}", "port": "8009"}
566
        ]
567
    } ,
568
    "endpoint-sources": {
569
        "filesystem": ["{{.OpFlexEndpointDir | js}}"]
570
    },
571
    "service-sources": {
572
        "filesystem": ["{{.OpFlexServiceDir | js}}"]
573
    },
574
    "snat-sources": {
575
        "filesystem": ["{{.OpFlexSnatDir | js}}"]
576
    },
577
        "netpol-sources": {
578
                "filesystem": ["{{.OpFlexNetPolDir | js}}"]
579
        },
580
    "drop-log-config-sources": {
581
        "filesystem": ["{{.OpFlexDropLogConfigDir | js}}"]
582
    },
583
    "packet-event-notif": {
584
        "socket-name": ["{{.PacketEventNotificationSock | js}}"]
585
    },
586
    "host-agent-fault-sources": {
587
        "filesystem": ["{{.OpFlexFaultDir | js}}"]
588
    }
589
}
590
`)
591

592
var opflexConfigVxlan = initTempl("opflex-config-vxlan", `{
593
    "renderers": {
594
        "stitched-mode": {
595
            "int-bridge-name": "{{.IntBridgeName | js}}",
596
            "access-bridge-name": "{{.AccessBridgeName | js}}",
597
            "encap": {
598
                "vxlan" : {
599
                    "encap-iface": "vxlan0",
600
                    "uplink-iface": "{{.VxlanIface | js}}",
601
                    "uplink-vlan": "{{.AciInfraVlan}}",
602
                    "remote-ip": "{{.VxlanAnycastIp | js}}",
603
                    "remote-port": 8472
604
                }
605
            },
606
            "flowid-cache-dir": "{{.OpFlexFlowIdCacheDir | js}}",
607
            "mcast-group-file": "{{.OpFlexMcastFile | js}}",
608
            "drop-log": {
609
                "geneve" : {
610
                    "int-br-iface": "{{.DropLogIntInterface | js}}",
611
                    "access-br-iface": "{{.DropLogAccessInterface | js}}",
612
                    "remote-ip": "{{.OpFlexDropLogRemoteIp | js}}"
613
                }
614
            },
615
            "statistics": {
616
                "service": {
617
                    "flow-disabled": "true",
618
                    "enabled": "false"
619
                }
620
            }
621
        }
622
    }
623
}
624
`)
625

626
var opflexConfigVlan = initTempl("opflex-config-vlan", `{
627
    "renderers": {
628
        "stitched-mode": {
629
            "int-bridge-name": "{{.IntBridgeName | js}}",
630
            "access-bridge-name": "{{.AccessBridgeName | js}}",
631
            "encap": {
632
                "vlan" : {
633
                    "encap-iface": "{{.UplinkIface | js}}"
634
                }
635
            },
636
            "flowid-cache-dir": "{{.OpFlexFlowIdCacheDir | js}}",
637
            "mcast-group-file": "{{.OpFlexMcastFile | js}}",
638
            "drop-log": {
639
                "geneve" : {
640
                    "int-br-iface": "{{.DropLogIntInterface | js}}",
641
                    "access-br-iface": "{{.DropLogAccessInterface | js}}",
642
                    "remote-ip": "{{.OpFlexDropLogRemoteIp | js}}"
643
                }
644
            }
645
        }
646
    }
647
}
648
`)
649

650
func initTempl(name, templ string) *template.Template {
1✔
651
        return template.Must(template.New(name).Parse(templ))
1✔
652
}
1✔
653

654
func (agent *HostAgent) writeConfigFile(name string,
655
        templ *template.Template) error {
1✔
656
        var buffer bytes.Buffer
1✔
657
        templ.Execute(&buffer, agent.config)
1✔
658

1✔
659
        path := filepath.Join(agent.config.OpFlexConfigPath, name)
1✔
660

1✔
661
        existing, err := os.ReadFile(path)
1✔
662
        if err != nil {
2✔
663
                if bytes.Equal(existing, buffer.Bytes()) {
1✔
664
                        agent.log.Info("OpFlex agent configuration file ",
×
665
                                path, " unchanged")
×
UNCOV
666
                        return nil
×
667
                }
×
668
        }
669

670
        f, err := os.Create(path)
1✔
671
        if err != nil {
2✔
672
                return err
1✔
673
        }
1✔
674
        // in case there's an error in the write
675
        defer f.Close()
1✔
676
        _, err = f.Write(buffer.Bytes())
1✔
677
        if err != nil {
1✔
UNCOV
678
                return err
×
UNCOV
679
        }
×
680
        err = f.Close()
1✔
681
        if err != nil {
1✔
UNCOV
682
                return err
×
UNCOV
683
        }
×
684

685
        agent.log.Info("Wrote OpFlex agent configuration file ", path)
1✔
686

1✔
687
        return nil
1✔
688
}
689

690
func (agent *HostAgent) updateOpflexConfig() {
1✔
691
        if agent.config.ChainedMode {
2✔
692
                return
1✔
693
        }
1✔
694
        if agent.config.OpFlexConfigPath == "" {
2✔
695
                agent.log.Debug("OpFlex agent configuration path not set")
1✔
696
                return
1✔
697
        }
1✔
698
        if agent.config.OpFlexFaultDir == "" {
2✔
699
                agent.log.Warn("OpFlex Fault directory not set")
1✔
700
        } else {
1✔
UNCOV
701
                err := agent.removeAllFiles(agent.config.OpFlexFaultDir)
×
UNCOV
702
                if err != nil {
×
UNCOV
703
                        agent.log.Error("Not able to clear Fault files on agent: ", err.Error())
×
UNCOV
704
                }
×
705
        }
706

707
        agent.indexMutex.Lock()
1✔
708
        newNodeConfig := agent.discoverHostConfig()
1✔
709
        agent.indexMutex.Unlock()
1✔
710
        if newNodeConfig == nil {
1✔
UNCOV
711
                panic(errors.New("Node configuration autodiscovery failed"))
×
712
        }
713

714
        agent.indexMutex.Lock()
1✔
715
        if !reflect.DeepEqual(*newNodeConfig, agent.config.HostAgentNodeConfig) ||
1✔
716
                !agent.opflexConfigWritten {
2✔
717
                // reset opflexConfigWritten flag when node-config differs
1✔
718
                agent.opflexConfigWritten = false
1✔
719

1✔
720
                agent.config.HostAgentNodeConfig = *newNodeConfig
1✔
721
                agent.log.WithFields(logrus.Fields{
1✔
722
                        "uplink-iface":     newNodeConfig.UplinkIface,
1✔
723
                        "vxlan-iface":      newNodeConfig.VxlanIface,
1✔
724
                        "vxlan-anycast-ip": newNodeConfig.VxlanAnycastIp,
1✔
725
                        "opflex-peer-ip":   newNodeConfig.OpflexPeerIp,
1✔
726
                        "opflex-mode":      agent.config.OpflexMode,
1✔
727
                }).Info("Discovered node configuration")
1✔
728
                if err := agent.writeOpflexConfig(); err == nil {
1✔
UNCOV
729
                        agent.opflexConfigWritten = true
×
730
                } else {
1✔
731
                        agent.log.Error("Failed to write OpFlex agent config: ", err)
1✔
732
                }
1✔
733
        }
734
        agent.indexMutex.Unlock()
1✔
735
}
736

737
func (agent *HostAgent) writeOpflexConfig() error {
1✔
738
        err := agent.writeConfigFile("01-base.conf", opflexConfigBase)
1✔
739
        if err != nil {
2✔
740
                return err
1✔
741
        }
1✔
742

743
        var rtempl *template.Template
1✔
744
        if agent.config.EncapType == "vlan" {
2✔
745
                rtempl = opflexConfigVlan
1✔
746
        } else if agent.config.EncapType == "vxlan" {
3✔
747
                rtempl = opflexConfigVxlan
1✔
748
        } else {
1✔
UNCOV
749
                panic("Unsupported encap type: " + agent.config.EncapType)
×
750
        }
751

752
        err = agent.writeConfigFile("10-renderer.conf", rtempl)
1✔
753
        if err != nil {
1✔
UNCOV
754
                return err
×
UNCOV
755
        }
×
756
        return nil
1✔
757
}
758

759
func (agent *HostAgent) removeAllFiles(dir string) error {
1✔
760
        d, err := os.Open(dir)
1✔
761
        if err != nil {
1✔
UNCOV
762
                return err
×
UNCOV
763
        }
×
764
        defer d.Close()
1✔
765
        names, err := d.Readdirnames(-1)
1✔
766
        if err != nil {
1✔
UNCOV
767
                return err
×
UNCOV
768
        }
×
769
        for _, name := range names {
1✔
UNCOV
770
                err = os.RemoveAll(filepath.Join(dir, name))
×
UNCOV
771
                if err != nil {
×
UNCOV
772
                        agent.log.Error("Not able to clear the Fault Files  ", err)
×
UNCOV
773
                        return err
×
UNCOV
774
                }
×
775
        }
776
        return nil
1✔
777
}
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

© 2025 Coveralls, Inc