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

noironetworks / aci-containers / 12060

10 Jul 2026 01:09PM UTC coverage: 63.998%. Remained the same
12060

Pull #1741

travis-pro

burhan20
Fix(apicapi): prevent invalid delete of APIC owned helper MOs

During SNAT create/delete churn, reconcile treated APIC-only helper/relation MOs as deletable extras and attempted to delete APIC-managed relation objects, causing 400 Invalid access.

Removed dependency on metadata-based deletable hints and switched delete control to controller ownership.
Unowned helper/relation MOs are now skipped, preventing invalid-access delete attempts.
Pull Request #1741: Fix(apicapi): prevent invalid delete of APIC owned helper MOs

16 of 17 new or added lines in 1 file covered. (94.12%)

685 existing lines in 9 files now uncovered.

13675 of 21368 relevant lines covered (64.0%)

0.73 hits per line

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

69.51
/pkg/controller/controller.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 controller
16

17
import (
18
        "encoding/json"
19
        "fmt"
20
        "net"
21
        "os"
22
        "reflect"
23
        "strconv"
24
        "strings"
25
        "sync"
26
        "time"
27

28
        "github.com/sirupsen/logrus"
29
        "github.com/yl2chen/cidranger"
30
        "golang.org/x/time/rate"
31

32
        v1 "k8s.io/api/core/v1"
33
        discovery "k8s.io/api/discovery/v1"
34
        v1net "k8s.io/api/networking/v1"
35
        "k8s.io/apimachinery/pkg/labels"
36
        "k8s.io/apimachinery/pkg/util/wait"
37
        "k8s.io/client-go/kubernetes"
38
        "k8s.io/client-go/tools/cache"
39
        "k8s.io/client-go/util/workqueue"
40

41
        "github.com/noironetworks/aci-containers/pkg/apicapi"
42
        fabattv1 "github.com/noironetworks/aci-containers/pkg/fabricattachment/apis/aci.fabricattachment/v1"
43
        fabattclset "github.com/noironetworks/aci-containers/pkg/fabricattachment/clientset/versioned"
44
        hppv1 "github.com/noironetworks/aci-containers/pkg/hpp/apis/aci.hpp/v1"
45
        "github.com/noironetworks/aci-containers/pkg/index"
46
        "github.com/noironetworks/aci-containers/pkg/ipam"
47
        istiov1 "github.com/noironetworks/aci-containers/pkg/istiocrd/apis/aci.istio/v1"
48
        "github.com/noironetworks/aci-containers/pkg/metadata"
49
        nodeinfo "github.com/noironetworks/aci-containers/pkg/nodeinfo/apis/aci.snat/v1"
50
        rdConfig "github.com/noironetworks/aci-containers/pkg/rdconfig/apis/aci.snat/v1"
51
        snatglobalinfo "github.com/noironetworks/aci-containers/pkg/snatglobalinfo/apis/aci.snat/v1"
52
        "github.com/noironetworks/aci-containers/pkg/util"
53
)
54

55
type podUpdateFunc func(*v1.Pod) (*v1.Pod, error)
56
type nodeUpdateFunc func(*v1.Node) (*v1.Node, error)
57
type serviceUpdateFunc func(*v1.Service) (*v1.Service, error)
58
type listNetworkPoliciesFunc func(string) (*v1net.NetworkPolicyList, error)
59
type listNamespacesFunc func() (*v1.NamespaceList, error)
60

61
type AciController struct {
62
        log    *logrus.Logger
63
        config *ControllerConfig
64
        env    Environment
65

66
        defaultEg string
67
        defaultSg string
68

69
        unitTestMode bool
70

71
        podQueue               workqueue.RateLimitingInterface
72
        netPolQueue            workqueue.RateLimitingInterface
73
        qosQueue               workqueue.RateLimitingInterface
74
        serviceQueue           workqueue.RateLimitingInterface
75
        snatQueue              workqueue.RateLimitingInterface
76
        netflowQueue           workqueue.RateLimitingInterface
77
        erspanQueue            workqueue.RateLimitingInterface
78
        snatNodeInfoQueue      workqueue.RateLimitingInterface
79
        rdConfigQueue          workqueue.RateLimitingInterface
80
        istioQueue             workqueue.RateLimitingInterface
81
        nodeFabNetAttQueue     workqueue.RateLimitingInterface
82
        netFabConfigQueue      workqueue.RateLimitingInterface
83
        nadVlanMapQueue        workqueue.RateLimitingInterface
84
        fabricVlanPoolQueue    workqueue.RateLimitingInterface
85
        netFabL3ConfigQueue    workqueue.RateLimitingInterface
86
        remIpContQueue         workqueue.RateLimitingInterface
87
        epgDnCacheUpdateQueue  workqueue.RateLimitingInterface
88
        aaepMonitorConfigQueue workqueue.RateLimitingInterface
89

90
        namespaceIndexer                     cache.Indexer
91
        namespaceInformer                    cache.Controller
92
        podIndexer                           cache.Indexer
93
        podInformer                          cache.Controller
94
        serviceIndexer                       cache.Indexer
95
        serviceInformer                      cache.Controller
96
        replicaSetIndexer                    cache.Indexer
97
        replicaSetInformer                   cache.Controller
98
        deploymentIndexer                    cache.Indexer
99
        deploymentInformer                   cache.Controller
100
        nodeIndexer                          cache.Indexer
101
        nodeInformer                         cache.Controller
102
        networkPolicyIndexer                 cache.Indexer
103
        networkPolicyInformer                cache.Controller
104
        snatIndexer                          cache.Indexer
105
        snatInformer                         cache.Controller
106
        snatNodeInfoIndexer                  cache.Indexer
107
        snatNodeInformer                     cache.Controller
108
        snatLocalInfoInformer                cache.Controller
109
        crdInformer                          cache.Controller
110
        rdConfigInformer                     cache.Controller
111
        rdConfigIndexer                      cache.Indexer
112
        qosIndexer                           cache.Indexer
113
        qosInformer                          cache.Controller
114
        netflowIndexer                       cache.Indexer
115
        netflowInformer                      cache.Controller
116
        erspanIndexer                        cache.Indexer
117
        erspanInformer                       cache.Controller
118
        nodePodIfIndexer                     cache.Indexer
119
        nodePodIfInformer                    cache.Controller
120
        istioIndexer                         cache.Indexer
121
        istioInformer                        cache.Controller
122
        endpointSliceIndexer                 cache.Indexer
123
        endpointSliceInformer                cache.Controller
124
        snatCfgInformer                      cache.Controller
125
        updatePod                            podUpdateFunc
126
        updateNode                           nodeUpdateFunc
127
        updateServiceStatus                  serviceUpdateFunc
128
        listNetworkPolicies                  listNetworkPoliciesFunc
129
        listNamespaces                       listNamespacesFunc
130
        nodeFabNetAttInformer                cache.SharedIndexInformer
131
        netFabConfigInformer                 cache.SharedIndexInformer
132
        nadVlanMapInformer                   cache.SharedIndexInformer
133
        fabricVlanPoolInformer               cache.SharedIndexInformer
134
        networkFabricL3ConfigurationInformer cache.SharedIndexInformer
135
        fabNetAttClient                      *fabattclset.Clientset
136
        proactiveConfInformer                cache.SharedIndexInformer
137
        aaepMonitorInformer                  cache.SharedIndexInformer
138
        poster                               *EventPoster
139

140
        indexMutex sync.Mutex
141
        hppMutex   sync.Mutex
142

143
        configuredPodNetworkIps *netIps
144
        podNetworkIps           *netIps
145
        serviceIps              *ipam.IpCache
146
        staticServiceIps        *netIps
147
        nodeServiceIps          *netIps
148

149
        // index of pods matched by deployments
150
        depPods *index.PodSelectorIndex
151
        // index of pods matched by network policies
152
        netPolPods *index.PodSelectorIndex
153
        // index of pods matched by network policy ingress rules
154
        netPolIngressPods *index.PodSelectorIndex
155
        // index of pods matched by network policy egress rules
156
        netPolEgressPods *index.PodSelectorIndex
157
        // index of IP addresses contained in endpoints objects
158
        endpointsIpIndex cidranger.Ranger
159
        // index of service target ports
160
        targetPortIndex map[string]*portIndexEntry
161
        // index of services with named target ports
162
        namedPortServiceIndex map[string]*namedPortServiceIndexEntry
163
        // index of ip blocks referenced by network policy egress rules
164
        netPolSubnetIndex cidranger.Ranger
165
        // index of pods matched by erspan policies
166
        erspanPolPods *index.PodSelectorIndex
167

168
        apicConn *apicapi.ApicConnection
169

170
        nodeServiceMetaCache map[string]*nodeServiceMeta
171
        nodeACIPod           map[string]aciPodAnnot
172
        nodeACIPodAnnot      map[string]aciPodAnnot
173
        nodeOpflexDevice     map[string]apicapi.ApicSlice
174
        nodePodNetCache      map[string]*nodePodNetMeta
175
        serviceMetaCache     map[string]*serviceMeta
176
        snatPolicyCache      map[string]*ContSnatPolicy
177
        delayedEpSlices      []*DelayedEpSlice
178
        snatServices         map[string]bool
179
        snatNodeInfoCache    map[string]*nodeinfo.NodeInfo
180
        rdConfigCache        map[string]*rdConfig.RdConfig
181
        rdConfigSubnetCache  map[string]*rdConfig.RdConfigSpec
182
        istioCache           map[string]*istiov1.AciIstioOperator
183
        podIftoEp            map[string]*EndPointData
184
        // Node Name and Policy Name
185
        snatGlobalInfoCache map[string]map[string]*snatglobalinfo.GlobalInfo
186
        nodeSyncEnabled     bool
187
        serviceSyncEnabled  bool
188
        snatSyncEnabled     bool
189
        syncQueue           workqueue.RateLimitingInterface
190
        syncProcessors      map[string]func() bool
191
        serviceEndPoints    ServiceEndPointType
192
        crdHandlers         map[string]func(*AciController, <-chan struct{})
193
        stopCh              <-chan struct{}
194
        //index of containerportname to ctrPortNameEntry
195
        ctrPortNameCache map[string]*ctrPortNameEntry
196
        // named networkPolicies
197
        nmPortNp map[string]bool
198
        //maps network policy hash to hpp
199
        hppRef map[string]hppReference
200
        //map for ns to remoteIpConts
201
        nsRemoteIpCont map[string]remoteIpConts
202
        // cache to look for Epg DNs which are bound to Vmm domain
203
        cachedEpgDns             []string
204
        vmmClusterFaultSupported bool
205
        additionalNetworkCache   map[string]*AdditionalNetworkMeta
206
        //Used in Shared mode
207
        sharedEncapCache       map[int]*sharedEncapData
208
        sharedEncapAepCache    map[string]map[int]bool
209
        sharedEncapSviCache    map[int]*NfL3Data
210
        sharedEncapVrfCache    map[string]*NfVrfData
211
        sharedEncapTenantCache map[string]*NfTenantData
212
        nfl3configGenerationId int64
213
        // vlan to propertiesList
214
        sharedEncapNfcCache         map[int]*NfcData
215
        sharedEncapNfcVlanMap       map[int]*NfcData
216
        sharedEncapNfcLabelMap      map[string]*NfcData
217
        sharedEncapNfcAppProfileMap map[string]bool
218
        // nadVlanMap encapLabel to vlan
219
        sharedEncapLabelMap      map[string][]int
220
        lldpIfCache              map[string]*NfLLDPIfData
221
        globalVlanConfig         globalVlanConfig
222
        fabricVlanPoolMap        map[string]map[string]string
223
        openStackFabricPathDnMap map[string]openstackOpflexOdevInfo
224
        hostFabricPathDnMap      map[string]hostFabricInfo
225
        openStackSystemId        string
226
        sharedAaepMonitor        map[string]map[string]*AaepEpgAttachData
227
}
228

