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

noironetworks / aci-containers / 10932

13 Sep 2025 06:27PM UTC coverage: 66.063% (-2.4%) from 68.496%
10932

push

travis-pro

web-flow
Merge pull request #1565 from noironetworks/vmm-lite

Added support for VMM lite future

53 of 779 new or added lines in 12 files covered. (6.8%)

5 existing lines in 2 files now uncovered.

13356 of 20217 relevant lines covered (66.06%)

0.75 hits per line

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

0.0
/pkg/controller/aaepmonitor.go
1
// Copyright 2019 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 WARRATIES 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
// Handlers for AaepMonitor CR updates.
16

17
package controller
18

19
import (
20
        "context"
21
        "encoding/json"
22
        "fmt"
23
        "regexp"
24
        "strconv"
25
        "strings"
26

27
        amv1 "github.com/noironetworks/aci-containers/pkg/aaepmonitor/apis/aci.attachmentmonitor/v1"
28
        aaepmonitorclientset "github.com/noironetworks/aci-containers/pkg/aaepmonitor/clientset/versioned"
29
        "github.com/noironetworks/aci-containers/pkg/apicapi"
30

31
        nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
32
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
        "k8s.io/apimachinery/pkg/runtime"
34
        "k8s.io/apimachinery/pkg/watch"
35
        "k8s.io/client-go/tools/cache"
36
        "k8s.io/kubernetes/pkg/controller"
37
)
38

39
const (
40
        aaepMonitorCRDName = "aaepmonitors.aci.attachmentmonitor"
41
)
42

NEW
43
func (cont *AciController) queueAaepMonitorConfigByKey(key string) {
×
NEW
44
        cont.aaepMonitorConfigQueue.Add(key)
×
NEW
45
}
×
46

NEW
47
func aaepMonitorInit(cont *AciController, stopCh <-chan struct{}) {
×
NEW
48
        restconfig := cont.env.RESTConfig()
×
NEW
49
        aaepMonitorClient, err := aaepmonitorclientset.NewForConfig(restconfig)
×
NEW
50
        if err != nil {
×
NEW
51
                cont.log.Errorf("Failed to intialize aaepMonitorClient")
×
NEW
52
                return
×
NEW
53
        }
×
54

NEW
55
        cont.initAaepMonitorInformerFromClient(aaepMonitorClient)
×
NEW
56
        go cont.aaepMonitorInformer.Run(stopCh)
×
NEW
57
        go cont.processQueue(cont.aaepMonitorConfigQueue, cont.aaepMonitorInformer.GetIndexer(),
×
NEW
58
                func(obj interface{}) bool {
×
NEW
59
                        return cont.handleAaepMonitorConfigurationUpdate(obj)
×
NEW
60
                }, func(key string) bool {
×
NEW
61
                        return cont.handleAaepMonitorConfigurationDelete(key)
×
NEW
62
                }, nil, stopCh)
×
NEW
63
        cache.WaitForCacheSync(stopCh, cont.aaepMonitorInformer.HasSynced)
×
64
}
65

66
func (cont *AciController) initAaepMonitorInformerFromClient(
NEW
67
        aaepMonitorClient *aaepmonitorclientset.Clientset) {
×
NEW
68
        cont.initAaepMonitorInformerBase(
×
NEW
69
                &cache.ListWatch{
×
NEW
70
                        ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
×
NEW
71
                                return aaepMonitorClient.AciV1().AaepMonitors().List(context.TODO(), options)
×
NEW
72
                        },
×
NEW
73
                        WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
×
NEW
74
                                return aaepMonitorClient.AciV1().AaepMonitors().Watch(context.TODO(), options)
×
NEW
75
                        },
×
76
                })
77
}
78

NEW
79
func (cont *AciController) initAaepMonitorInformerBase(listWatch *cache.ListWatch) {
×
NEW
80
        cont.aaepMonitorInformer = cache.NewSharedIndexInformer(
×
NEW
81
                listWatch,
×
NEW
82
                &amv1.AaepMonitor{},
×
NEW
83
                controller.NoResyncPeriodFunc(),
×
NEW
84
                cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
×
NEW
85
        )
×
NEW
86
        cont.aaepMonitorInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
×
NEW
87
                AddFunc: func(obj interface{}) {
×
NEW
88
                        cont.aaepMonitorConfAdded(obj)
×
NEW
89
                },
×
NEW
90
                UpdateFunc: func(oldobj interface{}, newobj interface{}) {
×
NEW
91
                        cont.aaepMonitorConfUpdate(oldobj, newobj)
×
NEW
92
                },
×
NEW
93
                DeleteFunc: func(obj interface{}) {
×
NEW
94
                        cont.aaepMonitorConfDelete(obj)
×
NEW
95
                },
×
96
        })
97
}
98

NEW
99
func (cont *AciController) aaepMonitorConfAdded(obj interface{}) {
×
NEW
100
        aaepMonitorConfig, ok := obj.(*amv1.AaepMonitor)
×
NEW
101
        if !ok {
×
NEW
102
                cont.log.Error("aaepMonitorConfAdded: Bad object type")
×
NEW
103
                return
×
NEW
104
        }
×
NEW
105
        key, err := cache.MetaNamespaceKeyFunc(aaepMonitorConfig)
×
NEW
106
        if err != nil {
×
NEW
107
                return
×
NEW
108
        }
×
NEW
109
        cont.queueAaepMonitorConfigByKey(key)
×
110
}
111

NEW
112
func (cont *AciController) aaepMonitorConfUpdate(oldobj interface{}, newobj interface{}) {
×
NEW
113
        newAaepMonitorConfig := newobj.(*amv1.AaepMonitor)
×
NEW
114

×
NEW
115
        key, err := cache.MetaNamespaceKeyFunc(newAaepMonitorConfig)
×
NEW
116
        if err != nil {
×
NEW
117
                return
×
NEW
118
        }
×
NEW
119
        cont.queueAaepMonitorConfigByKey(key)
×
120
}
121

