@@ -10,11 +10,13 @@ import (
10
10
"testing"
11
11
"time"
12
12
13
+ "github.com/google/uuid"
13
14
"github.com/ovn-org/libovsdb/cache"
14
15
"github.com/ovn-org/libovsdb/client"
15
16
"github.com/ovn-org/libovsdb/database/inmemory"
16
17
"github.com/ovn-org/libovsdb/model"
17
18
"github.com/ovn-org/libovsdb/ovsdb"
19
+ "github.com/ovn-org/libovsdb/test"
18
20
"github.com/stretchr/testify/assert"
19
21
"github.com/stretchr/testify/require"
20
22
@@ -516,3 +518,347 @@ func TestMultipleOpsSameRow(t *testing.T) {
516
518
require .Equal (t , map [string ]string {"key1" : "value1" , "keyA" : "valueA" }, br .ExternalIds )
517
519
require .Nil (t , br .DatapathID )
518
520
}
521
+
522
+ func TestReferentialIntegrity (t * testing.T ) {
523
+ // UUIDs to use throughout the tests
524
+ ovsUUID := uuid .New ().String ()
525
+ bridgeUUID := uuid .New ().String ()
526
+ port1UUID := uuid .New ().String ()
527
+ port2UUID := uuid .New ().String ()
528
+ mirrorUUID := uuid .New ().String ()
529
+
530
+ // the test adds an additional op to initialOps to set a reference to
531
+ // the bridge in OVS table
532
+ // the test deletes expectModels at the end
533
+ tests := []struct {
534
+ name string
535
+ initialOps []ovsdb.Operation
536
+ testOps func (client.Client ) ([]ovsdb.Operation , error )
537
+ expectModels []model.Model
538
+ dontExpectModels []model.Model
539
+ expectErr bool
540
+ }{
541
+ {
542
+ name : "strong reference is garbage collected" ,
543
+ initialOps : []ovsdb.Operation {
544
+ {
545
+ Op : ovsdb .OperationInsert ,
546
+ Table : "Bridge" ,
547
+ UUID : bridgeUUID ,
548
+ Row : ovsdb.Row {
549
+ "name" : bridgeUUID ,
550
+ "ports" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }}},
551
+ "mirrors" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : mirrorUUID }}},
552
+ },
553
+ },
554
+ {
555
+ Op : ovsdb .OperationInsert ,
556
+ Table : "Port" ,
557
+ UUID : port1UUID ,
558
+ Row : ovsdb.Row {
559
+ "name" : port1UUID ,
560
+ },
561
+ },
562
+ {
563
+ Op : ovsdb .OperationInsert ,
564
+ Table : "Mirror" ,
565
+ UUID : mirrorUUID ,
566
+ Row : ovsdb.Row {
567
+ "name" : mirrorUUID ,
568
+ "select_src_port" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }}},
569
+ },
570
+ },
571
+ },
572
+ testOps : func (c client.Client ) ([]ovsdb.Operation , error ) {
573
+ // remove the mirror reference
574
+ b := & test.BridgeType {UUID : bridgeUUID }
575
+ return c .Where (b ).Update (b , & b .Mirrors )
576
+ },
577
+ expectModels : []model.Model {
578
+ & test.BridgeType {UUID : bridgeUUID , Name : bridgeUUID , Ports : []string {port1UUID }},
579
+ & test.PortType {UUID : port1UUID , Name : port1UUID },
580
+ },
581
+ dontExpectModels : []model.Model {
582
+ // mirror should have been garbage collected
583
+ & test.MirrorType {UUID : mirrorUUID },
584
+ },
585
+ },
586
+ {
587
+ name : "adding non-root row that is not strongly reference is a noop" ,
588
+ initialOps : []ovsdb.Operation {
589
+ {
590
+ Op : ovsdb .OperationInsert ,
591
+ Table : "Bridge" ,
592
+ UUID : bridgeUUID ,
593
+ Row : ovsdb.Row {
594
+ "name" : bridgeUUID ,
595
+ },
596
+ },
597
+ },
598
+ testOps : func (c client.Client ) ([]ovsdb.Operation , error ) {
599
+ // add a mirror
600
+ m := & test.MirrorType {UUID : mirrorUUID , Name : mirrorUUID }
601
+ return c .Create (m )
602
+ },
603
+ expectModels : []model.Model {
604
+ & test.BridgeType {UUID : bridgeUUID , Name : bridgeUUID },
605
+ },
606
+ dontExpectModels : []model.Model {
607
+ // mirror should have not been added as is not referenced from anywhere
608
+ & test.MirrorType {UUID : mirrorUUID },
609
+ },
610
+ },
611
+ {
612
+ name : "adding non-existent strong reference fails" ,
613
+ initialOps : []ovsdb.Operation {
614
+ {
615
+ Op : ovsdb .OperationInsert ,
616
+ Table : "Bridge" ,
617
+ UUID : bridgeUUID ,
618
+ Row : ovsdb.Row {
619
+ "name" : bridgeUUID ,
620
+ },
621
+ },
622
+ },
623
+ testOps : func (c client.Client ) ([]ovsdb.Operation , error ) {
624
+ // add a mirror
625
+ b := & test.BridgeType {UUID : bridgeUUID , Mirrors : []string {mirrorUUID }}
626
+ return c .Where (b ).Update (b , & b .Mirrors )
627
+ },
628
+ expectModels : []model.Model {
629
+ & test.BridgeType {UUID : bridgeUUID , Name : bridgeUUID },
630
+ },
631
+ expectErr : true ,
632
+ },
633
+ {
634
+ name : "weak reference is garbage collected" ,
635
+ initialOps : []ovsdb.Operation {
636
+ {
637
+ Op : ovsdb .OperationInsert ,
638
+ Table : "Bridge" ,
639
+ UUID : bridgeUUID ,
640
+ Row : ovsdb.Row {
641
+ "name" : bridgeUUID ,
642
+ "ports" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }, ovsdb.UUID {GoUUID : port2UUID }}},
643
+ "mirrors" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : mirrorUUID }}},
644
+ },
645
+ },
646
+ {
647
+ Op : ovsdb .OperationInsert ,
648
+ Table : "Port" ,
649
+ UUID : port1UUID ,
650
+ Row : ovsdb.Row {
651
+ "name" : port1UUID ,
652
+ },
653
+ },
654
+ {
655
+ Op : ovsdb .OperationInsert ,
656
+ Table : "Port" ,
657
+ UUID : port2UUID ,
658
+ Row : ovsdb.Row {
659
+ "name" : port2UUID ,
660
+ },
661
+ },
662
+ {
663
+ Op : ovsdb .OperationInsert ,
664
+ Table : "Mirror" ,
665
+ UUID : mirrorUUID ,
666
+ Row : ovsdb.Row {
667
+ "name" : mirrorUUID ,
668
+ "select_src_port" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }, ovsdb.UUID {GoUUID : port2UUID }}},
669
+ },
670
+ },
671
+ },
672
+ testOps : func (c client.Client ) ([]ovsdb.Operation , error ) {
673
+ // remove port1
674
+ p := & test.PortType {UUID : port1UUID }
675
+ ops , err := c .Where (p ).Delete ()
676
+ if err != nil {
677
+ return nil , err
678
+ }
679
+ b := & test.BridgeType {UUID : bridgeUUID , Ports : []string {port2UUID }}
680
+ op , err := c .Where (b ).Update (b , & b .Ports )
681
+ if err != nil {
682
+ return nil , err
683
+ }
684
+ return append (ops , op ... ), nil
685
+ },
686
+ expectModels : []model.Model {
687
+ & test.BridgeType {UUID : bridgeUUID , Name : bridgeUUID , Ports : []string {port2UUID }, Mirrors : []string {mirrorUUID }},
688
+ & test.PortType {UUID : port2UUID , Name : port2UUID },
689
+ // mirror reference to port1 should have been garbage collected
690
+ & test.MirrorType {UUID : mirrorUUID , Name : mirrorUUID , SelectSrcPort : []string {port2UUID }},
691
+ },
692
+ dontExpectModels : []model.Model {
693
+ & test.PortType {UUID : port1UUID },
694
+ },
695
+ },
696
+ {
697
+ name : "adding a weak reference to a non-existent row is a noop" ,
698
+ initialOps : []ovsdb.Operation {
699
+ {
700
+ Op : ovsdb .OperationInsert ,
701
+ Table : "Bridge" ,
702
+ UUID : bridgeUUID ,
703
+ Row : ovsdb.Row {
704
+ "name" : bridgeUUID ,
705
+ "ports" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }}},
706
+ "mirrors" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : mirrorUUID }}},
707
+ },
708
+ },
709
+ {
710
+ Op : ovsdb .OperationInsert ,
711
+ Table : "Port" ,
712
+ UUID : port1UUID ,
713
+ Row : ovsdb.Row {
714
+ "name" : port1UUID ,
715
+ },
716
+ },
717
+ {
718
+ Op : ovsdb .OperationInsert ,
719
+ Table : "Mirror" ,
720
+ UUID : mirrorUUID ,
721
+ Row : ovsdb.Row {
722
+ "name" : mirrorUUID ,
723
+ "select_src_port" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }}},
724
+ },
725
+ },
726
+ },
727
+ testOps : func (c client.Client ) ([]ovsdb.Operation , error ) {
728
+ // add reference to non-existent port2
729
+ m := & test.MirrorType {UUID : mirrorUUID , SelectSrcPort : []string {port1UUID , port2UUID }}
730
+ return c .Where (m ).Update (m , & m .SelectSrcPort )
731
+ },
732
+ expectModels : []model.Model {
733
+ & test.BridgeType {UUID : bridgeUUID , Name : bridgeUUID , Ports : []string {port1UUID }, Mirrors : []string {mirrorUUID }},
734
+ & test.PortType {UUID : port1UUID , Name : port1UUID },
735
+ // mirror reference to port2 should have been garbage collected resulting in noop
736
+ & test.MirrorType {UUID : mirrorUUID , Name : mirrorUUID , SelectSrcPort : []string {port1UUID }},
737
+ },
738
+ },
739
+ {
740
+ name : "garbage collecting a weak reference on a column lowering it below the min length fails" ,
741
+ initialOps : []ovsdb.Operation {
742
+ {
743
+ Op : ovsdb .OperationInsert ,
744
+ Table : "Bridge" ,
745
+ UUID : bridgeUUID ,
746
+ Row : ovsdb.Row {
747
+ "name" : bridgeUUID ,
748
+ "ports" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }}},
749
+ "mirrors" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : mirrorUUID }}},
750
+ },
751
+ },
752
+ {
753
+ Op : ovsdb .OperationInsert ,
754
+ Table : "Port" ,
755
+ UUID : port1UUID ,
756
+ Row : ovsdb.Row {
757
+ "name" : port1UUID ,
758
+ },
759
+ },
760
+ {
761
+ Op : ovsdb .OperationInsert ,
762
+ Table : "Mirror" ,
763
+ UUID : mirrorUUID ,
764
+ Row : ovsdb.Row {
765
+ "name" : mirrorUUID ,
766
+ "select_src_port" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : port1UUID }}},
767
+ },
768
+ },
769
+ },
770
+ testOps : func (c client.Client ) ([]ovsdb.Operation , error ) {
771
+ // remove port 1
772
+ return c .Where (& test.PortType {UUID : port1UUID }).Delete ()
773
+ },
774
+ expectModels : []model.Model {
775
+ & test.BridgeType {UUID : bridgeUUID , Name : bridgeUUID , Ports : []string {port1UUID }, Mirrors : []string {mirrorUUID }},
776
+ & test.PortType {UUID : port1UUID , Name : port1UUID },
777
+ & test.MirrorType {UUID : mirrorUUID , Name : mirrorUUID , SelectSrcPort : []string {port1UUID }},
778
+ },
779
+ expectErr : true ,
780
+ },
781
+ }
782
+
783
+ for _ , tt := range tests {
784
+ t .Run (tt .name , func (t * testing.T ) {
785
+ c , close := buildTestServerAndClient (t )
786
+ defer close ()
787
+ _ , err := c .MonitorAll (context .Background ())
788
+ require .NoError (t , err )
789
+
790
+ // add the bridge reference to the initial ops
791
+ ops := append (tt .initialOps , ovsdb.Operation {
792
+ Op : ovsdb .OperationInsert ,
793
+ Table : "Open_vSwitch" ,
794
+ UUID : ovsUUID ,
795
+ Row : ovsdb.Row {
796
+ "bridges" : ovsdb.OvsSet {GoSet : []interface {}{ovsdb.UUID {GoUUID : bridgeUUID }}},
797
+ },
798
+ })
799
+
800
+ results , err := c .Transact (context .Background (), ops ... )
801
+ require .NoError (t , err )
802
+ require .Len (t , results , len (ops ))
803
+
804
+ errors , err := ovsdb .CheckOperationResults (results , ops )
805
+ require .Nil (t , errors )
806
+ require .NoError (t , err )
807
+
808
+ ops , err = tt .testOps (c )
809
+ require .NoError (t , err )
810
+
811
+ results , err = c .Transact (context .Background (), ops ... )
812
+ require .NoError (t , err )
813
+
814
+ errors , err = ovsdb .CheckOperationResults (results , ops )
815
+ require .Nil (t , errors )
816
+ if tt .expectErr {
817
+ require .Error (t , err )
818
+ } else {
819
+ require .NoError (t , err )
820
+ }
821
+
822
+ for _ , m := range tt .expectModels {
823
+ actual := model .Clone (m )
824
+ err := c .Get (context .Background (), actual )
825
+ require .NoError (t , err , "when expecting model %v" , m )
826
+ require .Equal (t , m , actual )
827
+ }
828
+
829
+ for _ , m := range tt .dontExpectModels {
830
+ err := c .Get (context .Background (), m )
831
+ require .ErrorIs (t , err , client .ErrNotFound , "when not expecting model %v" , m )
832
+ }
833
+
834
+ ops = []ovsdb.Operation {}
835
+ for _ , m := range tt .expectModels {
836
+ op , err := c .Where (m ).Delete ()
837
+ require .NoError (t , err )
838
+ require .Len (t , op , 1 )
839
+ ops = append (ops , op ... )
840
+ }
841
+
842
+ // remove the bridge reference
843
+ ops = append (ops , ovsdb.Operation {
844
+ Op : ovsdb .OperationDelete ,
845
+ Table : "Open_vSwitch" ,
846
+ Where : []ovsdb.Condition {
847
+ {
848
+ Column : "_uuid" ,
849
+ Function : ovsdb .ConditionEqual ,
850
+ Value : ovsdb.UUID {GoUUID : ovsUUID },
851
+ },
852
+ },
853
+ })
854
+
855
+ results , err = c .Transact (context .Background (), ops ... )
856
+ require .NoError (t , err )
857
+ require .Len (t , results , len (ops ))
858
+
859
+ errors , err = ovsdb .CheckOperationResults (results , ops )
860
+ require .Nil (t , errors )
861
+ require .NoError (t , err )
862
+ })
863
+ }
864
+ }
0 commit comments