229
type hostFabricInfo struct {
230
        fabricPathDn string
231
        host         string
232
        vpcIfDn      map[string]struct{}
233
}
234

235
type NfLLDPIfData struct {
236
        LLDPIf string
237
        // As of now, manage at the NAD level
238
        // more granular introduces intf tracking complexities
239
        // for not sufficient benefits
240
        Refs map[string]bool
241
}
242

243
type NfL3OutData struct {
244
        // +kubebuilder:validation:Enum:"import"
245
        RtCtrl     string
246
        PodId      int
247
        RtrNodeMap map[int]*fabattv1.FabricL3OutRtrNode
248
        ExtEpgMap  map[string]*fabattv1.PolicyPrefixGroup
249
        SviMap     map[int]bool
250
}
251

252
type NfTenantData struct {
253
        CommonTenant     bool
254
        L3OutConfig      map[string]*NfL3OutData
255
        BGPPeerPfxConfig map[string]*fabattv1.BGPPeerPrefixPolicy
256
}
257

258
type NfVrfData struct {
259
        TenantConfig map[string]*NfTenantData
260
}
261

262
type NfL3Networks struct {
263
        fabattv1.PrimaryNetwork
264
        Subnets map[string]*fabattv1.FabricL3Subnet
265
}
266

267
type NfL3Data struct {
268
        Tenant      string
269
        Vrf         fabattv1.VRF
270
        PodId       int
271
        ConnectedNw *NfL3Networks
272
        NetAddr     map[string]*RoutedNetworkData
273
        Nodes       map[int]fabattv1.FabricL3OutNode
274
}
275

276
// maps pod name to remoteIpCont
277
type remoteIpConts map[string]remoteIpCont
278

279
// remoteIpCont maps ip to pod labels
280
type remoteIpCont map[string]map[string]string
281

282
type NfcData struct {
283
        Aeps map[string]bool
284
        Epg  fabattv1.Epg
285
}
286

287
type sharedEncapData struct {
288
        //node to NAD to pods
289
        Pods   map[string]map[string][]string
290
        NetRef map[string]*AdditionalNetworkMeta
291
        Aeps   map[string]bool
292
}
293

294
type globalVlanConfig struct {
295
        SharedPhysDom apicapi.ApicObject
296
        SharedL3Dom   apicapi.ApicObject
297
}
298

299
type hppReference struct {
300
        RefCount       uint                       `json:"ref-count,omitempty"`
301
        Npkeys         []string                   `json:"npkeys,omitempty"`
302
        HppObj         apicapi.ApicSlice          `json:"hpp-obj,omitempty"`
303
        HppCr          hppv1.HostprotPol          `json:"hpp-cr,omitempty"`
304
        NpIngressRules map[string]map[string]bool `json:"-"` // npKey → set of ingress rule names
305
}
306

307
type DelayedEpSlice struct {
308
        ServiceKey  string
309
        OldEpSlice  *discovery.EndpointSlice
310
        NewEpSlice  *discovery.EndpointSlice
311
        DelayedTime time.Time
312
}
313

314
type aciPodAnnot struct {
315
        aciPod         string
316
        disconnectTime time.Time
317
        lastErrorTime  time.Time
318
}
319

320
type nodeServiceMeta struct {
321
        serviceEp metadata.ServiceEndpoint
322
}
323

324
type nodePodNetMeta struct {
325
        nodePods            map[string]bool
326
        podNetIps           metadata.NetIps
327
        podNetIpsAnnotation string
328
}
329

330
type openstackOpflexOdevInfo struct {
331
        opflexODevDn map[string]struct{}
332
        fabricPathDn string
333
}
334

335
type serviceMeta struct {
336
        requestedIps     []net.IP
337
        ingressIps       []net.IP
338
        staticIngressIps []net.IP
339
}
340

341
type ipIndexEntry struct {
342
        ipNet net.IPNet
343
        keys  map[string]bool
344
}
345

346
type targetPort struct {
347
        proto v1.Protocol
348
        // portServiceMap maps a resolved numeric port number to the set of
349
        // service keys whose endpoints resolve the named port to that number.
350
        // eg.: {80: {"default/svc1": true, "default/svc2": true}, 8080: {"default/svc3": true}}
351
        portServiceMap map[int]map[string]bool
352
}
353

354
// portIndexEntry is an entry in the targetPortIndex, keyed by a port
355
// specification string (e.g. "tcp-name-http" or "tcp-num-80"). It tracks
356
// which services have resolved endpoints to specific numeric ports for
357
// this port spec, and which network policies reference it.
358
type portIndexEntry struct {
359
        portMapping       targetPort
360
        networkPolicyKeys map[string]bool
361
}
362

363
// hasServiceKeys returns true if any per-port inner map contains
364
// at least one service key.
365
func (e *portIndexEntry) hasServiceKeys() bool {
1✔
366
        for _, svcKeys := range e.portMapping.portServiceMap {
2✔
367
                if len(svcKeys) > 0 {
2✔
368
                        return true
1✔
369
                }
1✔
370
        }
371
        return false
1✔
372
}
373

374
// removeServiceKey removes a service key from all per-port inner
375
// maps. If an inner map becomes empty it is set to nil so the port
376
// number itself is preserved (it may still be needed by NP tracking).
377
func (e *portIndexEntry) removeServiceKey(key string) {
1✔
378
        for p, svcKeys := range e.portMapping.portServiceMap {
2✔
379
                if svcKeys != nil {
2✔
380
                        delete(svcKeys, key)
1✔
381
                        if len(svcKeys) == 0 {
2✔
382
                                e.portMapping.portServiceMap[p] = nil
1✔
383
                        }
1✔
384
                }
385
        }
386
}
387

388
type namedPortServiceIndexPort struct {
389
        targetPortName string
390
        resolvedPorts  map[int]bool
391
}
392

393
type namedPortServiceIndexEntry map[string]*namedPortServiceIndexPort
394