NEW
122
func (cont *AciController) aaepMonitorConfDelete(obj interface{}) {
×
NEW
123
        cont.indexMutex.Lock()
×
NEW
124
        defer cont.indexMutex.Unlock()
×
NEW
125
        aaepMonitorConfig, ok := obj.(*amv1.AaepMonitor)
×
NEW
126

×
NEW
127
        if !ok {
×
NEW
128
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
×
NEW
129
                if !ok {
×
NEW
130
                        cont.log.Errorf("Received unexpected object: ")
×
NEW
131
                        return
×
NEW
132
                }
×
NEW
133
                aaepMonitorConfig, ok = deletedState.Obj.(*amv1.AaepMonitor)
×
NEW
134
                if !ok {
×
NEW
135
                        cont.log.Errorf("DeletedFinalStateUnknown contained non-aaepmonitorconfiguration object: %v", deletedState.Obj)
×
NEW
136
                        return
×
NEW
137
                }
×
138
        }
139

NEW
140
        key, err := cache.MetaNamespaceKeyFunc(aaepMonitorConfig)
×
NEW
141
        if err != nil {
×
NEW
142
                return
×
NEW
143
        }
×
NEW
144
        cont.queueAaepMonitorConfigByKey("DELETED_" + key)
×
145
}
146

NEW
147
func (cont *AciController) handleAaepMonitorConfigurationUpdate(obj interface{}) bool {
×
NEW
148
        aaepMonitorConfig, ok := obj.(*amv1.AaepMonitor)
×
NEW
149
        if !ok {
×
NEW
150
                cont.log.Error("handleAaepMonitorConfigurationUpdate: Bad object type")
×
NEW
151
                return false
×
NEW
152
        }
×
153

NEW
154
        addedAaeps, removedAaeps := cont.getAaepDiff(aaepMonitorConfig.Spec.Aaeps)
×
NEW
155
        for _, aaepName := range addedAaeps {
×
NEW
156
                cont.reconcileNadData(aaepName)
×
NEW
157
        }
×
158

NEW
159
        for _, aaepName := range removedAaeps {
×
NEW
160
                cont.cleanAnnotationSubscriptions(aaepName)
×
NEW
161

×
NEW
162
                cont.indexMutex.Lock()
×
NEW
163
                aaepEpgDataList := cont.sharedAaepMonitor[aaepName]
×
NEW
164
                delete(cont.sharedAaepMonitor, aaepName)
×
NEW
165

×
NEW
166
                for _, aaepEpgData := range aaepEpgDataList {
×
NEW
167
                        cont.deleteNetworkAttachmentDefinition(aaepEpgData, "AaepRemovedFromCR")
×
NEW
168
                }
×
NEW
169
                cont.indexMutex.Unlock()
×
170
        }
171

NEW
172
        return false
×
173
}
174

NEW
175
func (cont *AciController) handleAaepMonitorConfigurationDelete(key string) bool {
×
NEW
176
        cont.indexMutex.Lock()
×
NEW
177
        defer cont.indexMutex.Unlock()
×
NEW
178
        for aaepName, aaepMonitorDataList := range cont.sharedAaepMonitor {
×
NEW
179
                cont.cleanAnnotationSubscriptions(aaepName)
×
NEW
180

×
NEW
181
                for _, aaepMonitorData := range aaepMonitorDataList {
×
NEW
182
                        cont.deleteNetworkAttachmentDefinition(aaepMonitorData, "CRDeleted")
×
NEW
183
                }
×
184
        }
185

NEW
186
        cont.sharedAaepMonitor = make(map[string][]*AaepMonitoringData)
×
NEW
187
        return false
×
188
}
189

NEW
190
func (cont *AciController) handleAaepEpgAttach(infraRsObj apicapi.ApicObject) {
×
NEW
191
        infraRsObjDn := infraRsObj.GetDn()
×
NEW
192
        aaepName := cont.matchesAEPFilter(infraRsObjDn)
×
NEW
193
        if aaepName == "" {
×
NEW
194
                cont.log.Debugf("Unable to find AAEP from %s in monitoring list", infraRsObjDn)
×
NEW
195
                return
×
NEW
196
        }
×
197

NEW
198
        state := infraRsObj.GetAttrStr("state")
×
NEW
199
        if state != "formed" {
×
NEW
200
                cont.log.Debugf("[AAEP-HANDLER] Skipping NAD creation: %s is with state: %s", infraRsObjDn, state)
×
NEW
201
                return
×
NEW
202
        }
×
203

NEW
204
        epgDn := infraRsObj.GetAttrStr("tDn")
×
NEW
205

×
NEW
206
        cont.apicConn.AddImmediateSubscriptionDnLocked(epgDn, []string{"tagAnnotation"}, cont.handleAnnotationAdded,
×
NEW
207
                cont.handleAnnotationDeleted)
×
NEW
208

×
NEW
209
        encap := infraRsObj.GetAttrStr("encap")
×
NEW
210
        vlanID := cont.getVlanId(encap)
×
NEW
211

×
NEW
212
        aaepEpgData := &AaepEpgAttachData{
×
NEW
213
                epgDn:     epgDn,
×
NEW
214
                encapVlan: vlanID,
×
NEW
215
        }
×
NEW
216

×
NEW
217
        aaepMonitorData := cont.collectNadData(aaepEpgData)
×
NEW
218
        if aaepMonitorData == nil {
×
NEW
219
                return
×
NEW
220
        }
×
221

NEW
222
        cont.indexMutex.Lock()
×
NEW
223
        oldAaepMonitorData, dataIndex := cont.getAaepEpgAttachDataLocked(aaepName, epgDn)
×
NEW
224
        cont.indexMutex.Unlock()
×
NEW
225

×
NEW
226
        cont.syncNADsWithAciState(aaepName, dataIndex, oldAaepMonitorData, aaepMonitorData)
×
227
}
228