395
type portRangeSnat struct {
396
        start int
397
        end   int
398
}
399

400
// EndPointData holds PodIF data in controller.
401
type EndPointData struct {
402
        MacAddr    string
403
        EPG        string
404
        Namespace  string
405
        AppProfile string
406
}
407

408
type ctrPortNameEntry struct {
409
        // Proto+port->pods
410
        ctrNmpToPods map[string]map[string]bool
411
}
412

413
type LinkData struct {
414
        Link []string
415
        Pods []string
416
}
417

418
type RoutedNodeData struct {
419
        addr string
420
        idx  int
421
}
422

423
type RoutedNetworkData struct {
424
        subnet       string
425
        netAddress   string
426
        maskLen      int
427
        numAllocated int
428
        maxAddresses int
429
        baseAddress  net.IP
430
        nodeMap      map[string]RoutedNodeData
431
        availableMap map[int]bool
432
}
433

434
type AdditionalNetworkMeta struct {
435
        NetworkName string
436
        EncapVlan   string
437
        //node+localiface->fabricLinks
438
        FabricLink map[string]map[string]LinkData
439
        NodeCache  map[string]*fabattv1.NodeFabricNetworkAttachment
440
        Mode       util.EncapMode
441
}
442

443
type ServiceEndPointType interface {
444
        InitClientInformer(kubeClient *kubernetes.Clientset)
445
        Run(stopCh <-chan struct{})
446
        Wait(stopCh <-chan struct{})
447
        UpdateServicesForNode(nodename string)
448
        GetnodesMetadata(key string, service *v1.Service, nodeMap map[string]*metadata.ServiceEndpoint)
449
        SetServiceApicObject(aobj apicapi.ApicObject, service *v1.Service) bool
450
        SetNpServiceAugmentForService(servicekey string, service *v1.Service, prs *portRemoteSubnet,
451
                portAugments map[string]*portServiceAugment, subnetIndex cidranger.Ranger, logger *logrus.Entry)
452
}
453

454
type serviceEndpointSlice struct {
455
        cont *AciController
456
}
457

458
type AaepEpgAttachData struct {
459
        encapVlan     int
460
        nadName       string
461
        namespaceName string
462
        nadCreated    bool
463
}
464

465
type EpgVlanMap struct {
466
        epgDn     string
467
        encapVlan int
468
}
469

UNCOV
470
func (seps *serviceEndpointSlice) InitClientInformer(kubeClient *kubernetes.Clientset) {
×
UNCOV
471
        seps.cont.initEndpointSliceInformerFromClient(kubeClient)
×
UNCOV
472
}
×
473

474
func (seps *serviceEndpointSlice) Run(stopCh <-chan struct{}) {
1✔
475
        go seps.cont.endpointSliceInformer.Run(stopCh)
1✔
476
}
1✔
477

478
func (seps *serviceEndpointSlice) Wait(stopCh <-chan struct{}) {
1✔
479
        seps.cont.log.Debug("Waiting for EndPointSlicecache sync")
1✔
480
        cache.WaitForCacheSync(stopCh,
1✔
481
                seps.cont.endpointSliceInformer.HasSynced,
1✔
482
                seps.cont.serviceInformer.HasSynced)
1✔
483
}
1✔
484

485
func (e *ipIndexEntry) Network() net.IPNet {
1✔
486
        return e.ipNet
1✔
487
}
1✔
488

489
func newNodePodNetMeta() *nodePodNetMeta {
1✔
490
        return &nodePodNetMeta{
1✔
491
                nodePods: make(map[string]bool),
1✔
492
        }
1✔
493
}
1✔
494

495
func createQueue(name string) workqueue.RateLimitingInterface {
1✔
496
        return workqueue.NewNamedRateLimitingQueue(
1✔
497
                workqueue.NewMaxOfRateLimiter(
1✔
498
                        workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond,
1✔
499
                                10*time.Second),
1✔
500
                        &workqueue.BucketRateLimiter{
1✔
501
                                Limiter: rate.NewLimiter(rate.Limit(10), int(100)),
1✔
502
                        },
1✔
503
                ),
1✔
504
                "delta")
1✔
505
}
1✔
506

507
func NewController(config *ControllerConfig, env Environment, log *logrus.Logger, unittestmode bool) *AciController {
1✔
508
        cont := &AciController{
1✔
509
                log:          log,
1✔
510
                config:       config,
1✔
511
                env:          env,
1✔
512
                defaultEg:    "",
1✔
513
                defaultSg:    "",
1✔
514
                unitTestMode: unittestmode,
1✔
515

1✔
516
                podQueue:               createQueue("pod"),
1✔
517
                netPolQueue:            createQueue("networkPolicy"),
1✔
518
                qosQueue:               createQueue("qos"),
1✔
519
                netflowQueue:           createQueue("netflow"),
1✔
520
                erspanQueue:            createQueue("erspan"),
1✔
521
                serviceQueue:           createQueue("service"),
1✔
522
                snatQueue:              createQueue("snat"),
1✔
523
                snatNodeInfoQueue:      createQueue("snatnodeinfo"),
1✔
524
                rdConfigQueue:          createQueue("rdconfig"),
1✔
525
                istioQueue:             createQueue("istio"),
1✔
526
                nodeFabNetAttQueue:     createQueue("nodefabricnetworkattachment"),
1✔
527
                netFabConfigQueue:      createQueue("networkfabricconfiguration"),
1✔
528
                nadVlanMapQueue:        createQueue("nadvlanmap"),
1✔
529
                fabricVlanPoolQueue:    createQueue("fabricvlanpool"),
1✔
530
                netFabL3ConfigQueue:    createQueue("networkfabricl3configuration"),
1✔
531
                remIpContQueue:         createQueue("remoteIpContainer"),
1✔
532
                epgDnCacheUpdateQueue:  createQueue("epgDnCache"),
1✔
533
                aaepMonitorConfigQueue: createQueue("aaepepgmap"),
1✔
534
                syncQueue: workqueue.NewNamedRateLimitingQueue(
1✔
535
                        &workqueue.BucketRateLimiter{
1✔
536
                                Limiter: rate.NewLimiter(rate.Limit(10), int(100)),
1✔
537
                        }, "sync"),
1✔
538

1✔
539
                configuredPodNetworkIps: newNetIps(),
1✔
540
                podNetworkIps:           newNetIps(),
1✔
541
                serviceIps:              ipam.NewIpCache(),
1✔
542
                staticServiceIps:        newNetIps(),
1✔
543
                nodeServiceIps:          newNetIps(),
1✔
544

1✔
545
                nodeACIPod:       make(map[string]aciPodAnnot),
1✔
546
                nodeACIPodAnnot:  make(map[string]aciPodAnnot),
1✔
547
                nodeOpflexDevice: make(map[string]apicapi.ApicSlice),
1✔
548

1✔
549
                nodeServiceMetaCache:        make(map[string]*nodeServiceMeta),
1✔
550
                nodePodNetCache:             make(map[string]*nodePodNetMeta),
1✔
551
                serviceMetaCache:            make(map[string]*serviceMeta),
1✔
552
                snatPolicyCache:             make(map[string]*ContSnatPolicy),
1✔
553
                snatServices:                make(map[string]bool),
1✔
554
                snatNodeInfoCache:           make(map[string]*nodeinfo.NodeInfo),
1✔
555
                rdConfigCache:               make(map[string]*rdConfig.RdConfig),
1✔
556
                rdConfigSubnetCache:         make(map[string]*rdConfig.RdConfigSpec),
1✔
557
                podIftoEp:                   make(map[string]*EndPointData),
1✔
558
                snatGlobalInfoCache:         make(map[string]map[string]*snatglobalinfo.GlobalInfo),
1✔
559
                istioCache:                  make(map[string]*istiov1.AciIstioOperator),
1✔
560
                crdHandlers:                 make(map[string]func(*AciController, <-chan struct{})),
1✔
561
                ctrPortNameCache:            make(map[string]*ctrPortNameEntry),
1✔
562
                nmPortNp:                    make(map[string]bool),
1✔
563
                hppRef:                      make(map[string]hppReference),
1✔
564
                additionalNetworkCache:      make(map[string]*AdditionalNetworkMeta),
1✔
565
                sharedEncapCache:            make(map[int]*sharedEncapData),
1✔
566
                sharedEncapAepCache:         make(map[string]map[int]bool),
1✔
567
                sharedEncapSviCache:         make(map[int]*NfL3Data),
1✔
568
                sharedEncapVrfCache:         make(map[string]*NfVrfData),
1✔
569
                sharedEncapTenantCache:      make(map[string]*NfTenantData),
1✔
570
                sharedEncapNfcCache:         make(map[int]*NfcData),
1✔
571
                sharedEncapNfcVlanMap:       make(map[int]*NfcData),
1✔
572
                sharedEncapNfcLabelMap:      make(map[string]*NfcData),
1✔
573
                sharedEncapNfcAppProfileMap: make(map[string]bool),
1✔
574
                sharedEncapLabelMap:         make(map[string][]int),
1✔
575
                lldpIfCache:                 make(map[string]*NfLLDPIfData),
1✔
576
                fabricVlanPoolMap:           make(map[string]map[string]string),
1✔
577
                openStackFabricPathDnMap:    make(map[string]openstackOpflexOdevInfo),
1✔
578
                hostFabricPathDnMap:         make(map[string]hostFabricInfo),
1✔
579
                nsRemoteIpCont:              make(map[string]remoteIpConts),
1✔
580
                sharedAaepMonitor:           make(map[string]map[string]*AaepEpgAttachData),
1✔
581
        }
1✔
582
        cont.syncProcessors = map[string]func() bool{
1✔
583
                "snatGlobalInfo": cont.syncSnatGlobalInfo,
1✔
584
                "rdConfig":       cont.syncRdConfig,
1✔
585
                /* Commenting code to remove dependency from istio.io/istio package.
1✔
586
                   Vulnerabilties were detected by quay.io security scan of aci-containers-controller
1✔
587
                   and aci-containers-operator images for istio.io/istio package
1✔
588

1✔
589
                "istioCR":        cont.createIstioCR,
1✔
590
                */
1✔
591
        }
1✔
592
        return cont
1✔
593
}
1✔
594

UNCOV
595
func (cont *AciController) Init() {
×
UNCOV
596
        if cont.config.ChainedMode {
×
UNCOV
597
                cont.log.Info("In chained mode")
×
UNCOV
598
        }
×
UNCOV
599
        if cont.config.VmmLite {
×
UNCOV
600
                cont.log.Info("In VMM lite mode")
×
UNCOV
601
        }
×
602

UNCOV
603
        egdata, err := json.Marshal(cont.config.DefaultEg)
×
UNCOV
604
        if err != nil {
×
UNCOV
605
                cont.log.Error("Could not serialize default endpoint group")
×
UNCOV
606
                panic(err.Error())
×
607
        }
UNCOV
608
        cont.defaultEg = string(egdata)
×
UNCOV
609

×
UNCOV
610
        sgdata, err := json.Marshal(cont.config.DefaultSg)
×
UNCOV
611
        if err != nil {
×
UNCOV
612
                cont.log.Error("Could not serialize default security groups")
×
UNCOV
613
                panic(err.Error())
×
614
        }
UNCOV
615
        cont.defaultSg = string(sgdata)
×
616

×
617
        cont.log.Debug("Initializing IPAM")
×
618
        cont.initIpam()
×
619

×
620
        cont.serviceEndPoints = &serviceEndpointSlice{}
×
621
        cont.serviceEndPoints.(*serviceEndpointSlice).cont = cont
×
622
        cont.log.Info("Initializing ServiceEndpointSlices")
×
UNCOV
623

×
624
        err = cont.env.Init(cont)
×
625
        if err != nil {
×
626
                panic(err.Error())
×
627
        }
628
}
629

630
func (cont *AciController) processQueue(queue workqueue.RateLimitingInterface,
631
        store cache.Store, handler func(interface{}) bool,
632
        deleteHandler func(string) bool,
633
        postDelHandler func() bool, stopCh <-chan struct{}) {
1✔
634
        go wait.Until(func() {
2✔
635
                for {
2✔
636
                        key, quit := queue.Get()
1✔
637
                        if quit {
2✔
638
                                break
1✔
639
                        }
640

641
                        var requeue bool
1✔
642
                        switch key := key.(type) {
1✔
643
                        case chan struct{}:
×
644
                                close(key)
×
645
                        case string:
1✔
646
                                if strings.HasPrefix(key, "DELETED_") {
2✔
647
                                        delKey := strings.Trim(key, "DELETED_")
1✔
648
                                        requeue = deleteHandler(delKey)
1✔
649
                                } else {
2✔
650
                                        obj, exists, err := store.GetByKey(key)
1✔
651
                                        if err != nil {
1✔
UNCOV
652
                                                cont.log.Debugf("Error fetching object with key %s from store: %v", key, err)
×
UNCOV
653
                                        }
×
654
                                        //Handle Add/Update/Delete
655
                                        if exists && handler != nil {
2✔
656
                                                requeue = handler(obj)
1✔
657
                                        }
1✔
658
                                        //Handle Post Delete
659
                                        if !exists && postDelHandler != nil {
1✔
UNCOV
660
                                                requeue = postDelHandler()
×
UNCOV
661
                                        }
×
662
                                }
663
                        }
664
                        if requeue {
2✔
665
                                queue.AddRateLimited(key)
1✔
666
                        } else {
2✔
667
                                queue.Forget(key)
1✔
668
                        }
1✔
669
                        queue.Done(key)
1✔
670
                }
671
        }, time.Second, stopCh)
672
        <-stopCh
1✔
673
        queue.ShutDown()
1✔
674
}
675

676
func (cont *AciController) processRemIpContQueue(queue workqueue.RateLimitingInterface,
677
        handler func(interface{}) bool,
678
        postDelHandler func() bool, stopCh <-chan struct{}) {
1✔
679
        go wait.Until(func() {
2✔
680
                for {
2✔
681
                        key, quit := queue.Get()
1✔
682
                        if quit {
2✔
683
                                break
1✔
684
                        }
685

686
                        var requeue bool
1✔
687
                        switch key := key.(type) {
1✔
UNCOV
688
                        case chan struct{}:
×
UNCOV
689
                                close(key)
×
690
                        case string:
1✔
691
                                if handler != nil {
2✔
692
                                        requeue = handler(key)
1✔
693
                                }
1✔
694
                                if postDelHandler != nil {
2✔
695
                                        requeue = postDelHandler()
1✔
696
                                }
1✔
697
                        }
698
                        if requeue {
1✔
UNCOV
699
                                queue.AddRateLimited(key)
×
700
                        } else {
1✔
701
                                queue.Forget(key)
1✔
702
                        }
1✔
703
                        queue.Done(key)
1✔
704

705
                }
706
        }, time.Second, stopCh)
707
        <-stopCh
1✔
708
        queue.ShutDown()
1✔
709
}
710

711
func (cont *AciController) processEpgDnCacheUpdateQueue(queue workqueue.RateLimitingInterface,
712
        handler func(interface{}) bool,
713
        postDelHandler func() bool, stopCh <-chan struct{}) {
1✔
714
        go wait.Until(func() {
2✔
715
                for {
2✔
716
                        key, quit := queue.Get()
1✔
717
                        if quit {
2✔
718
                                break
1✔
719
                        }
720

721
                        var requeue bool
1✔
722
                        switch key := key.(type) {
1✔
UNCOV
723
                        case chan struct{}:
×
UNCOV
724
                                close(key)
×
725
                        case bool:
1✔
726
                                if handler != nil {
2✔
727
                                        requeue = handler(key)
1✔
728
                                }
1✔
729
                                if postDelHandler != nil {
1✔
UNCOV
730
                                        requeue = postDelHandler()
×
UNCOV
731
                                }
×
732
                        }
733
                        if requeue {
1✔
UNCOV
734
                                queue.AddRateLimited(key)
×
735
                        } else {
1✔
736
                                queue.Forget(key)
1✔
737
                        }
1✔
738
                        queue.Done(key)
1✔
739

740
                }
741
        }, time.Second, stopCh)
742
        <-stopCh
1✔
743
        queue.ShutDown()
1✔
744
}
745

746
func (cont *AciController) globalStaticObjs() apicapi.ApicSlice {
1✔
747
        return apicapi.ApicSlice{}
1✔
748
}
1✔
749

750
func (cont *AciController) aciNameForKey(ktype, key string) string {
1✔
751
        return util.AciNameForKey(cont.config.AciPrefix, ktype, key)
1✔
752
}
1✔
753

754
func (cont *AciController) initStaticObjs() {
1✔
755
        cont.env.InitStaticAciObjects()
1✔
756
        cont.apicConn.WriteStaticApicObjects(cont.config.AciPrefix+"_static",
1✔
757
                cont.globalStaticObjs())
1✔
758
}
1✔
759

760
func (cont *AciController) vmmDomainProvider() (vmmProv string) {
1✔
761
        vmmProv = "Kubernetes"
1✔
762
        if strings.ToLower(cont.config.AciVmmDomainType) == "openshift" {
1✔
UNCOV
763
                vmmProv = "OpenShift"
×
UNCOV
764
        }
×
765
        return
1✔
766
}
767