NEW
229
func (cont *AciController) handleAaepEpgDetach(infraRsObjDn string) {
×
NEW
230
        aaepName := cont.matchesAEPFilter(infraRsObjDn)
×
NEW
231
        if aaepName == "" {
×
NEW
232
                cont.log.Debugf("Unable to find AAEP from %s in monitoring list", infraRsObjDn)
×
NEW
233
                return
×
NEW
234
        }
×
235

NEW
236
        epgDn := cont.getEpgDnFromInfraRsDn(infraRsObjDn)
×
NEW
237

×
NEW
238
        cont.apicConn.UnsubscribeImmediateDnLocked(epgDn, []string{"tagAnnotation"})
×
NEW
239

×
NEW
240
        cont.indexMutex.Lock()
×
NEW
241
        aaepMonitorData, dataIndex := cont.getAaepEpgAttachDataLocked(aaepName, epgDn)
×
NEW
242
        cont.indexMutex.Unlock()
×
NEW
243

×
NEW
244
        if aaepMonitorData == nil || !cont.namespaceChecks(aaepMonitorData.namespaceName, epgDn) {
×
NEW
245
                return
×
NEW
246
        }
×
247

NEW
248
        cont.indexMutex.Lock()
×
NEW
249
        cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName][:dataIndex], cont.sharedAaepMonitor[aaepName][dataIndex+1:]...)
×
NEW
250
        cont.deleteNetworkAttachmentDefinition(aaepMonitorData, "AaepEpgDetached")
×
NEW
251
        cont.indexMutex.Unlock()
×
252
}
253

NEW
254
func (cont *AciController) handleAnnotationAdded(obj apicapi.ApicObject) bool {
×
NEW
255
        annotationDn := obj.GetDn()
×
NEW
256
        epgDn := annotationDn[:strings.Index(annotationDn, "/annotationKey-")]
×
NEW
257
        aaepName, aaepMonitorData := cont.getAaepMonitoringDataForEpg(epgDn)
×
NEW
258

×
NEW
259
        if aaepMonitorData == nil || !cont.namespaceChecks(aaepMonitorData.namespaceName, epgDn) {
×
NEW
260
                cont.log.Debugf("Insufficient data for NAD creation: Either namespace not exist or monitoring data not found")
×
NEW
261
                return true
×
NEW
262
        }
×
263

NEW
264
        cont.indexMutex.Lock()
×
NEW
265
        oldAaepMonitorData, dataIndex := cont.getAaepEpgAttachDataLocked(aaepName, epgDn)
×
NEW
266
        cont.indexMutex.Unlock()
×
NEW
267

×
NEW
268
        cont.syncNADsWithAciState(aaepName, dataIndex, oldAaepMonitorData, aaepMonitorData)
×
NEW
269

×
NEW
270
        return true
×
271
}
272

NEW
273
func (cont *AciController) handleAnnotationDeleted(annotationDn string) {
×
NEW
274
        epgDn := annotationDn[:strings.Index(annotationDn, "/annotationKey-")]
×
NEW
275

×
NEW
276
        aaepName, aaepMonitorData := cont.getAaepMonitoringDataForEpg(epgDn)
×
NEW
277

×
NEW
278
        oldAaepMonitorData, dataIndex := cont.findAaepEpgAttachDataInCache(epgDn)
×
NEW
279

×
NEW
280
        if oldAaepMonitorData == nil {
×
NEW
281
                return
×
NEW
282
        }
×
283

NEW
284
        if aaepMonitorData == nil {
×
NEW
285
                cont.indexMutex.Lock()
×
NEW
286
                cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName][:dataIndex],
×
NEW
287
                        cont.sharedAaepMonitor[aaepName][dataIndex+1:]...)
×
NEW
288
                cont.deleteNetworkAttachmentDefinition(oldAaepMonitorData, "NamespaceAnnotationRemoved")
×
NEW
289
                cont.indexMutex.Unlock()
×
NEW
290
                return
×
NEW
291
        }
×
292

NEW
293
        cont.syncNADsWithAciState(aaepName, dataIndex, oldAaepMonitorData, aaepMonitorData)
×
294
}
295

NEW
296
func (cont *AciController) collectNadData(aaepEpgData *AaepEpgAttachData) *AaepMonitoringData {
×
NEW
297
        epgDn := aaepEpgData.epgDn
×
NEW
298
        epgAnnotations := cont.getEpgAnnotations(epgDn)
×
NEW
299
        namespaceName, nadName := cont.getSpecificEPGAnnotation(epgAnnotations)
×
NEW
300

×
NEW
301
        if !cont.namespaceChecks(namespaceName, epgDn) {
×
NEW
302
                return nil
×
NEW
303
        }
×
304

NEW
305
        aaepMonitoringData := &AaepMonitoringData{
×
NEW
306
                aaepEpgData:   *aaepEpgData,
×
NEW
307
                nadName:       nadName,
×
NEW
308
                namespaceName: namespaceName,
×
NEW
309
        }
×
NEW
310

×
NEW
311
        return aaepMonitoringData
×
312
}
313

NEW
314
func (cont *AciController) findAaepEpgAttachDataInCache(epgDn string) (*AaepMonitoringData, int) {
×
NEW
315
        cont.indexMutex.Lock()
×
NEW
316
        defer cont.indexMutex.Unlock()
×
NEW
317
        for aaepName := range cont.sharedAaepMonitor {
×
NEW
318
                aaepEpgData, dataIndex := cont.getAaepEpgAttachDataLocked(aaepName, epgDn)
×
NEW
319
                if aaepEpgData != nil {
×
NEW
320
                        return aaepEpgData, dataIndex
×
NEW
321
                }
×
322
        }
323

NEW
324
        return nil, -1
×
325
}
326

NEW
327
func (cont *AciController) getAaepEpgAttachDataLocked(aaepName string, epgDn string) (*AaepMonitoringData, int) {
×
NEW
328
        aaepEpgDataList, exists := cont.sharedAaepMonitor[aaepName]
×
NEW
329
        if !exists || len(aaepEpgDataList) == 0 {
×
NEW
330
                cont.log.Debugf("AAEP EPG attachment data not found for AAEP: %s", aaepName)
×
NEW
331
                return nil, -1
×
NEW
332
        }
×
333

NEW
334
        for dataIndex, aaepEpgData := range aaepEpgDataList {
×
NEW
335
                if aaepEpgData.aaepEpgData.epgDn == epgDn {
×
NEW
336
                        cont.log.Infof("Found Attach data: %v EPG : %s AAEP: %s", aaepEpgData, epgDn, aaepName)
×
NEW
337
                        return aaepEpgData, dataIndex
×
NEW
338
                }
×
339
        }
NEW
340
        return nil, -1
×
341
}
342