768
func (cont *AciController) Run(stopCh <-chan struct{}) {
1✔
769
        var err error
1✔
770
        var privKey []byte
1✔
771
        var apicCert []byte
1✔
772

1✔
773
        cont.config.AciVrfDn = "uni/tn-" + cont.config.AciVrfTenant + "/ctx-" + cont.config.AciVrf
1✔
774

1✔
775
        if cont.config.ApicPrivateKeyPath != "" {
1✔
UNCOV
776
                privKey, err = os.ReadFile(cont.config.ApicPrivateKeyPath)
×
UNCOV
777
                if err != nil {
×
UNCOV
778
                        panic(err)
×
779
                }
780
        }
781
        if cont.config.ApicCertPath != "" {
1✔
UNCOV
782
                apicCert, err = os.ReadFile(cont.config.ApicCertPath)
×
UNCOV
783
                if err != nil {
×
UNCOV
784
                        panic(err)
×
785
                }
786
        }
787
        // If not defined, default is 1800
788
        if cont.config.ApicRefreshTimer == "" {
2✔
789
                cont.config.ApicRefreshTimer = "1800"
1✔
790
        }
1✔
791
        refreshTimeout, err := strconv.Atoi(cont.config.ApicRefreshTimer)
1✔
792
        if err != nil {
1✔
UNCOV
793
                panic(err)
×
794
        }
795
        cont.log.Info("ApicRefreshTimer conf is set to: ", refreshTimeout)
1✔
796

1✔
797
        // Bailout if the refreshTimeout is more than 12Hours or less than 5Mins
1✔
798
        if refreshTimeout > (12*60*60) || refreshTimeout < (5*60) {
1✔
UNCOV
799
                cont.log.Info("ApicRefreshTimer can't be more than 12Hrs or less than 5Mins")
×
UNCOV
800
                panic(err)
×
801
        }
802

803
        // If RefreshTickerAdjustInterval is not defined, default to 150Sec.
804
        if cont.config.ApicRefreshTickerAdjust == "" {
2✔
805
                cont.config.ApicRefreshTickerAdjust = "210"
1✔
806
        }
1✔
807
        refreshTickerAdjust, err := strconv.Atoi(cont.config.ApicRefreshTickerAdjust)
1✔
808
        if err != nil {
1✔
809
                panic(err)
×
810
        }
811

812
        // If not defined, default to 900s
813
        if cont.config.LeafRebootCheckInterval == 0 {
2✔
814
                cont.config.LeafRebootCheckInterval = 900
1✔
815
        }
1✔
816

817
        //If ApicSubscriptionDelay is not defined, default to 100ms
818
        if cont.config.ApicSubscriptionDelay == 0 {
2✔
819
                cont.config.ApicSubscriptionDelay = 100
1✔
820
        }
1✔
821
        cont.log.Info("ApicSubscriptionDelay conf is set to: ", cont.config.ApicSubscriptionDelay)
1✔
822

1✔
823
        // If OpflexDeviceDeleteTimeout is not defined, default to 1800s
1✔
824
        if cont.config.OpflexDeviceDeleteTimeout == 0 {
2✔
825
                cont.config.OpflexDeviceDeleteTimeout = 1800
1✔
826
        }
1✔
827

828
        // If OpflexDeviceReconnectWaitTimeout is not defined, default to 25s
829
        if cont.config.OpflexDeviceReconnectWaitTimeout == 0 {
2✔
830
                cont.config.OpflexDeviceReconnectWaitTimeout = 25
1✔
831
        }
1✔
832
        cont.log.Debug("OpflexDeviceReconnectWaitTimeout set to: ", cont.config.OpflexDeviceReconnectWaitTimeout)
1✔
833

1✔
834
        // If SleepTimeSnatGlobalInfoSync is not defined, default to 60
1✔
835
        if cont.config.SleepTimeSnatGlobalInfoSync == 0 {
2✔
836
                cont.config.SleepTimeSnatGlobalInfoSync = 60
1✔
837
        }
1✔
838

839
        // If not defined, default to 32
840
        if cont.config.PodIpPoolChunkSize == 0 {
2✔
841
                cont.config.PodIpPoolChunkSize = 32
1✔
842
        }
1✔
843
        if !cont.isCNOEnabled() {
2✔
844
                cont.log.Info("PodIpPoolChunkSize conf is set to: ", cont.config.PodIpPoolChunkSize)
1✔
845
        }
1✔
846

847
        // If ApicConnectionRetryLimit is not defined, default to 5
848
        if cont.config.ApicConnectionRetryLimit == 0 {
2✔
849
                cont.config.ApicConnectionRetryLimit = 5
1✔
850
        }
1✔
851
        cont.log.Debug("ApicConnectionRetryLimit set to: ", cont.config.ApicConnectionRetryLimit)
1✔
852

1✔
853
        // If not valid, default to 5000-65000
1✔
854
        // other permissible values 1-65000
1✔
855
        defStart := 5000
1✔
856
        defEnd := 65000
1✔
857
        if cont.config.SnatDefaultPortRangeStart == 0 {
2✔
858
                cont.config.SnatDefaultPortRangeStart = defStart
1✔
859
        }
1✔
860
        if cont.config.SnatDefaultPortRangeEnd == 0 {
2✔
861
                cont.config.SnatDefaultPortRangeEnd = defEnd
1✔
862
        }
1✔
863
        if cont.config.SnatDefaultPortRangeStart < 0 || cont.config.SnatDefaultPortRangeEnd < 0 ||
1✔
864
                cont.config.SnatDefaultPortRangeStart > defEnd || cont.config.SnatDefaultPortRangeEnd > defEnd ||
1✔
865
                cont.config.SnatDefaultPortRangeStart > cont.config.SnatDefaultPortRangeEnd {
1✔
UNCOV
866
                cont.config.SnatDefaultPortRangeStart = defStart
×
UNCOV
867
                cont.config.SnatDefaultPortRangeEnd = defEnd
×
UNCOV
868
        }
×
869

870
        // Set default value for pbr programming delay if services list is not empty
871
        // and delay value is empty
872
        if cont.config.ServiceGraphEndpointAddDelay.Delay == 0 &&
1✔
873
                cont.config.ServiceGraphEndpointAddDelay.Services != nil &&
1✔
874
                len(cont.config.ServiceGraphEndpointAddDelay.Services) > 0 {
1✔
UNCOV
875
                cont.config.ServiceGraphEndpointAddDelay.Delay = 90
×
UNCOV
876
        }
×
877
        if cont.config.ServiceGraphEndpointAddDelay.Delay > 0 {
1✔
UNCOV
878
                cont.log.Info("ServiceGraphEndpointAddDelay set to: ", cont.config.ServiceGraphEndpointAddDelay.Delay)
×
UNCOV
879
        }
×
880

881
        // Set contract scope for snat svc graph to global by default
882
        if cont.config.SnatSvcContractScope == "" {
2✔
883
                cont.config.SnatSvcContractScope = "global"
1✔
884
        }
1✔
885
        if cont.config.MaxSvcGraphNodes == 0 {
2✔
886
                cont.config.MaxSvcGraphNodes = 32
1✔
887
        }
1✔
888
        if !cont.isCNOEnabled() {
2✔
889
                cont.log.Info("Max number of nodes per svc graph is set to: ", cont.config.MaxSvcGraphNodes)
1✔
890
        }
1✔
891
        cont.apicConn, err = apicapi.New(cont.log, cont.config.ApicHosts,
1✔
892
                cont.config.ApicUsername, cont.config.ApicPassword,
1✔
893
                privKey, apicCert, cont.config.AciPrefix,
1✔
894
                refreshTimeout, refreshTickerAdjust, cont.config.LeafRebootCheckInterval, cont.config.ApicSubscriptionDelay,
1✔
895
                cont.config.AciVrfTenant, cont.UpdateLLDPIfLocked, cont.isCNOEnabled())
1✔
896
        if err != nil {
1✔
UNCOV
897
                panic(err)
×
898
        }
899

900
        cont.apicConn.FilterOpflexDevice = cont.config.FilterOpflexDevice
1✔
901
        cont.apicConn.Flavor = cont.config.Flavor
1✔
902
        cont.apicConn.VmmDomain = cont.config.AciVmmDomain
1✔
903
        cont.apicConn.ReconnectRetryLimit = cont.config.ApicConnectionRetryLimit
1✔
904
        cont.apicConn.RequestRetryDelayBase = cont.config.ApicRequestRetryDelayBase
1✔
905
        cont.apicConn.EnableRequestRetry = cont.config.EnableApicRequestRetry
1✔
906
        cont.apicConn.DisableRateLimit = cont.config.DisableApicRateLimit
1✔
907

1✔
908
        if len(cont.config.ApicHosts) != 0 {
1✔
UNCOV
909
        APIC_SWITCH:
×
UNCOV
910
                cont.log.WithFields(logrus.Fields{
×
UNCOV
911
                        "mod":  "APICAPI",
×
UNCOV
912
                        "host": cont.apicConn.Apic[cont.apicConn.ApicIndex],
×
UNCOV
913
                }).Debug("Connecting to APIC to determine the Version")
×
UNCOV
914

×
UNCOV
915
                version, err := cont.apicConn.GetVersion()
×
UNCOV
916
                if err != nil {
×
UNCOV
917
                        cont.log.Error("Could not get APIC version, switching to next APIC")
×
UNCOV
918
                        cont.apicConn.ApicIndex = (cont.apicConn.ApicIndex + 1) % len(cont.apicConn.Apic)
×
UNCOV
919
                        time.Sleep(cont.apicConn.ReconnectInterval)
×
UNCOV
920
                        goto APIC_SWITCH
×
921
                }
UNCOV
922
                cont.apicConn.CachedVersion = version
×
UNCOV
923
                apicapi.ApicVersion = version
×
924
                if version >= "4.2(4i)" {
×
UNCOV
925
                        cont.apicConn.SnatPbrFltrChain = true
×
UNCOV
926
                } else {
×
UNCOV
927
                        cont.apicConn.SnatPbrFltrChain = false
×
UNCOV
928
                }
×
UNCOV
929
                if version >= "5.2" {
×
UNCOV
930
                        cont.vmmClusterFaultSupported = true
×
UNCOV
931
                }
×
932
        } else { // For unit-tests
1✔
933
                cont.apicConn.SnatPbrFltrChain = true
1✔
934
        }
1✔
935

936
        if !cont.isCNOEnabled() {
2✔
937
                cont.log.Debug("SnatPbrFltrChain set to:", cont.apicConn.SnatPbrFltrChain)
1✔
938
                // Make sure Pod/NodeBDs are assoicated to same VRF.
1✔
939
                if len(cont.config.ApicHosts) != 0 && cont.config.AciPodBdDn != "" && cont.config.AciNodeBdDn != "" {
1✔
940
                        var expectedVrfRelations []string
×
941
                        expectedVrfRelations = append(expectedVrfRelations, cont.config.AciPodBdDn, cont.config.AciNodeBdDn)
×
942
                        cont.log.Debug("expectedVrfRelations:", expectedVrfRelations)
×
943
                        err = cont.apicConn.ValidateAciVrfAssociation(cont.config.AciVrfDn, expectedVrfRelations)
×
944
                        if err != nil {
×
945
                                cont.log.Error("Pod/NodeBDs and AciL3Out VRF association is incorrect")
×
946
                                panic(err)
×
947
                        }
948
                }
949
        }
950

951
        if len(cont.config.ApicHosts) != 0 && cont.vmmClusterFaultSupported && !cont.isCNOEnabled() {
1✔
952
                //Clear fault instances when the controller starts
×
953
                cont.clearFaultInstances()
×
954
                //Subscribe for vmmEpPD for a given domain
×
955
                var tnTargetFilterEpg string
×
956
                tnTargetFilterEpg += fmt.Sprintf("uni/vmmp-%s/dom-%s/", cont.vmmDomainProvider(), cont.config.AciVmmDomain)
×
957
                subnetTargetFilterEpg := fmt.Sprintf("and(wcard(vmmEpPD.dn,\"%s\"))", tnTargetFilterEpg)
×
958
                cont.apicConn.AddSubscriptionClass("vmmEpPD",
×
UNCOV
959
                        []string{"vmmEpPD"}, subnetTargetFilterEpg)
×
UNCOV
960
                cont.apicConn.SetSubscriptionHooks("vmmEpPD",
×
UNCOV
961
                        func(obj apicapi.ApicObject) bool {
×
UNCOV
962
                                cont.vmmEpPDChanged(obj)
×
UNCOV
963
                                return true
×
UNCOV
964
                        },
×
UNCOV
965
                        func(dn string) {
×
UNCOV
966
                                cont.vmmEpPDDeleted(dn)
×
967
                        })
×
968
        }
969

970
        cont.initStaticObjs()
1✔
971

1✔
972
        err = cont.env.PrepareRun(stopCh)
1✔
973
        if err != nil {
1✔
UNCOV
974
                panic(err.Error())
×
975
        }
976

977
        cont.apicConn.FullSyncHook = func() {
1✔
UNCOV
978
                // put a channel into each work queue and wait on it to
×
979
                // checkpoint object syncing in response to new subscription
×
980
                // updates
×
981
                cont.log.Debug("Starting checkpoint")
×
982
                var chans []chan struct{}
×
983
                qs := make([]workqueue.RateLimitingInterface, 0)
×
984
                _, ok := cont.env.(*K8sEnvironment)
×
985
                if ok {
×
986
                        qs = []workqueue.RateLimitingInterface{cont.podQueue}
×
987
                        if !cont.isCNOEnabled() {
×
988
                                if !cont.config.DisableHppRendering {
×
989
                                        qs = append(qs, cont.netPolQueue)
×
990
                                }
×
991
                                if cont.config.EnableHppDirect {
×
992
                                        qs = append(qs, cont.remIpContQueue)
×
993
                                }
×
994
                                qs = append(qs, cont.qosQueue, cont.serviceQueue,
×
UNCOV
995
                                        cont.snatQueue, cont.netflowQueue, cont.snatNodeInfoQueue,
×
UNCOV
996
                                        cont.rdConfigQueue, cont.erspanQueue,
×
UNCOV
997
                                        cont.epgDnCacheUpdateQueue)
×
998
                        }
999
                }
UNCOV
1000
                for _, q := range qs {
×
1001
                        c := make(chan struct{})
×
UNCOV
1002
                        chans = append(chans, c)
×
UNCOV
1003
                        q.Add(c)
×
UNCOV
1004
                }
×
1005
                for _, c := range chans {
×
1006
                        <-c
×
1007
                }
×
1008
                cont.log.Debug("Checkpoint complete")
×
1009
        }
1010

1011
        if len(cont.config.ApicHosts) != 0 && !cont.isCNOEnabled() {
1✔
1012
                cont.BuildSubnetDnCache(cont.config.AciVrfDn, cont.config.AciVrfDn)
×
1013
                cont.scheduleRdConfig()
×
1014
                if strings.Contains(cont.config.Flavor, "openstack") {
×
1015
                        cont.setOpenStackSystemId()
×
1016
                }
×
1017
        }
1018

1019
        if !cont.isCNOEnabled() {
2✔
1020
                if cont.config.AciPolicyTenant != cont.config.AciVrfTenant {
2✔
1021
                        cont.apicConn.AddSubscriptionDn("uni/tn-"+cont.config.AciPolicyTenant,
1✔
1022
                                []string{"hostprotPol"})
1✔
1023
                }
1✔
1024
        } else {
1✔
1025
                cont.apicConn.AddSubscriptionDn("uni/tn-"+cont.config.AciPolicyTenant,
1✔
1026
                        []string{"fvBD", "fvAp"})
1✔
1027
                cont.apicConn.AddSubscriptionClass("fvnsVlanInstP",
1✔
1028
                        []string{"fvnsVlanInstP"}, "")
1✔
1029
                cont.apicConn.AddSubscriptionClass("infraRsDomP",
1✔
1030
                        []string{"infraRsDomP"}, "")
1✔
1031
                cont.apicConn.AddSubscriptionClass("physDomP",
1✔
1032
                        []string{"physDomP"}, "")
1✔
1033
                cont.apicConn.AddSubscriptionClass("l3extDomP",
1✔
1034
                        []string{"l3extDomP"}, "")
1✔
1035
                cont.apicConn.AddSubscriptionClass("infraRsVlanNs",
1✔
1036
                        []string{"infraRsVlanNs"}, "")
1✔
1037
                cont.apicConn.AddSubscriptionClass("infraGeneric",
1✔
1038
                        []string{"infraGeneric", "infraRsFuncToEpg"}, "")
1✔
1039
                cont.apicConn.AddSubscriptionClass("l3extOut",
1✔
1040
                        []string{"l3extInstP", "l3extSubnet", "fvRsCons", "fvRsProv", "l3extRsEctx", "l3extRsL3DomAtt", "l3extLNodeP", "l3extRsNodeL3OutAtt", "ipRouteP", "ipNexthopP", "l3extLIfP", "l3extVirtualLIfP", "l3extRsDynPathAtt",
1✔
1041
                                "l3extRsPathL3OutAtt", "l3extMember", "l3extIp", "bgpExtP", "bgpPeerP", "bgpAsP", "bgpLocalAsnP", "bgpRsPeerPfxPol"}, "")
1✔
1042
                cont.apicConn.AddSubscriptionClass("bgpPeerPfxPol",
1✔
1043
                        []string{"bgpPeerPfxPol"}, "")
1✔
1044
        }
1✔
1045

1046
        if cont.config.VmmLite {
1✔
UNCOV
1047
                cont.apicConn.AddSubscriptionClass("infraAttEntityP",
×
UNCOV
1048
                        []string{"infraRsFuncToEpg"}, "")
×
UNCOV
1049

×
UNCOV
1050
                cont.apicConn.SetSubscriptionHooks(
×
UNCOV
1051
                        "infraAttEntityP",
×
UNCOV
1052
                        func(obj apicapi.ApicObject) bool {
×
UNCOV
1053
                                cont.log.Debug("EPG attached to AAEP")
×
UNCOV
1054
                                cont.handleAaepEpgAttach(obj)
×
UNCOV
1055
                                return true
×
UNCOV
1056
                        },
×
UNCOV
1057
                        func(dn string) {
×
UNCOV
1058
                                cont.log.Debug("EPG detached from AAEP")
×
UNCOV
1059
                                cont.handleAaepEpgDetach(dn)
×
UNCOV
1060
                        },
×
1061
                )
1062
        }
1063

1064
        if !cont.isCNOEnabled() {
2✔
1065
                // Websocket notifications for objects under vrfTenant are filtered in
1✔
1066
                // handleSocketUpdate() using DN-based prefix matching (conn.prefix + "_").
1✔
1067
                // All MO names created via AciNameForKey contain this prefix pattern.
1✔
1068
                subscribeMo := []string{"fvBD", "vnsLDevVip", "vnsAbsGraph", "vnsLDevCtx",
1✔
1069
                        "vzFilter", "vzBrCP", "l3extInstP", "vnsSvcRedirectPol",
1✔
1070
                        "vnsRedirectHealthGroup", "fvIPSLAMonitoringPol"}
1✔
1071
                if cont.config.AciPolicyTenant == cont.config.AciVrfTenant {
1✔
UNCOV
1072
                        subscribeMo = append(subscribeMo, "hostprotPol")
×
UNCOV
1073
                }
×
1074
                cont.apicConn.AddSubscriptionDn("uni/tn-"+cont.config.AciVrfTenant,
1✔
1075
                        subscribeMo)
1✔
1076
                cont.apicConn.AddSubscriptionDn(fmt.Sprintf("uni/tn-%s/out-%s",
1✔
1077
                        cont.config.AciVrfTenant, cont.config.AciL3Out),
1✔
1078
                        []string{"fvRsCons"})
1✔
1079
                vmmDn := fmt.Sprintf("comp/prov-%s/ctrlr-[%s]-%s/injcont",
1✔
1080
                        cont.env.VmmPolicy(), cont.config.AciVmmDomain,
1✔
1081
                        cont.config.AciVmmController)
1✔
1082
                // Before subscribing to vmm objects, add vmmInjectedLabel as a child after explicit APIC version check
1✔
1083
                // Since it is not supported for APIC versions < "5.0"
1✔
1084
                cont.addVmmInjectedLabel()
1✔
1085
                cont.apicConn.AddSyncDn(vmmDn,
1✔
1086
                        []string{"vmmInjectedHost", "vmmInjectedNs"})
1✔
1087

1✔
1088
                var tnTargetFilter string
1✔
1089
                if len(cont.config.AciVrfRelatedTenants) > 0 {
1✔
UNCOV
1090
                        for _, tn := range cont.config.AciVrfRelatedTenants {
×
UNCOV
1091
                                tnTargetFilter += fmt.Sprintf("tn-%s/|", tn)
×
UNCOV
1092
                        }
×
1093
                } else {
1✔
1094
                        tnTargetFilter += fmt.Sprintf("tn-%s/|tn-%s/",
1✔
1095
                                cont.config.AciPolicyTenant, cont.config.AciVrfTenant)
1✔
1096
                }
1✔
1097
                subnetTargetFilter := fmt.Sprintf("and(wcard(fvSubnet.dn,\"%s\"))",
1✔
1098
                        tnTargetFilter)
1✔
1099
                cont.apicConn.AddSubscriptionClass("fvSubnet",
1✔
1100
                        []string{"fvSubnet"}, subnetTargetFilter)
1✔
1101

1✔
1102
                cont.apicConn.SetSubscriptionHooks("fvSubnet",
1✔
1103
                        func(obj apicapi.ApicObject) bool {
1✔
UNCOV
1104
                                cont.SubnetChanged(obj, cont.config.AciVrfDn)
×
UNCOV
1105
                                return true
×
UNCOV
1106
                        },
×
UNCOV
1107
                        func(dn string) {
×
UNCOV
1108
                                cont.SubnetDeleted(dn)
×
UNCOV
1109
                        })
×
1110

1111
                cont.apicConn.AddSubscriptionClass("opflexODev",
1✔
1112
                        []string{"opflexODev"}, "")
1✔
1113

1✔
1114
                cont.apicConn.SetSubscriptionHooks("opflexODev",
1✔
1115
                        func(obj apicapi.ApicObject) bool {
1✔
UNCOV
1116
                                cont.opflexDeviceChanged(obj)
×
1117
                                return true
×
1118
                        },
×
1119
                        func(dn string) {
×
UNCOV
1120
                                cont.opflexDeviceDeleted(dn)
×
UNCOV
1121
                        })
×
1122

1123
                if !cont.config.DisableServiceVlanPreprovisioning && !strings.Contains(cont.config.Flavor, "openstack") {
2✔
1124
                        if cont.config.AEP == "" {
2✔
1125
                                cont.log.Error("AEP is missing in configuration, preprovisioning of service vlan will be disabled")
1✔
1126
                        } else {
1✔
UNCOV
1127
                                infraRtAttEntPFilter := fmt.Sprintf("and(wcard(infraRtAttEntP.dn,\"/attentp-%s/\"))", cont.config.AEP)
×
UNCOV
1128
                                cont.apicConn.AddSubscriptionClass("infraRtAttEntP",
×
UNCOV
1129
                                        []string{"infraRtAttEntP"}, infraRtAttEntPFilter)
×
UNCOV
1130

×
1131
                                // For bare metal, the infraRtAttEntP associated with an AEP will be empty.
×
1132
                                // We should not receive any updates for such cases.
×
1133
                                cont.apicConn.SetSubscriptionHooks("infraRtAttEntP",
×
1134
                                        func(obj apicapi.ApicObject) bool {
×
1135
                                                cont.infraRtAttEntPChanged(obj)
×
1136
                                                return true
×
UNCOV
1137
                                        },
×
UNCOV
1138
                                        func(dn string) {
×
UNCOV
1139
                                                cont.infraRtAttEntPDeleted(dn)
×
UNCOV
1140
                                        })
×
1141

UNCOV
1142
                                cont.apicConn.AddSubscriptionClass("vpcIf",
×
1143
                                        []string{"vpcIf"}, "")
×
1144

×
1145
                                cont.apicConn.SetSubscriptionHooks("vpcIf",
×
1146
                                        func(obj apicapi.ApicObject) bool {
×
1147
                                                cont.vpcIfChanged(obj)
×
1148
                                                return true
×
UNCOV
1149
                                        },
×
UNCOV
1150
                                        func(dn string) {
×
UNCOV
1151
                                                cont.vpcIfDeleted(dn)
×
UNCOV
1152
                                        })
×
1153
                        }
1154
                }
1155

1156
                cont.apicConn.VersionUpdateHook =
1✔
1157
                        func() {
1✔
1158
                                cont.initStaticServiceObjs()
×
1159
                        }
×
1160
        } else if cont.config.VmmLite {
1✔
1161
                cont.apicConn.VMMLiteSyncHook = func() {
×
1162
                        cont.syncAndCleanNadCache()
×
1163
                        cont.syncAndCleanNads()
×
1164
                }
×
1165
        }
1166
        go cont.apicConn.Run(stopCh)
1✔
1167
}
1168

1169
func (cont *AciController) syncNodeAciPods(stopCh <-chan struct{}, seconds time.Duration) {
1✔
1170
        cont.log.Debug("Go routine to periodically check the aci pod information in the opflexOdev of a node")
1✔
1171
        ticker := time.NewTicker(seconds * time.Second)
1✔
1172
        defer ticker.Stop()
1✔
1173

1✔
1174
        for {
2✔
1175
                select {
1✔
1176
                case <-ticker.C:
1✔
1177
                        if cont.config.EnableOpflexAgentReconnect {
1✔
1178
                                cont.checkChangeOfOpflexOdevAciPod()
×
1179
                        }
×
1180
                        if cont.config.AciMultipod {
1✔
UNCOV
1181
                                cont.checkChangeOfOdevAciPod()
×
UNCOV
1182
                        }
×
1183
                case <-stopCh:
1✔
1184
                        return
1✔
1185
                }
1186
        }
1187
}
1188

1189
func (cont *AciController) syncOpflexDevices(stopCh <-chan struct{}, seconds time.Duration) {
1✔
1190
        cont.log.Debug("Go routine to periodically delete old opflexdevices started")
1✔
1191
        ticker := time.NewTicker(seconds * time.Second)
1✔
1192
        defer ticker.Stop()
1✔
1193

1✔
1194
        for {
2✔
1195
                select {
1✔
1196
                case <-ticker.C:
1✔
1197
                        cont.deleteOldOpflexDevices()
1✔
1198
                case <-stopCh:
1✔
1199
                        return
1✔
1200
                }
1201
        }
1202
}
1203