NEW
343
func (cont *AciController) matchesAEPFilter(infraRsObjDn string) string {
×
NEW
344
        cont.indexMutex.Lock()
×
NEW
345
        defer cont.indexMutex.Unlock()
×
NEW
346
        var aaepName string
×
NEW
347
        for aaepName = range cont.sharedAaepMonitor {
×
NEW
348
                expectedPrefix := fmt.Sprintf("uni/infra/attentp-%s", aaepName)
×
NEW
349
                if strings.HasPrefix(infraRsObjDn, expectedPrefix) {
×
NEW
350
                        return aaepName
×
NEW
351
                }
×
352
        }
NEW
353
        return ""
×
354
}
355

NEW
356
func (cont *AciController) getEpgDnFromInfraRsDn(infraRsObjDn string) string {
×
NEW
357
        re := regexp.MustCompile(`\[(.*?)\]`)
×
NEW
358
        match := re.FindStringSubmatch(infraRsObjDn)
×
NEW
359

×
NEW
360
        var epgDn string
×
NEW
361
        if len(match) > 1 {
×
NEW
362
                epgDn = match[1]
×
NEW
363
                return epgDn
×
NEW
364
        }
×
365

NEW
366
        return epgDn
×
367
}
368

NEW
369
func (cont *AciController) getAaepMonitoringDataForEpg(epgDn string) (string, *AaepMonitoringData) {
×
NEW
370
        var aaepMonitorData *AaepMonitoringData
×
NEW
371
        var aaepName string
×
NEW
372

×
NEW
373
        cont.indexMutex.Lock()
×
NEW
374
        defer cont.indexMutex.Unlock()
×
NEW
375
        for aaepName = range cont.sharedAaepMonitor {
×
NEW
376
                encap := cont.getEncapFromAaepEpgAttachObj(aaepName, epgDn)
×
NEW
377

×
NEW
378
                if encap != "" {
×
NEW
379
                        vlanID := cont.getVlanId(encap)
×
NEW
380
                        aaepEpgData := &AaepEpgAttachData{
×
NEW
381
                                epgDn:     epgDn,
×
NEW
382
                                encapVlan: vlanID,
×
NEW
383
                        }
×
NEW
384

×
NEW
385
                        aaepMonitorData = cont.collectNadData(aaepEpgData)
×
NEW
386
                        return aaepName, aaepMonitorData
×
NEW
387
                }
×
388
        }
389

NEW
390
        return "", nil
×
391
}
392

NEW
393
func (cont *AciController) cleanAnnotationSubscriptions(aaepName string) {
×
NEW
394
        aaepEpgDataList := cont.getAaepEpgAttObjDetails(aaepName)
×
NEW
395
        if aaepEpgDataList == nil {
×
NEW
396
                return
×
NEW
397
        }
×
398

NEW
399
        for _, aaepEpgData := range aaepEpgDataList {
×
NEW
400
                cont.apicConn.UnsubscribeImmediateDnLocked(aaepEpgData.epgDn, []string{"tagAnnotation"})
×
NEW
401
        }
×
402
}
403

NEW
404
func (cont *AciController) syncNADsWithAciState(aaepName string, dataIndex int, oldAaepMonitorData, aaepMonitorData *AaepMonitoringData) {
×
NEW
405
        if oldAaepMonitorData == nil {
×
NEW
406
                cont.indexMutex.Lock()
×
NEW
407
                needCacheChange := cont.createNetworkAttachmentDefinition(aaepMonitorData)
×
NEW
408
                if needCacheChange {
×
NEW
409
                        cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName], aaepMonitorData)
×
NEW
410
                }
×
NEW
411
                cont.indexMutex.Unlock()
×
NEW
412
        } else {
×
NEW
413
                if oldAaepMonitorData.namespaceName != aaepMonitorData.namespaceName {
×
NEW
414
                        cont.indexMutex.Lock()
×
NEW
415
                        cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName][:dataIndex], cont.sharedAaepMonitor[aaepName][dataIndex+1:]...)
×
NEW
416
                        cont.deleteNetworkAttachmentDefinition(oldAaepMonitorData, "NamespaceAnnotationChanged")
×
NEW
417
                        cont.indexMutex.Unlock()
×
NEW
418

×
NEW
419
                        cont.indexMutex.Lock()
×
NEW
420
                        needCacheChange := cont.createNetworkAttachmentDefinition(aaepMonitorData)
×
NEW
421
                        if needCacheChange {
×
NEW
422
                                cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName], aaepMonitorData)
×
NEW
423
                        }
×
NEW
424
                        cont.indexMutex.Unlock()
×
NEW
425
                        return
×
426
                }
427

NEW
428
                if oldAaepMonitorData.nadName != aaepMonitorData.nadName || oldAaepMonitorData.aaepEpgData.encapVlan != aaepMonitorData.aaepEpgData.encapVlan {
×
NEW
429
                        cont.indexMutex.Lock()
×
NEW
430
                        needCacheChange := cont.createNetworkAttachmentDefinition(aaepMonitorData)
×
NEW
431
                        if needCacheChange {
×
NEW
432
                                cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName][:dataIndex], cont.sharedAaepMonitor[aaepName][dataIndex+1:]...)
×
NEW
433
                                cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName], aaepMonitorData)
×
NEW
434
                        }
×
NEW
435
                        cont.indexMutex.Unlock()
×
436
                }
437
        }
438
}
439

NEW
440
func (cont *AciController) addDeferredNADs(namespaceName string) {
×
NEW
441
        cont.indexMutex.Lock()
×
NEW
442
        defer cont.indexMutex.Unlock()
×
NEW
443
        for aaepName := range cont.sharedAaepMonitor {
×
NEW
444
                aaepEpgDataList := cont.getAaepEpgAttObjDetails(aaepName)
×
NEW
445

×
NEW
446
                if aaepEpgDataList == nil {
×
NEW
447
                        continue
×
448
                }
449

NEW
450
                for _, aaepEpgData := range aaepEpgDataList {
×
NEW
451
                        aaepMonitoringData := cont.collectNadData(&aaepEpgData)
×
NEW
452
                        if aaepMonitoringData == nil || aaepMonitoringData.namespaceName != namespaceName {
×
NEW
453
                                continue
×
454
                        }
455

NEW
456
                        needCacheChange := cont.createNetworkAttachmentDefinition(aaepMonitoringData)
×
NEW
457
                        if needCacheChange {
×
NEW
458
                                cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName], aaepMonitoringData)
×
NEW
459
                        }
×
460
                }