1204
func (cont *AciController) syncDelayedEpSlices(stopCh <-chan struct{}, seconds time.Duration) {
1✔
1205
        cont.log.Debug("Go routine to periodically check and process the epslices having delay adding in service")
1✔
1206
        ticker := time.NewTicker(seconds * time.Second)
1✔
1207
        defer ticker.Stop()
1✔
1208

1✔
1209
        for {
2✔
1210
                select {
1✔
1211
                case <-ticker.C:
1✔
1212
                        cont.processDelayedEpSlices()
1✔
1213
                case <-stopCh:
1✔
1214
                        return
1✔
1215
                }
1216
        }
1217
}
1218

1219
func (cont *AciController) snatGlobalInfoSync(stopCh <-chan struct{}, seconds int) {
1✔
1220
        time.Sleep(time.Duration(seconds) * time.Second)
1✔
1221
        cont.log.Debug("Go routine to periodically sync globalinfo and nodeinfo started")
1✔
1222
        iteration := 0
1✔
1223
        for {
2✔
1224
                // To avoid noisy logs, only printing once in 5 minutes
1✔
1225
                if iteration%5 == 0 {
2✔
1226
                        cont.log.Debug("Syncing GlobalInfo with Node infos")
1✔
1227
                }
1✔
1228
                var nodeInfos []*nodeinfo.NodeInfo
1✔
1229
                cont.indexMutex.Lock()
1✔
1230
                cache.ListAll(cont.snatNodeInfoIndexer, labels.Everything(),
1✔
1231
                        func(nodeInfoObj interface{}) {
2✔
1232
                                nodeInfo := nodeInfoObj.(*nodeinfo.NodeInfo)
1✔
1233
                                nodeInfos = append(nodeInfos, nodeInfo)
1✔
1234
                        })
1✔
1235
                expectedmap := make(map[string]map[string]bool)
1✔
1236
                for _, glinfo := range cont.snatGlobalInfoCache {
2✔
1237
                        for nodename, entry := range glinfo {
2✔
1238
                                if _, found := expectedmap[nodename]; !found {
2✔
1239
                                        newentry := make(map[string]bool)
1✔
1240
                                        newentry[entry.SnatPolicyName] = true
1✔
1241
                                        expectedmap[nodename] = newentry
1✔
1242
                                } else {
2✔
1243
                                        currententry := expectedmap[nodename]
1✔
1244
                                        currententry[entry.SnatPolicyName] = true
1✔
1245
                                        expectedmap[nodename] = currententry
1✔
1246
                                }
1✔
1247
                        }
1248
                }
1249
                cont.indexMutex.Unlock()
1✔
1250

1✔
1251
                for _, value := range nodeInfos {
2✔
1252
                        marked := false
1✔
1253
                        policyNames := value.Spec.SnatPolicyNames
1✔
1254
                        nodeName := value.ObjectMeta.Name
1✔
1255
                        _, ok := expectedmap[nodeName]
1✔
1256
                        if !ok && len(policyNames) > 0 {
2✔
1257
                                cont.log.Info("Adding missing entry in snatglobalinfo for node: ", nodeName)
1✔
1258
                                cont.log.Debug("No snat policies found in snatglobalinfo")
1✔
1259
                                cont.log.Debug("Snatpolicy list according to nodeinfo: ", policyNames)
1✔
1260
                                marked = true
1✔
1261
                        } else if len(policyNames) != len(expectedmap[nodeName]) {
3✔
1262
                                cont.log.Info("Adding missing snatpolicy entry in snatglobalinfo for node: ", nodeName)
1✔
1263
                                cont.log.Debug("Snatpolicy list according to snatglobalinfo: ", expectedmap[nodeName])
1✔
1264
                                cont.log.Debug("Snatpolicy list according to nodeinfo: ", policyNames)
1✔
1265
                                marked = true
1✔
1266
                        } else {
2✔
1267
                                if len(policyNames) == 0 && len(expectedmap[nodeName]) == 0 {
1✔
UNCOV
1268
                                        // No snatpolicies present
×
UNCOV
1269
                                        continue
×
1270
                                }
1271
                                eq := reflect.DeepEqual(expectedmap[nodeName], policyNames)
1✔
1272
                                if !eq {
2✔
1273
                                        cont.log.Debug("Syncing inconsistent snatpolicy entry in snatglobalinfo for node: ", nodeName)
1✔
1274
                                        cont.log.Debug("Snatpolicy list according to snatglobalinfo: ", expectedmap[nodeName])
1✔
1275
                                        cont.log.Debug("Snatpolicy list according to nodeinfo: ", policyNames)
1✔
1276
                                        marked = true
1✔
1277
                                }
1✔
1278
                        }
1279
                        if marked {
2✔
1280
                                cont.log.Info("Nodeinfo and globalinfo out of sync for node: ", nodeName)
1✔
1281
                                nodeinfokey, err := cache.MetaNamespaceKeyFunc(value)
1✔
1282
                                if err != nil {
1✔
UNCOV
1283
                                        cont.log.Error("Not able to get key for node: ", nodeName)
×
UNCOV
1284
                                        continue
×
1285
                                }
1286
                                cont.log.Info("Queuing nodeinfokey for globalinfo sync: ", nodeinfokey)
1✔
1287
                                cont.queueNodeInfoUpdateByKey(nodeinfokey)
1✔
1288
                        } else if iteration%5 == 0 {
2✔
1289
                                cont.log.Info("Nodeinfo and globalinfo in sync for node: ", nodeName)
1✔
1290
                        }
1✔
1291
                }
1292
                time.Sleep(time.Duration(seconds) * time.Second)
1✔
1293
                iteration++
1✔
1294
        }
1295
}
1296