461
        }
462
}
463

NEW
464
func (cont *AciController) cleanNADs(namespaceName string) {
×
NEW
465
        cont.indexMutex.Lock()
×
NEW
466
        defer cont.indexMutex.Unlock()
×
NEW
467
        for aaepName := range cont.sharedAaepMonitor {
×
NEW
468
                aaepEpgDataList, exists := cont.sharedAaepMonitor[aaepName]
×
NEW
469
                if !exists || len(aaepEpgDataList) == 0 {
×
NEW
470
                        continue
×
471
                }
NEW
472
                newAaepEpgDataList := []*AaepMonitoringData{}
×
NEW
473
                for _, aaepEpgData := range aaepEpgDataList {
×
NEW
474
                        if aaepEpgData.namespaceName != namespaceName {
×
NEW
475
                                newAaepEpgDataList = append(newAaepEpgDataList, aaepEpgData)
×
NEW
476
                        }
×
477
                }
NEW
478
                cont.sharedAaepMonitor[aaepName] = newAaepEpgDataList
×
479
        }
480
}
481

NEW
482
func (cont *AciController) getAaepEpgAttObjDetails(aaepName string) []AaepEpgAttachData {
×
NEW
483
        uri := fmt.Sprintf("/api/node/mo/uni/infra/attentp-%s.json?query-target=subtree&target-subtree-class=infraRsFuncToEpg", aaepName)
×
NEW
484

×
NEW
485
        resp, err := cont.apicConn.GetApicResponse(uri)
×
NEW
486
        if err != nil {
×
NEW
487
                cont.log.Errorf("Failed to get response from APIC: %v", err)
×
NEW
488
                return nil
×
NEW
489
        }
×
490

NEW
491
        if len(resp.Imdata) == 0 {
×
NEW
492
                cont.log.Debugf("Skipping NAD creation: Can't find EPGs attached with AAEP %s", aaepName)
×
NEW
493
                return nil
×
NEW
494
        }
×
495

NEW
496
        aaepEpgAttchDetails := make([]AaepEpgAttachData, 0)
×
NEW
497
        for _, respImdata := range resp.Imdata {
×
NEW
498
                aaepEpgAttachObj, ok := respImdata["infraRsFuncToEpg"]
×
NEW
499
                if !ok {
×
NEW
500
                        cont.log.Debugf("Skipping NAD creation: Empty AAEP EPG attachment object")
×
NEW
501
                        continue
×
502
                }
503

NEW
504
                if state, hasState := aaepEpgAttachObj.Attributes["state"].(string); hasState {
×
NEW
505
                        if state != "formed" {
×
NEW
506
                                aaepEpgAttchDn := aaepEpgAttachObj.Attributes["dn"].(string)
×
NEW
507
                                cont.log.Debugf("Skipping NAD creation: %s is with state: %s", aaepEpgAttchDn, state)
×
NEW
508
                                continue
×
509
                        }
510
                }
NEW
511
                vlanID := 0
×
NEW
512
                if encap, hasEncap := aaepEpgAttachObj.Attributes["encap"].(string); hasEncap {
×
NEW
513
                        vlanID = cont.getVlanId(encap)
×
NEW
514
                }
×
515

NEW
516
                aaepEpgData := AaepEpgAttachData{
×
NEW
517
                        epgDn:     aaepEpgAttachObj.Attributes["tDn"].(string),
×
NEW
518
                        encapVlan: vlanID,
×
NEW
519
                }
×
NEW
520
                aaepEpgAttchDetails = append(aaepEpgAttchDetails, aaepEpgData)
×
521
        }
522

NEW
523
        return aaepEpgAttchDetails
×
524
}
525

NEW
526
func (cont *AciController) getEpgAnnotations(epgDn string) map[string]string {
×
NEW
527
        uri := fmt.Sprintf("/api/node/mo/%s.json?query-target=subtree&target-subtree-class=tagAnnotation", epgDn)
×
NEW
528
        resp, err := cont.apicConn.GetApicResponse(uri)
×
NEW
529
        if err != nil {
×
NEW
530
                cont.log.Errorf("Failed to get response from APIC: %v", err)
×
NEW
531
                return nil
×
NEW
532
        }
×
533

NEW
534
        annotationsMap := make(map[string]string)
×
NEW
535
        for _, respImdata := range resp.Imdata {
×
NEW
536
                annotationObj, ok := respImdata["tagAnnotation"]
×
NEW
537
                if !ok {
×
NEW
538
                        cont.log.Debugf("Skipping NAD creation: Empty tag annotation of EPG %s", epgDn)
×
NEW
539
                        continue
×
540
                }
541

NEW
542
                key := annotationObj.Attributes["key"].(string)
×
NEW
543
                annotationsMap[key] = annotationObj.Attributes["value"].(string)
×
544
        }
545

NEW
546
        return annotationsMap
×
547
}
548

NEW
549
func (cont *AciController) getSpecificEPGAnnotation(annotations map[string]string) (string, string) {
×
NEW
550
        namespaceNameAnnotationKey := cont.config.CnoIdentifier + "-namespace"
×
NEW
551
        namespaceName, exists := annotations[namespaceNameAnnotationKey]
×
NEW
552
        if !exists {
×
NEW
553
                cont.log.Errorf("Annotation with key '%s' not found", namespaceNameAnnotationKey)
×
NEW
554
        }
×
555

NEW
556
        nadNameAnnotationKey := cont.config.CnoIdentifier + "-nad"
×
NEW
557
        nadName, exists := annotations[nadNameAnnotationKey]
×
NEW
558
        if !exists {
×
NEW
559
                cont.log.Errorf("Annotation with key '%s' not found", nadNameAnnotationKey)
×
NEW
560
        }
×
NEW
561
        return namespaceName, nadName
×
562
}
563

NEW
564
func (cont *AciController) namespaceChecks(namespaceName string, epgDn string) bool {
×
NEW
565
        if namespaceName == "" {
×
NEW
566
                cont.log.Debugf("Defering NAD operation for EPG %s: Namespace name not provided in EPG annotation", epgDn)
×
NEW
567
                return false
×
NEW
568
        }
×
569

NEW
570
        kubeClient := cont.env.(*K8sEnvironment).kubeClient
×
NEW
571
        _, err := kubeClient.CoreV1().Namespaces().Get(context.TODO(), namespaceName, metav1.GetOptions{})
×
NEW
572
        namespaceExists := err == nil
×
NEW
573
        if !namespaceExists {
×
NEW
574
                cont.log.Debugf("Defering NAD operation for EPG %s: Namespace %s not exists", epgDn, namespaceName)
×
NEW
575
                return false
×
NEW
576
        }
×
577

NEW
578
        return true
×
579
}
580

NEW
581
func (cont *AciController) reconcileNadData(aaepName string) {
×
NEW
582
        aaepEpgDataList := cont.getAaepEpgAttObjDetails(aaepName)
×
NEW
583

×
NEW
584
        for _, aaepEpgData := range aaepEpgDataList {
×
NEW
585
                aaepMonitoringData := cont.collectNadData(&aaepEpgData)
×
NEW
586
                if aaepMonitoringData == nil {
×
NEW
587
                        cont.apicConn.AddImmediateSubscriptionDnLocked(aaepEpgData.epgDn,
×
NEW
588
                                []string{"tagAnnotation"}, cont.handleAnnotationAdded,
×
NEW
589
                                cont.handleAnnotationDeleted)
×
NEW
590
                        continue
×
591
                }
592

NEW
593
                cont.indexMutex.Lock()
×
NEW
594
                needCacheChange := cont.createNetworkAttachmentDefinition(aaepMonitoringData)
×
NEW
595
                if needCacheChange {
×
NEW
596
                        cont.sharedAaepMonitor[aaepName] = append(cont.sharedAaepMonitor[aaepName], aaepMonitoringData)
×
NEW
597
                }
×
NEW
598
                cont.indexMutex.Unlock()
×
NEW
599

×
NEW
600
                cont.apicConn.AddImmediateSubscriptionDnLocked(aaepEpgData.epgDn,
×
NEW
601
                        []string{"tagAnnotation"}, cont.handleAnnotationAdded,
×
NEW
602
                        cont.handleAnnotationDeleted)
×
603
        }
604

NEW
605
        cont.indexMutex.Lock()
×
NEW
606
        if _, ok := cont.sharedAaepMonitor[aaepName]; !ok {
×
NEW
607
                cont.sharedAaepMonitor[aaepName] = []*AaepMonitoringData{}
×
NEW
608
        }
×
NEW
609
        cont.indexMutex.Unlock()
×
610
}
611

NEW
612
func (cont *AciController) generateDefaultNadName(epgDn string) string {
×
NEW
613
        parts := strings.Split(epgDn, "/")
×
NEW
614

×
NEW
615
        tenant := parts[1][3:]
×
NEW
616
        appProfile := parts[2][3:]
×
NEW
617
        epgName := parts[3][4:]
×
NEW
618

×
NEW
619
        return fmt.Sprintf("%s-%s-%s", tenant, appProfile, epgName)
×
NEW
620
}
×
621

NEW
622
func (cont *AciController) isNADUpdateRequired(nadData *AaepMonitoringData, existingNAD *nadapi.NetworkAttachmentDefinition) bool {
×
NEW
623
        vlanID := nadData.aaepEpgData.encapVlan
×
NEW
624
        namespaceName := nadData.namespaceName
×
NEW
625
        customNadName := nadData.nadName
×
NEW
626
        defaultNadName := cont.generateDefaultNadName(nadData.aaepEpgData.epgDn)
×
NEW
627
        existingAnnotaions := existingNAD.ObjectMeta.Annotations
×
NEW
628
        if existingAnnotaions != nil {
×
NEW
629
                if existingNAD.ObjectMeta.Annotations["aci-sync-status"] == "out-of-sync" || existingNAD.ObjectMeta.Annotations["cno-name"] != customNadName {
×
NEW
630
                        return true
×
NEW
631
                }
×
NEW
632
        } else {
×
NEW
633
                // NAD exists, check if VLAN needs to be updated
×
NEW
634
                existingConfig := existingNAD.Spec.Config
×
NEW
635
                if existingConfig != "" {
×
NEW
636
                        var existingCNVConfig map[string]interface{}
×
NEW
637
                        if json.Unmarshal([]byte(existingConfig), &existingCNVConfig) == nil {
×
NEW
638
                                if existingVLAN, ok := existingCNVConfig["vlan"].(float64); ok {
×
NEW
639
                                        if int(existingVLAN) == vlanID {
×
NEW
640
                                                // VLAN hasn't changed, no update needed
×
NEW
641
                                                cont.log.Infof("NetworkAttachmentDefinition %s already exists with correct VLAN %d in namespace %s", defaultNadName, vlanID, namespaceName)
×
NEW
642
                                                return false
×
NEW
643
                                        }
×
NEW
644
                                } else if vlanID == 0 {
×
NEW
645
                                        // Both existing and new have no VLAN, no update needed
×
NEW
646
                                        cont.log.Infof("NetworkAttachmentDefinition %s already exists with no VLAN in namespace %s", defaultNadName, namespaceName)
×
NEW
647
                                        return false
×
NEW
648
                                }
×
649
                        }
650
                }
651
        }
652

NEW
653
        return true
×
654
}
655