1297
func (cont *AciController) processSyncQueue(queue workqueue.RateLimitingInterface,
1298
        queueStop <-chan struct{}) {
1✔
1299
        go wait.Until(func() {
2✔
1300
                for {
2✔
1301
                        syncType, quit := queue.Get()
1✔
1302
                        if quit {
2✔
1303
                                break
1✔
1304
                        }
1305
                        var requeue bool
1✔
1306
                        if sType, ok := syncType.(string); ok {
2✔
1307
                                if f, ok := cont.syncProcessors[sType]; ok {
2✔
1308
                                        requeue = f()
1✔
1309
                                }
1✔
1310
                        }
1311
                        if requeue {
1✔
UNCOV
1312
                                queue.AddRateLimited(syncType)
×
1313
                        } else {
1✔
1314
                                queue.Forget(syncType)
1✔
1315
                        }
1✔
1316
                        queue.Done(syncType)
1✔
1317
                }
1318
        }, time.Second, queueStop)
1319
        <-queueStop
1✔
1320
        queue.ShutDown()
1✔
1321
}
1322

1323
func (cont *AciController) scheduleSyncGlobalInfo() {
1✔
1324
        cont.syncQueue.AddRateLimited("snatGlobalInfo")
1✔
1325
}
1✔
UNCOV
1326
func (cont *AciController) scheduleRdConfig() {
×
UNCOV
1327
        cont.syncQueue.AddRateLimited("rdConfig")
×
UNCOV
1328
}
×
UNCOV
1329
func (cont *AciController) scheduleCreateIstioCR() {
×
UNCOV
1330
        cont.syncQueue.AddRateLimited("istioCR")
×
UNCOV
1331
}
×
1332

1333
func (cont *AciController) addVmmInjectedLabel() {
1✔
1334
        if apicapi.ApicVersion >= "5.2" {
1✔
UNCOV
1335
                err := apicapi.AddMetaDataChild("vmmInjectedNs", "vmmInjectedLabel")
×
UNCOV
1336
                if err != nil {
×
UNCOV
1337
                        panic(err.Error())
×
1338
                }
1339
                err = apicapi.AddMetaDataChild("vmmInjectedSvc", "vmmInjectedLabel")
×
UNCOV
1340
                if err != nil {
×
UNCOV
1341
                        panic(err.Error())
×
1342
                }
1343
        }
1344
        if apicapi.ApicVersion >= "5.0" {
2✔
1345
                err := apicapi.AddMetaDataChild("vmmInjectedReplSet", "vmmInjectedLabel")
1✔
1346
                if err != nil {
1✔
UNCOV
1347
                        panic(err.Error())
×
1348
                }
1349
                err = apicapi.AddMetaDataChild("vmmInjectedContGrp", "vmmInjectedLabel")
1✔
1350
                if err != nil {
1✔
UNCOV
1351
                        panic(err.Error())
×
1352
                }
1353
                err = apicapi.AddMetaDataChild("vmmInjectedDepl", "vmmInjectedLabel")
1✔
1354
                if err != nil {
1✔
1355
                        panic(err.Error())
×
1356
                }
1357
        }
1358
}
1359

1360
func (cont *AciController) isCNOEnabled() bool {
1✔
1361
        return cont.config.ChainedMode || cont.config.VmmLite
1✔
1362
}
1✔
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