NEW
656
func (cont *AciController) createNetworkAttachmentDefinition(nadData *AaepMonitoringData) bool {
×
NEW
657
        bridge := cont.config.BridgeName
×
NEW
658
        if bridge == "" {
×
NEW
659
                cont.log.Errorf("Linux bridge name must be specified when creating NetworkAttachmentDefinitions")
×
NEW
660
                return false
×
NEW
661
        }
×
662

NEW
663
        vlanID := nadData.aaepEpgData.encapVlan
×
NEW
664
        namespaceName := nadData.namespaceName
×
NEW
665
        customNadName := nadData.nadName
×
NEW
666
        defaultNadName := cont.generateDefaultNadName(nadData.aaepEpgData.epgDn)
×
NEW
667
        nadClient := cont.env.(*K8sEnvironment).nadClient
×
NEW
668
        mtu := 1500
×
NEW
669

×
NEW
670
        // Check if NAD already exists
×
NEW
671
        existingNAD, err := nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Get(context.TODO(), defaultNadName, metav1.GetOptions{})
×
NEW
672
        nadExists := err == nil
×
NEW
673

×
NEW
674
        if nadExists && !cont.isNADUpdateRequired(nadData, existingNAD) {
×
NEW
675
                return true
×
NEW
676
        }
×
677

NEW
678
        cnvBridgeConfig := map[string]any{
×
NEW
679
                "cniVersion":       "0.3.1",
×
NEW
680
                "name":             defaultNadName,
×
NEW
681
                "type":             "bridge",
×
NEW
682
                "isDefaultGateway": true,
×
NEW
683
                "bridge":           bridge,
×
NEW
684
                "mtu":              mtu,
×
NEW
685
        }
×
NEW
686

×
NEW
687
        if vlanID > 0 {
×
NEW
688
                cnvBridgeConfig["vlan"] = vlanID
×
NEW
689
        }
×
690

NEW
691
        configJSON, err := json.Marshal(cnvBridgeConfig)
×
NEW
692
        if err != nil {
×
NEW
693
                cont.log.Errorf("Failed to marshal CNV bridge config: %v", err)
×
NEW
694
                return false
×
NEW
695
        }
×
696

NEW
697
        nad := &nadapi.NetworkAttachmentDefinition{
×
NEW
698
                TypeMeta: metav1.TypeMeta{
×
NEW
699
                        APIVersion: "k8s.cni.cncf.io/v1",
×
NEW
700
                        Kind:       "NetworkAttachmentDefinition",
×
NEW
701
                },
×
NEW
702
                ObjectMeta: metav1.ObjectMeta{
×
NEW
703
                        Name:      defaultNadName,
×
NEW
704
                        Namespace: namespaceName,
×
NEW
705
                        Labels: map[string]string{
×
NEW
706
                                "managed-by": "cisco-network-operator",
×
NEW
707
                                "vlan":       strconv.Itoa(vlanID),
×
NEW
708
                        },
×
NEW
709
                        Annotations: map[string]string{
×
NEW
710
                                "managed-by": "cisco-network-operator",
×
NEW
711
                                "vlan":       strconv.Itoa(vlanID),
×
NEW
712
                                "cno-name":   customNadName,
×
NEW
713
                        },
×
NEW
714
                },
×
NEW
715
                Spec: nadapi.NetworkAttachmentDefinitionSpec{
×
NEW
716
                        Config: string(configJSON),
×
NEW
717
                },
×
NEW
718
        }
×
NEW
719

×
NEW
720
        if nadExists {
×
NEW
721
                nad.ObjectMeta.ResourceVersion = existingNAD.ObjectMeta.ResourceVersion
×
NEW
722

×
NEW
723
                _, err = nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Update(context.TODO(), nad, metav1.UpdateOptions{})
×
NEW
724

×
NEW
725
                if err != nil {
×
NEW
726
                        cont.log.Errorf("Failed to update NetworkAttachmentDefinition %s from namespace %s : %v", customNadName, namespaceName, err)
×
NEW
727
                        return false
×
NEW
728
                }
×
NEW
729
                cont.log.Infof("Updated NetworkAttachmentDefinition %s from namespace %s", defaultNadName, namespaceName)
×
NEW
730
        } else {
×
NEW
731
                _, err = nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Create(context.TODO(), nad, metav1.CreateOptions{})
×
NEW
732
                if err != nil {
×
NEW
733
                        cont.log.Errorf("Failed to create NetworkAttachmentDefinition %s in namespace %s : %v", customNadName, namespaceName, err)
×
NEW
734
                        return false
×
NEW
735
                }
×
NEW
736
                cont.log.Infof("Created NetworkAttachmentDefinition %s in namespace %s", defaultNadName, namespaceName)
×
737
        }
738

NEW
739
        return true
×
740
}
741

NEW
742
func (cont *AciController) deleteNetworkAttachmentDefinition(nadData *AaepMonitoringData, deleteReason string) {
×
NEW
743
        namespaceName := nadData.namespaceName
×
NEW
744
        epgDn := nadData.aaepEpgData.epgDn
×
NEW
745

×
NEW
746
        kubeClient := cont.env.(*K8sEnvironment).kubeClient
×
NEW
747
        _, err := kubeClient.CoreV1().Namespaces().Get(context.TODO(), namespaceName, metav1.GetOptions{})
×
NEW
748
        namespaceExists := err == nil
×
NEW
749
        if !namespaceExists {
×
NEW
750
                cont.log.Debugf("Defering NAD deletion for EPG %s: Namespace %s not exists", epgDn, namespaceName)
×
NEW
751
                return
×
NEW
752
        }
×
753

NEW
754
        nadName := cont.generateDefaultNadName(nadData.aaepEpgData.epgDn)
×
NEW
755
        nadClient := cont.env.(*K8sEnvironment).nadClient
×
NEW
756

×
NEW
757
        nadDetails, err := nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Get(context.TODO(), nadName, metav1.GetOptions{})
×
NEW
758
        nadExists := err == nil
×
NEW
759

×
NEW
760
        if nadExists {
×
NEW
761
                if !cont.isVmmLiteNAD(nadDetails) {
×
NEW
762
                        return
×
NEW
763
                }
×
764

NEW
765
                if cont.isNADinUse(namespaceName, nadName) {
×
NEW
766
                        nadDetails.ObjectMeta.Annotations["aci-sync-status"] = "out-of-sync"
×
NEW
767
                        _, err = nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Update(context.TODO(), nadDetails, metav1.UpdateOptions{})
×
NEW
768
                        if err != nil {
×
NEW
769
                                cont.log.Errorf("Failed to add out-of-sync annotation to the NAD %s from namespace %s : %v", nadName, namespaceName, err)
×
NEW
770
                                return
×
NEW
771
                        }
×
NEW
772
                        cont.submitEvent(nadDetails, deleteReason, cont.getNADDeleteMessage(deleteReason))
×
NEW
773
                        cont.log.Infof("Added annotation out-of-sync for NAD %s from namespace %s", nadName, namespaceName)
×
NEW
774
                        return
×
775
                }
776

NEW
777
                delete(nadDetails.ObjectMeta.Annotations, "managed-by")
×
NEW
778
                _, err = nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Update(context.TODO(), nadDetails, metav1.UpdateOptions{})
×
NEW
779
                if err != nil {
×
NEW
780
                        cont.log.Errorf("Failed to remove VMM lite annotation from NetworkAttachmentDefinition %s from namespace %s: %v", nadName, namespaceName, err)
×
NEW
781
                        return
×
NEW
782
                }
×
783

NEW
784
                nadClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespaceName).Delete(context.TODO(), nadName, metav1.DeleteOptions{})
×
NEW
785
                cont.log.Infof("Deleted NAD %s from %s namespace", nadName, namespaceName)
×
NEW
786
        } else {
×
NEW
787
                cont.log.Debugf("NAD %s not there to delete in namespace %s", nadName, namespaceName)
×
NEW
788
        }
×
789
}
790

NEW
791
func (cont *AciController) getVlanId(encap string) int {
×
NEW
792
        if after, ok := strings.CutPrefix(encap, "vlan-"); ok {
×
NEW
793
                vlanStr := after
×
NEW
794
                if vlanID, err := strconv.Atoi(vlanStr); err == nil && vlanID > 0 {
×
NEW
795
                        return vlanID
×
NEW
796
                }
×
NEW
797
        } else if after, ok := strings.CutPrefix(encap, "vlan"); ok {
×
NEW
798
                vlanStr := after
×
NEW
799
                if vlanID, err := strconv.Atoi(vlanStr); err == nil && vlanID > 0 {
×
NEW
800
                        return vlanID
×
NEW
801
                }
×
802
        }
803

NEW
804
        return 0
×
805
}
806

NEW
807
func (cont *AciController) getAaepDiff(crAaeps []string) (addedAaeps, removedAaeps []string) {
×
NEW
808
        crAaepMap := make(map[string]bool)
×
NEW
809
        for _, crAaep := range crAaeps {
×
NEW
810
                crAaepMap[crAaep] = true
×
NEW
811
        }
×
812

NEW
813
        cont.indexMutex.Lock()
×
NEW
814
        for _, crAaep := range crAaeps {
×
NEW
815
                if _, ok := cont.sharedAaepMonitor[crAaep]; !ok {
×
NEW
816
                        addedAaeps = append(addedAaeps, crAaep)
×
NEW
817
                }
×
818
        }
NEW
819
        cont.indexMutex.Unlock()
×
NEW
820

×
NEW
821
        cont.indexMutex.Lock()
×
NEW
822
        for cachedAaep := range cont.sharedAaepMonitor {
×
NEW
823
                if !crAaepMap[cachedAaep] {
×
NEW
824
                        removedAaeps = append(removedAaeps, cachedAaep)
×
NEW
825
                }
×
826
        }
NEW
827
        cont.indexMutex.Unlock()
×
NEW
828

×
NEW
829
        return
×
830
}
831

NEW
832
func (cont *AciController) getEncapFromAaepEpgAttachObj(aaepName, epgDn string) string {
×
NEW
833
        uri := fmt.Sprintf("/api/node/mo/uni/infra/attentp-%s/gen-default/rsfuncToEpg-[%s].json?query-target=self", aaepName, epgDn)
×
NEW
834
        resp, err := cont.apicConn.GetApicResponse(uri)
×
NEW
835
        if err != nil {
×
NEW
836
                cont.log.Errorf("Failed to get response from APIC: AAEP %s and EPG %s ERROR: %v", aaepName, epgDn, err)
×
NEW
837
                return ""
×
NEW
838
        }
×
839

NEW
840
        for _, obj := range resp.Imdata {
×
NEW
841
                lresp, ok := obj["infraRsFuncToEpg"]
×
NEW
842
                if !ok {
×
NEW
843
                        cont.log.Errorf("InfraRsFuncToEpg object not found in response for %s", uri)
×
NEW
844
                        break
×
845
                }
NEW
846
                if val, ok := lresp.Attributes["encap"]; ok {
×
NEW
847
                        encap := val.(string)
×
NEW
848
                        return encap
×
NEW
849
                } else {
×
NEW
850
                        cont.log.Errorf("Encap missing for infraRsFuncToEpg object for %s: %v", uri, err)
×
NEW
851
                        break
×
852
                }
853
        }
854

NEW
855
        return ""
×
856
}
857

NEW
858
func (cont *AciController) isVmmLiteNAD(nadDetails *nadapi.NetworkAttachmentDefinition) bool {
×
NEW
859
        return nadDetails.ObjectMeta.Annotations["managed-by"] == "cisco-network-operator"
×
NEW
860
}
×
861

NEW
862
func (cont *AciController) isNADinUse(namespaceName string, nadName string) bool {
×
NEW
863
        kubeClient := cont.env.(*K8sEnvironment).kubeClient
×
NEW
864
        pods, err := kubeClient.CoreV1().Pods(namespaceName).List(context.TODO(), metav1.ListOptions{})
×
NEW
865
        if err == nil {
×
NEW
866
                for _, pod := range pods.Items {
×
NEW
867
                        networksAnn, ok := pod.Annotations["k8s.v1.cni.cncf.io/networks"]
×
NEW
868
                        if ok && (networksAnn == nadName) {
×
NEW
869
                                cont.log.Infof("NAD %s is still used by Pod %s/%s", nadName, namespaceName, pod.Name)
×
NEW
870
                                return true
×
NEW
871
                        }
×
872
                }
873
        }
NEW
874
        return false
×
875
}
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