diff --git a/go-controller/go.mod b/go-controller/go.mod index 5563c3330bd..ad14b8505a1 100644 --- a/go-controller/go.mod +++ b/go-controller/go.mod @@ -23,7 +23,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.14.0 - github.com/ovn-org/libovsdb v0.6.1-0.20211014201246-28345b9aeccf + github.com/ovn-org/libovsdb v0.6.1-0.20211025161502-80be4acc6773 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/spf13/afero v1.4.1 diff --git a/go-controller/go.sum b/go-controller/go.sum index 924b12e22f4..ceac827c0e5 100644 --- a/go-controller/go.sum +++ b/go-controller/go.sum @@ -349,6 +349,8 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA github.com/ory/dockertest/v3 v3.8.0/go.mod h1:9zPATATlWQru+ynXP+DytBQrsXV7Tmlx7K86H6fQaDo= github.com/ovn-org/libovsdb v0.6.1-0.20211014201246-28345b9aeccf h1:JgpRUwUCOU+++1LLNHhBWSsAmWQ/mK4ALyeO2veR7mA= github.com/ovn-org/libovsdb v0.6.1-0.20211014201246-28345b9aeccf/go.mod h1:aLvY7gPs/vLyJXF+PpZzvWlR5LB4QNJvBYIQMskJLZk= +github.com/ovn-org/libovsdb v0.6.1-0.20211025161502-80be4acc6773 h1:NJgd0Pv3+44oMsDhF8iV53kXJ5EDmQHeCwxWQf3pS4c= +github.com/ovn-org/libovsdb v0.6.1-0.20211025161502-80be4acc6773/go.mod h1:aLvY7gPs/vLyJXF+PpZzvWlR5LB4QNJvBYIQMskJLZk= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/go-controller/pkg/libovsdb/libovsdb.go b/go-controller/pkg/libovsdb/libovsdb.go index 28d9904cbd0..b758d5d9be0 100644 --- a/go-controller/pkg/libovsdb/libovsdb.go +++ b/go-controller/pkg/libovsdb/libovsdb.go @@ -28,7 +28,7 @@ const ( // newClient creates a new client object given the provided config // the stopCh is required to ensure the goroutine for ssl cert // update is not leaked -func newClient(cfg config.OvnAuthConfig, dbModel *model.DBModel, stopCh <-chan struct{}) (client.Client, error) { +func newClient(cfg config.OvnAuthConfig, dbModel *model.ClientDBModel, stopCh <-chan struct{}) (client.Client, error) { logger := klogr.New() options := []client.Option{ client.WithReconnect(OVSDBTimeout, &backoff.ZeroBackOff{}), @@ -98,7 +98,7 @@ func NewNBClientWithConfig(cfg config.OvnAuthConfig, stopCh <-chan struct{}) (cl return newClientWithMonitor(cfg, stopCh, dbModel) } -func newClientWithMonitor(cfg config.OvnAuthConfig, stopCh <-chan struct{}, dbModel *model.DBModel) (client.Client, error) { +func newClientWithMonitor(cfg config.OvnAuthConfig, stopCh <-chan struct{}, dbModel *model.ClientDBModel) (client.Client, error) { c, err := newClient(cfg, dbModel, stopCh) if err != nil { return nil, err diff --git a/go-controller/pkg/nbdb/acl.go b/go-controller/pkg/nbdb/acl.go index 0f2e30d733b..a01dff7a6f3 100644 --- a/go-controller/pkg/nbdb/acl.go +++ b/go-controller/pkg/nbdb/acl.go @@ -18,10 +18,10 @@ var ( ACLDirectionFromLport ACLDirection = "from-lport" ACLDirectionToLport ACLDirection = "to-lport" ACLSeverityAlert ACLSeverity = "alert" - ACLSeverityDebug ACLSeverity = "debug" - ACLSeverityInfo ACLSeverity = "info" - ACLSeverityNotice ACLSeverity = "notice" ACLSeverityWarning ACLSeverity = "warning" + ACLSeverityNotice ACLSeverity = "notice" + ACLSeverityInfo ACLSeverity = "info" + ACLSeverityDebug ACLSeverity = "debug" ) // ACL defines an object in ACL table diff --git a/go-controller/pkg/nbdb/bfd.go b/go-controller/pkg/nbdb/bfd.go index d0a86cbd97b..29e4ba02c01 100644 --- a/go-controller/pkg/nbdb/bfd.go +++ b/go-controller/pkg/nbdb/bfd.go @@ -8,10 +8,10 @@ type ( ) var ( - BFDStatusAdminDown BFDStatus = "admin_down" BFDStatusDown BFDStatus = "down" BFDStatusInit BFDStatus = "init" BFDStatusUp BFDStatus = "up" + BFDStatusAdminDown BFDStatus = "admin_down" ) // BFD defines an object in BFD table diff --git a/go-controller/pkg/nbdb/load_balancer.go b/go-controller/pkg/nbdb/load_balancer.go index cd9ff6e03b4..cb7ef130627 100644 --- a/go-controller/pkg/nbdb/load_balancer.go +++ b/go-controller/pkg/nbdb/load_balancer.go @@ -9,15 +9,15 @@ type ( ) var ( - LoadBalancerProtocolSCTP LoadBalancerProtocol = "sctp" LoadBalancerProtocolTCP LoadBalancerProtocol = "tcp" LoadBalancerProtocolUDP LoadBalancerProtocol = "udp" - LoadBalancerSelectionFieldsEthDst LoadBalancerSelectionFields = "eth_dst" + LoadBalancerProtocolSCTP LoadBalancerProtocol = "sctp" LoadBalancerSelectionFieldsEthSrc LoadBalancerSelectionFields = "eth_src" - LoadBalancerSelectionFieldsIPDst LoadBalancerSelectionFields = "ip_dst" + LoadBalancerSelectionFieldsEthDst LoadBalancerSelectionFields = "eth_dst" LoadBalancerSelectionFieldsIPSrc LoadBalancerSelectionFields = "ip_src" - LoadBalancerSelectionFieldsTpDst LoadBalancerSelectionFields = "tp_dst" + LoadBalancerSelectionFieldsIPDst LoadBalancerSelectionFields = "ip_dst" LoadBalancerSelectionFieldsTpSrc LoadBalancerSelectionFields = "tp_src" + LoadBalancerSelectionFieldsTpDst LoadBalancerSelectionFields = "tp_dst" ) // LoadBalancer defines an object in Load_Balancer table diff --git a/go-controller/pkg/nbdb/logical_router_static_route.go b/go-controller/pkg/nbdb/logical_router_static_route.go index 63b469e6f50..ceae319a066 100644 --- a/go-controller/pkg/nbdb/logical_router_static_route.go +++ b/go-controller/pkg/nbdb/logical_router_static_route.go @@ -8,8 +8,8 @@ type ( ) var ( - LogicalRouterStaticRoutePolicyDstIP LogicalRouterStaticRoutePolicy = "dst-ip" LogicalRouterStaticRoutePolicySrcIP LogicalRouterStaticRoutePolicy = "src-ip" + LogicalRouterStaticRoutePolicyDstIP LogicalRouterStaticRoutePolicy = "dst-ip" ) // LogicalRouterStaticRoute defines an object in Logical_Router_Static_Route table diff --git a/go-controller/pkg/nbdb/model.go b/go-controller/pkg/nbdb/model.go index 2a4b2c6bf57..0e3f0687fa2 100644 --- a/go-controller/pkg/nbdb/model.go +++ b/go-controller/pkg/nbdb/model.go @@ -11,8 +11,8 @@ import ( ) // FullDatabaseModel returns the DatabaseModel object to be used in libovsdb -func FullDatabaseModel() (*model.DBModel, error) { - return model.NewDBModel("OVN_Northbound", map[string]model.Model{ +func FullDatabaseModel() (*model.ClientDBModel, error) { + return model.NewClientDBModel("OVN_Northbound", map[string]model.Model{ "ACL": &ACL{}, "Address_Set": &AddressSet{}, "BFD": &BFD{}, @@ -101,7 +101,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "name": { @@ -111,7 +112,8 @@ var schema = `{ "minLength": 63, "maxLength": 63 }, - "min": 0 + "min": 0, + "max": 1 } }, "priority": { @@ -131,14 +133,15 @@ var schema = `{ "set", [ "alert", - "debug", - "info", + "warning", "notice", - "warning" + "info", + "debug" ] ] }, - "min": 0 + "min": 0, + "max": 1 } } } @@ -184,7 +187,8 @@ var schema = `{ "type": "integer", "minInteger": 1 }, - "min": 0 + "min": 0, + "max": 1 } }, "dst_ip": { @@ -210,7 +214,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } }, "min_tx": { @@ -219,7 +224,8 @@ var schema = `{ "type": "integer", "minInteger": 1 }, - "min": 0 + "min": 0, + "max": 1 } }, "options": { @@ -241,14 +247,15 @@ var schema = `{ "enum": [ "set", [ - "admin_down", "down", "init", - "up" + "up", + "admin_down" ] ] }, - "min": 0 + "min": 0, + "max": 1 } } }, @@ -278,7 +285,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } }, "is_connected": { @@ -291,7 +299,8 @@ var schema = `{ "type": "integer", "minInteger": 1000 }, - "min": 0 + "min": 0, + "max": 1 } }, "other_config": { @@ -395,6 +404,7 @@ var schema = `{ "key": { "type": "string" }, + "min": 1, "max": "unlimited" } }, @@ -518,7 +528,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "HA_Chassis" + "refTable": "HA_Chassis", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -552,7 +563,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Load_Balancer_Health_Check" + "refTable": "Load_Balancer_Health_Check", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -592,13 +604,14 @@ var schema = `{ "enum": [ "set", [ - "sctp", "tcp", - "udp" + "udp", + "sctp" ] ] }, - "min": 0 + "min": 0, + "max": 1 } }, "selection_fields": { @@ -608,12 +621,12 @@ var schema = `{ "enum": [ "set", [ - "eth_dst", "eth_src", - "ip_dst", + "eth_dst", "ip_src", - "tp_dst", - "tp_src" + "ip_dst", + "tp_src", + "tp_dst" ] ] }, @@ -673,7 +686,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -706,7 +720,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "NAT" + "refTable": "NAT", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -728,7 +743,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Logical_Router_Policy" + "refTable": "Logical_Router_Policy", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -738,7 +754,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Logical_Router_Port" + "refTable": "Logical_Router_Port", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -748,7 +765,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Logical_Router_Static_Route" + "refTable": "Logical_Router_Static_Route", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -793,7 +811,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "nexthops": { @@ -835,7 +854,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -854,7 +874,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Gateway_Chassis" + "refTable": "Gateway_Chassis", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -864,9 +885,11 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "HA_Chassis_Group" + "refTable": "HA_Chassis_Group", + "refType": "strong" }, - "min": 0 + "min": 0, + "max": 1 } }, "ipv6_prefix": { @@ -901,6 +924,7 @@ var schema = `{ "key": { "type": "string" }, + "min": 1, "max": "unlimited" } }, @@ -921,7 +945,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } } }, @@ -940,7 +965,8 @@ var schema = `{ "refTable": "BFD", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -978,7 +1004,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "policy": { @@ -988,12 +1015,13 @@ var schema = `{ "enum": [ "set", [ - "dst-ip", - "src-ip" + "src-ip", + "dst-ip" ] ] }, - "min": 0 + "min": 0, + "max": 1 } } } @@ -1004,7 +1032,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "ACL" + "refTable": "ACL", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -1037,7 +1066,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Forwarding_Group" + "refTable": "Forwarding_Group", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -1073,7 +1103,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Logical_Switch_Port" + "refTable": "Logical_Switch_Port", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -1083,7 +1114,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "QoS" + "refTable": "QoS", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -1109,7 +1141,8 @@ var schema = `{ "refTable": "DHCP_Options", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "dhcpv6_options": { @@ -1119,7 +1152,8 @@ var schema = `{ "refTable": "DHCP_Options", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "dynamic_addresses": { @@ -1127,7 +1161,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "enabled": { @@ -1135,7 +1170,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -1154,9 +1190,11 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "HA_Chassis_Group" + "refTable": "HA_Chassis_Group", + "refType": "strong" }, - "min": 0 + "min": 0, + "max": 1 } }, "name": { @@ -1179,7 +1217,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "port_security": { @@ -1198,7 +1237,8 @@ var schema = `{ "minInteger": 1, "maxInteger": 4095 }, - "min": 0 + "min": 0, + "max": 1 } }, "tag_request": { @@ -1208,7 +1248,8 @@ var schema = `{ "minInteger": 0, "maxInteger": 4095 }, - "min": 0 + "min": 0, + "max": 1 } }, "type": { @@ -1219,7 +1260,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } } }, @@ -1235,8 +1277,10 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Meter_Band" + "refTable": "Meter_Band", + "refType": "strong" }, + "min": 1, "max": "unlimited" } }, @@ -1257,7 +1301,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "name": { @@ -1332,18 +1377,22 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Address_Set" + "refTable": "Address_Set", + "refType": "strong" }, - "min": 0 + "min": 0, + "max": 1 } }, "exempted_ext_ips": { "type": { "key": { "type": "uuid", - "refTable": "Address_Set" + "refTable": "Address_Set", + "refType": "strong" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -1366,7 +1415,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_port_range": { @@ -1380,7 +1430,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "options": { @@ -1403,8 +1454,8 @@ var schema = `{ "set", [ "dnat", - "dnat_and_snat", - "snat" + "snat", + "dnat_and_snat" ] ] } @@ -1478,7 +1529,8 @@ var schema = `{ "type": "uuid", "refTable": "SSL" }, - "min": 0 + "min": 0, + "max": 1 } } } @@ -1489,7 +1541,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "ACL" + "refTable": "ACL", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -1552,8 +1605,8 @@ var schema = `{ "enum": [ "set", [ - "burst", - "rate" + "rate", + "burst" ] ] }, diff --git a/go-controller/pkg/nbdb/nat.go b/go-controller/pkg/nbdb/nat.go index 437fe93024b..d48f4d8c9af 100644 --- a/go-controller/pkg/nbdb/nat.go +++ b/go-controller/pkg/nbdb/nat.go @@ -9,8 +9,8 @@ type ( var ( NATTypeDNAT NATType = "dnat" - NATTypeDNATAndSNAT NATType = "dnat_and_snat" NATTypeSNAT NATType = "snat" + NATTypeDNATAndSNAT NATType = "dnat_and_snat" ) // NAT defines an object in NAT table diff --git a/go-controller/pkg/nbdb/qos.go b/go-controller/pkg/nbdb/qos.go index ce8faa7e55e..2e6ea34278d 100644 --- a/go-controller/pkg/nbdb/qos.go +++ b/go-controller/pkg/nbdb/qos.go @@ -11,8 +11,8 @@ type ( var ( QoSActionDSCP QoSAction = "dscp" - QoSBandwidthBurst QoSBandwidth = "burst" QoSBandwidthRate QoSBandwidth = "rate" + QoSBandwidthBurst QoSBandwidth = "burst" QoSDirectionFromLport QoSDirection = "from-lport" QoSDirectionToLport QoSDirection = "to-lport" ) diff --git a/go-controller/pkg/sbdb/bfd.go b/go-controller/pkg/sbdb/bfd.go index 05f9c5396ac..cf3c2e4acec 100644 --- a/go-controller/pkg/sbdb/bfd.go +++ b/go-controller/pkg/sbdb/bfd.go @@ -8,10 +8,10 @@ type ( ) var ( - BFDStatusAdminDown BFDStatus = "admin_down" BFDStatusDown BFDStatus = "down" BFDStatusInit BFDStatus = "init" BFDStatusUp BFDStatus = "up" + BFDStatusAdminDown BFDStatus = "admin_down" ) // BFD defines an object in BFD table diff --git a/go-controller/pkg/sbdb/dhcp_options.go b/go-controller/pkg/sbdb/dhcp_options.go index d5e873d6055..322e234f0b7 100644 --- a/go-controller/pkg/sbdb/dhcp_options.go +++ b/go-controller/pkg/sbdb/dhcp_options.go @@ -9,14 +9,14 @@ type ( var ( DHCPOptionsTypeBool DHCPOptionsType = "bool" - DHCPOptionsTypeDomains DHCPOptionsType = "domains" - DHCPOptionsTypeHostID DHCPOptionsType = "host_id" + DHCPOptionsTypeUint8 DHCPOptionsType = "uint8" + DHCPOptionsTypeUint16 DHCPOptionsType = "uint16" + DHCPOptionsTypeUint32 DHCPOptionsType = "uint32" DHCPOptionsTypeIpv4 DHCPOptionsType = "ipv4" DHCPOptionsTypeStaticRoutes DHCPOptionsType = "static_routes" DHCPOptionsTypeStr DHCPOptionsType = "str" - DHCPOptionsTypeUint16 DHCPOptionsType = "uint16" - DHCPOptionsTypeUint32 DHCPOptionsType = "uint32" - DHCPOptionsTypeUint8 DHCPOptionsType = "uint8" + DHCPOptionsTypeHostID DHCPOptionsType = "host_id" + DHCPOptionsTypeDomains DHCPOptionsType = "domains" ) // DHCPOptions defines an object in DHCP_Options table diff --git a/go-controller/pkg/sbdb/dhcpv6_options.go b/go-controller/pkg/sbdb/dhcpv6_options.go index 39b1602d499..7bca86d149a 100644 --- a/go-controller/pkg/sbdb/dhcpv6_options.go +++ b/go-controller/pkg/sbdb/dhcpv6_options.go @@ -9,8 +9,8 @@ type ( var ( DHCPv6OptionsTypeIpv6 DHCPv6OptionsType = "ipv6" - DHCPv6OptionsTypeMAC DHCPv6OptionsType = "mac" DHCPv6OptionsTypeStr DHCPv6OptionsType = "str" + DHCPv6OptionsTypeMAC DHCPv6OptionsType = "mac" ) // DHCPv6Options defines an object in DHCPv6_Options table diff --git a/go-controller/pkg/sbdb/load_balancer.go b/go-controller/pkg/sbdb/load_balancer.go index ca0c5474327..08bb844e01d 100644 --- a/go-controller/pkg/sbdb/load_balancer.go +++ b/go-controller/pkg/sbdb/load_balancer.go @@ -8,9 +8,9 @@ type ( ) var ( - LoadBalancerProtocolSCTP LoadBalancerProtocol = "sctp" LoadBalancerProtocolTCP LoadBalancerProtocol = "tcp" LoadBalancerProtocolUDP LoadBalancerProtocol = "udp" + LoadBalancerProtocolSCTP LoadBalancerProtocol = "sctp" ) // LoadBalancer defines an object in Load_Balancer table diff --git a/go-controller/pkg/sbdb/logical_flow.go b/go-controller/pkg/sbdb/logical_flow.go index 1c98b6f7901..eb0db31615e 100644 --- a/go-controller/pkg/sbdb/logical_flow.go +++ b/go-controller/pkg/sbdb/logical_flow.go @@ -8,8 +8,8 @@ type ( ) var ( - LogicalFlowPipelineEgress LogicalFlowPipeline = "egress" LogicalFlowPipelineIngress LogicalFlowPipeline = "ingress" + LogicalFlowPipelineEgress LogicalFlowPipeline = "egress" ) // LogicalFlow defines an object in Logical_Flow table diff --git a/go-controller/pkg/sbdb/model.go b/go-controller/pkg/sbdb/model.go index 9704c5b882a..8e4331254d9 100644 --- a/go-controller/pkg/sbdb/model.go +++ b/go-controller/pkg/sbdb/model.go @@ -11,8 +11,8 @@ import ( ) // FullDatabaseModel returns the DatabaseModel object to be used in libovsdb -func FullDatabaseModel() (*model.DBModel, error) { - return model.NewDBModel("OVN_Southbound", map[string]model.Model{ +func FullDatabaseModel() (*model.ClientDBModel, error) { + return model.NewClientDBModel("OVN_Southbound", map[string]model.Model{ "Address_Set": &AddressSet{}, "BFD": &BFD{}, "Chassis": &Chassis{}, @@ -132,10 +132,10 @@ var schema = `{ "enum": [ "set", [ - "admin_down", "down", "init", - "up" + "up", + "admin_down" ] ] } @@ -159,6 +159,7 @@ var schema = `{ "type": "uuid", "refTable": "Encap" }, + "min": 1, "max": "unlimited" } }, @@ -229,7 +230,8 @@ var schema = `{ "refTable": "Chassis", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -279,7 +281,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } }, "is_connected": { @@ -292,7 +295,8 @@ var schema = `{ "type": "integer", "minInteger": 1000 }, - "min": 0 + "min": 0, + "max": 1 } }, "other_config": { @@ -345,7 +349,8 @@ var schema = `{ "refTable": "Chassis", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "event_info": { @@ -395,14 +400,14 @@ var schema = `{ "set", [ "bool", - "domains", - "host_id", + "uint8", + "uint16", + "uint32", "ipv4", "static_routes", "str", - "uint16", - "uint32", - "uint8" + "host_id", + "domains" ] ] } @@ -432,8 +437,8 @@ var schema = `{ "set", [ "ipv6", - "mac", - "str" + "str", + "mac" ] ] } @@ -449,6 +454,7 @@ var schema = `{ "type": "uuid", "refTable": "Datapath_Binding" }, + "min": 1, "max": "unlimited" } }, @@ -602,7 +608,8 @@ var schema = `{ "refTable": "Chassis", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -657,7 +664,8 @@ var schema = `{ "refTable": "Chassis", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -701,7 +709,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "HA_Chassis" + "refTable": "HA_Chassis", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -740,7 +749,8 @@ var schema = `{ "refTable": "Chassis", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "datapath": { @@ -750,7 +760,8 @@ var schema = `{ "refTable": "Datapath_Binding", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "ports": { @@ -789,7 +800,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "eth_src": { @@ -800,7 +812,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } }, "ip4_src": { @@ -814,7 +827,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "query_interval": { @@ -822,7 +836,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } }, "query_max_resp": { @@ -830,7 +845,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } }, "seq_no": { @@ -841,7 +857,8 @@ var schema = `{ "key": { "type": "integer" }, - "min": 0 + "min": 0, + "max": 1 } } }, @@ -897,13 +914,14 @@ var schema = `{ "enum": [ "set", [ - "sctp", "tcp", - "udp" + "udp", + "sctp" ] ] }, - "min": 0 + "min": 0, + "max": 1 } }, "vips": { @@ -958,7 +976,8 @@ var schema = `{ "type": "uuid", "refTable": "Datapath_Binding" }, - "min": 0 + "min": 0, + "max": 1 } }, "logical_dp_group": { @@ -967,7 +986,8 @@ var schema = `{ "type": "uuid", "refTable": "Logical_DP_Group" }, - "min": 0 + "min": 0, + "max": 1 } }, "match": { @@ -980,8 +1000,8 @@ var schema = `{ "enum": [ "set", [ - "egress", - "ingress" + "ingress", + "egress" ] ] } @@ -1040,8 +1060,10 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Meter_Band" + "refTable": "Meter_Band", + "refType": "strong" }, + "min": 1, "max": "unlimited" } }, @@ -1153,7 +1175,8 @@ var schema = `{ "refTable": "Chassis", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "datapath": { @@ -1171,7 +1194,8 @@ var schema = `{ "refTable": "Encap", "refType": "weak" }, - "min": 0 + "min": 0, + "max": 1 } }, "external_ids": { @@ -1190,7 +1214,8 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "Gateway_Chassis" + "refTable": "Gateway_Chassis", + "refType": "strong" }, "min": 0, "max": "unlimited" @@ -1200,9 +1225,11 @@ var schema = `{ "type": { "key": { "type": "uuid", - "refTable": "HA_Chassis_Group" + "refTable": "HA_Chassis_Group", + "refType": "strong" }, - "min": 0 + "min": 0, + "max": 1 } }, "logical_port": { @@ -1243,7 +1270,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } }, "tag": { @@ -1253,7 +1281,8 @@ var schema = `{ "minInteger": 1, "maxInteger": 4095 }, - "min": 0 + "min": 0, + "max": 1 } }, "tunnel_key": { @@ -1273,7 +1302,8 @@ var schema = `{ "key": { "type": "boolean" }, - "min": 0 + "min": 0, + "max": 1 } }, "virtual_parent": { @@ -1281,7 +1311,8 @@ var schema = `{ "key": { "type": "string" }, - "min": 0 + "min": 0, + "max": 1 } } }, @@ -1413,7 +1444,8 @@ var schema = `{ "type": "uuid", "refTable": "SSL" }, - "min": 0 + "min": 0, + "max": 1 } } } @@ -1505,7 +1537,8 @@ var schema = `{ ] ] }, - "min": 0 + "min": 0, + "max": 1 } }, "src_ip": { @@ -1521,13 +1554,14 @@ var schema = `{ "enum": [ "set", [ - "error", + "online", "offline", - "online" + "error" ] ] }, - "min": 0 + "min": 0, + "max": 1 } } }, diff --git a/go-controller/pkg/sbdb/service_monitor.go b/go-controller/pkg/sbdb/service_monitor.go index 06809071bc6..8fc390873f7 100644 --- a/go-controller/pkg/sbdb/service_monitor.go +++ b/go-controller/pkg/sbdb/service_monitor.go @@ -11,9 +11,9 @@ type ( var ( ServiceMonitorProtocolTCP ServiceMonitorProtocol = "tcp" ServiceMonitorProtocolUDP ServiceMonitorProtocol = "udp" - ServiceMonitorStatusError ServiceMonitorStatus = "error" - ServiceMonitorStatusOffline ServiceMonitorStatus = "offline" ServiceMonitorStatusOnline ServiceMonitorStatus = "online" + ServiceMonitorStatusOffline ServiceMonitorStatus = "offline" + ServiceMonitorStatusError ServiceMonitorStatus = "error" ) // ServiceMonitor defines an object in Service_Monitor table diff --git a/go-controller/pkg/testing/libovsdb/libovsdb.go b/go-controller/pkg/testing/libovsdb/libovsdb.go index 4bb9d3486fa..9a727565174 100644 --- a/go-controller/pkg/testing/libovsdb/libovsdb.go +++ b/go-controller/pkg/testing/libovsdb/libovsdb.go @@ -131,14 +131,20 @@ func newNBServer(cfg config.OvnAuthConfig, data []TestData) (*server.OvsdbServer return newOVSDBServer(cfg, dbModel, schema, data) } -func updateData(db server.Database, dbModel *model.DBModel, schema ovsdb.DatabaseSchema, data []TestData) error { +func updateData(db server.Database, dbModel *model.ClientDBModel, schema ovsdb.DatabaseSchema, data []TestData) error { dbName := dbModel.Name() m := mapper.NewMapper(&schema) updates := ovsdb.TableUpdates2{} namedUUIDs := map[string]string{} newData := copystructure.Must(copystructure.Copy(data)).([]TestData) + + dbMod, errs := model.NewDatabaseModel(&schema, dbModel) + if len(errs) > 0 { + return errs[0] + } + for _, d := range newData { - tableName := dbModel.FindTable(reflect.TypeOf(d)) + tableName := dbMod.FindTable(reflect.TypeOf(d)) if tableName == "" { return fmt.Errorf("object of type %s is not part of the DBModel", reflect.TypeOf(d)) } @@ -168,7 +174,12 @@ func updateData(db server.Database, dbModel *model.DBModel, schema ovsdb.Databas namedUUIDs[namedUUID] = uuid } - row, err := m.NewRow(tableName, d) + info, err := mapper.NewInfo(tableName, schema.Table(tableName), d) + if err != nil { + return err + } + + row, err := m.NewRow(info) if err != nil { return err } @@ -189,27 +200,29 @@ func updateData(db server.Database, dbModel *model.DBModel, schema ovsdb.Databas return nil } -func newOVSDBServer(cfg config.OvnAuthConfig, dbModel *model.DBModel, schema ovsdb.DatabaseSchema, data []TestData) (*server.OvsdbServer, error) { +func newOVSDBServer(cfg config.OvnAuthConfig, dbModel *model.ClientDBModel, schema ovsdb.DatabaseSchema, data []TestData) (*server.OvsdbServer, error) { serverDBModel, err := serverdb.FullDatabaseModel() if err != nil { return nil, err } serverSchema := serverdb.Schema() - db := server.NewInMemoryDatabase(map[string]*model.DBModel{ + db := server.NewInMemoryDatabase(map[string]*model.ClientDBModel{ schema.Name: dbModel, serverSchema.Name: serverDBModel, }) - s, err := server.NewOvsdbServer(db, - server.DatabaseModel{ - Model: dbModel, - Schema: &schema, - }, - server.DatabaseModel{ - Model: serverDBModel, - Schema: &serverSchema, - }, - ) + + dbMod, errs := model.NewDatabaseModel(&schema, dbModel) + if len(errs) > 0 { + log.Fatal(errs) + } + + servMod, errs := model.NewDatabaseModel(&serverSchema, serverDBModel) + if len(errs) > 0 { + log.Fatal(errs) + } + + s, err := server.NewOvsdbServer(db, dbMod, servMod) if err != nil { return nil, err } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go b/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go index 77e50b1690a..4acfa7fa3a7 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go @@ -89,7 +89,7 @@ func newIndex(columns ...string) index { // RowCache is a collections of Models hashed by UUID type RowCache struct { name string - schema ovsdb.TableSchema + dbModel *model.DatabaseModel dataType reflect.Type cache map[string]model.Model indexes columnToValue @@ -113,7 +113,7 @@ func (r *RowCache) RowByModel(m model.Model) model.Model { if reflect.TypeOf(m) != r.dataType { return nil } - info, _ := mapper.NewInfo(&r.schema, m) + info, _ := r.dbModel.NewModelInfo(m) uuid, err := info.FieldByColumn("_uuid") if err != nil { return nil @@ -143,11 +143,11 @@ func (r *RowCache) Create(uuid string, m model.Model, checkIndexes bool) error { if reflect.TypeOf(m) != r.dataType { return fmt.Errorf("expected data of type %s, but got %s", r.dataType.String(), reflect.TypeOf(m).String()) } - info, err := mapper.NewInfo(&r.schema, m) + info, err := r.dbModel.NewModelInfo(m) if err != nil { return err } - newIndexes := newColumnToValue(r.schema.Indexes) + newIndexes := newColumnToValue(r.dbModel.Schema().Table(r.name).Indexes) for index := range r.indexes { val, err := valueFromIndex(info, index) if err != nil { @@ -179,16 +179,17 @@ func (r *RowCache) Update(uuid string, m model.Model, checkIndexes bool) error { return NewErrCacheInconsistent(fmt.Sprintf("cannot update row %s as it does not exist in the cache", uuid)) } oldRow := model.Clone(r.cache[uuid]) - oldInfo, err := mapper.NewInfo(&r.schema, oldRow) + oldInfo, err := r.dbModel.NewModelInfo(oldRow) if err != nil { return err } - newInfo, err := mapper.NewInfo(&r.schema, m) + newInfo, err := r.dbModel.NewModelInfo(m) if err != nil { return err } - newIndexes := newColumnToValue(r.schema.Indexes) - oldIndexes := newColumnToValue(r.schema.Indexes) + indexes := r.dbModel.Schema().Table(r.name).Indexes + newIndexes := newColumnToValue(indexes) + oldIndexes := newColumnToValue(indexes) var errs []error for index := range r.indexes { var err error @@ -241,7 +242,7 @@ func (r *RowCache) Update(uuid string, m model.Model, checkIndexes bool) error { } func (r *RowCache) IndexExists(row model.Model) error { - info, err := mapper.NewInfo(&r.schema, row) + info, err := r.dbModel.NewModelInfo(row) if err != nil { return err } @@ -275,7 +276,7 @@ func (r *RowCache) Delete(uuid string) error { return NewErrCacheInconsistent(fmt.Sprintf("cannot delete row %s as it does not exist in the cache", uuid)) } oldRow := r.cache[uuid] - oldInfo, err := mapper.NewInfo(&r.schema, oldRow) + oldInfo, err := r.dbModel.NewModelInfo(oldRow) if err != nil { return err } @@ -301,11 +302,12 @@ func (r *RowCache) Rows() map[string]model.Model { return result } -func (r *RowCache) RowsByCondition(conditions []ovsdb.Condition) ([]model.Model, error) { - var results []model.Model +func (r *RowCache) RowsByCondition(conditions []ovsdb.Condition) (map[string]model.Model, error) { + results := make(map[string]model.Model) + schema := r.dbModel.Schema().Table(r.name) if len(conditions) == 0 { - for _, row := range r.Rows() { - results = append(results, row) + for uuid, row := range r.Rows() { + results[uuid] = row } return results, nil } @@ -323,12 +325,12 @@ func (r *RowCache) RowsByCondition(conditions []ovsdb.Condition) ([]model.Model, return nil, err } if ok { - results = append(results, row) + results[rowUUID] = row } } } else if index, err := r.Index(condition.Column); err != nil { - for k, v := range index { - tSchema := r.schema.Columns[condition.Column] + for k, rowUUID := range index { + tSchema := schema.Columns[condition.Column] nativeValue, err := ovsdb.OvsToNative(tSchema, condition.Value) if err != nil { return nil, err @@ -338,13 +340,13 @@ func (r *RowCache) RowsByCondition(conditions []ovsdb.Condition) ([]model.Model, return nil, err } if ok { - row := r.Row(v) - results = append(results, row) + row := r.Row(rowUUID) + results[rowUUID] = row } } } else { - for _, row := range r.Rows() { - info, err := mapper.NewInfo(&r.schema, row) + for uuid, row := range r.Rows() { + info, err := r.dbModel.NewModelInfo(row) if err != nil { return nil, err } @@ -357,7 +359,7 @@ func (r *RowCache) RowsByCondition(conditions []ovsdb.Condition) ([]model.Model, return nil, err } if ok { - results = append(results, row) + results[uuid] = row } } } @@ -425,11 +427,7 @@ func (e *EventHandlerFuncs) OnDelete(table string, row model.Model) { type TableCache struct { cache map[string]*RowCache eventProcessor *eventProcessor - mapper *mapper.Mapper - dbModel *model.DBModel - schema *ovsdb.DatabaseSchema - updates chan ovsdb.TableUpdates - updates2 chan ovsdb.TableUpdates2 + dbModel *model.DatabaseModel errorChan chan error ovsdb.NotificationHandler mutex sync.RWMutex @@ -440,9 +438,9 @@ type TableCache struct { type Data map[string]map[string]model.Model // NewTableCache creates a new TableCache -func NewTableCache(schema *ovsdb.DatabaseSchema, dbModel *model.DBModel, data Data, logger *logr.Logger) (*TableCache, error) { - if schema == nil || dbModel == nil { - return nil, fmt.Errorf("tablecache without databasemodel cannot be populated") +func NewTableCache(dbModel *model.DatabaseModel, data Data, logger *logr.Logger) (*TableCache, error) { + if !dbModel.Valid() { + return nil, fmt.Errorf("tablecache without valid databasemodel cannot be populated") } if logger == nil { l := stdr.NewWithOptions(log.New(os.Stderr, "", log.LstdFlags), stdr.Options{LogCaller: stdr.All}).WithName("libovsdb/cache") @@ -454,11 +452,11 @@ func NewTableCache(schema *ovsdb.DatabaseSchema, dbModel *model.DBModel, data Da eventProcessor := newEventProcessor(bufferSize, logger) cache := make(map[string]*RowCache) tableTypes := dbModel.Types() - for name, tableSchema := range schema.Tables { - cache[name] = newRowCache(name, tableSchema, tableTypes[name]) + for name := range dbModel.Schema().Tables { + cache[name] = newRowCache(name, dbModel, tableTypes[name]) } for table, rowData := range data { - if _, ok := schema.Tables[table]; !ok { + if _, ok := dbModel.Schema().Tables[table]; !ok { return nil, fmt.Errorf("table %s is not in schema", table) } for uuid, row := range rowData { @@ -469,13 +467,9 @@ func NewTableCache(schema *ovsdb.DatabaseSchema, dbModel *model.DBModel, data Da } return &TableCache{ cache: cache, - schema: schema, eventProcessor: eventProcessor, - mapper: mapper.NewMapper(schema), dbModel: dbModel, mutex: sync.RWMutex{}, - updates: make(chan ovsdb.TableUpdates, bufferSize), - updates2: make(chan ovsdb.TableUpdates2, bufferSize), errorChan: make(chan error), logger: logger, }, nil @@ -483,11 +477,11 @@ func NewTableCache(schema *ovsdb.DatabaseSchema, dbModel *model.DBModel, data Da // Mapper returns the mapper func (t *TableCache) Mapper() *mapper.Mapper { - return t.mapper + return t.dbModel.Mapper() } -// DBModel returns the DBModel -func (t *TableCache) DBModel() *model.DBModel { +// DatabaseModel returns the DatabaseModelRequest +func (t *TableCache) DatabaseModel() *model.DatabaseModel { return t.dbModel } @@ -515,21 +509,31 @@ func (t *TableCache) Tables() []string { // Update implements the update method of the NotificationHandler interface // this populates a channel with updates so they can be processed after the initial // state has been Populated -func (t *TableCache) Update(context interface{}, tableUpdates ovsdb.TableUpdates) { +func (t *TableCache) Update(context interface{}, tableUpdates ovsdb.TableUpdates) error { if len(tableUpdates) == 0 { - return + return nil } - t.updates <- tableUpdates + if err := t.Populate(tableUpdates); err != nil { + t.logger.Error(err, "during libovsdb cache populate") + t.errorChan <- NewErrCacheInconsistent(err.Error()) + return err + } + return nil } // Update2 implements the update method of the NotificationHandler interface // this populates a channel with updates so they can be processed after the initial // state has been Populated -func (t *TableCache) Update2(context interface{}, tableUpdates ovsdb.TableUpdates2) { +func (t *TableCache) Update2(context interface{}, tableUpdates ovsdb.TableUpdates2) error { if len(tableUpdates) == 0 { - return + return nil + } + if err := t.Populate2(tableUpdates); err != nil { + t.logger.Error(err, "during libovsdb cache populate2") + t.errorChan <- NewErrCacheInconsistent(err.Error()) + return err } - t.updates2 <- tableUpdates + return nil } // Locked implements the locked method of the NotificationHandler interface @@ -636,12 +640,12 @@ func (t *TableCache) Populate2(tableUpdates ovsdb.TableUpdates2) error { case row.Modify != nil: existing := tCache.Row(uuid) if existing == nil { - panic(fmt.Errorf("row with uuid %s does not exist", uuid)) + return fmt.Errorf("row with uuid %s does not exist", uuid) } modified := tCache.Row(uuid) err := t.ApplyModifications(table, modified, *row.Modify) if err != nil { - return err + return fmt.Errorf("unable to apply row modifications: %v", err) } if !reflect.DeepEqual(modified, existing) { t.logger.V(5).Info("updating row", "uuid", uuid, "old", fmt.Sprintf("%+v", existing), "new", fmt.Sprintf("%+v", modified)) @@ -657,7 +661,7 @@ func (t *TableCache) Populate2(tableUpdates ovsdb.TableUpdates2) error { // no value on the wire), then process a delete m := tCache.Row(uuid) if m == nil { - panic(fmt.Errorf("row with uuid %s does not exist", uuid)) + return fmt.Errorf("row with uuid %s does not exist", uuid) } t.logger.V(5).Info("deleting row", "uuid", uuid, "model", fmt.Sprintf("%+v", m)) if err := tCache.Delete(uuid); err != nil { @@ -671,13 +675,14 @@ func (t *TableCache) Populate2(tableUpdates ovsdb.TableUpdates2) error { } // Purge drops all data in the cache and reinitializes it using the -// provided schema -func (t *TableCache) Purge(schema *ovsdb.DatabaseSchema) { +// provided database model +func (t *TableCache) Purge(dbModel *model.DatabaseModel) { t.mutex.Lock() defer t.mutex.Unlock() + t.dbModel = dbModel tableTypes := t.dbModel.Types() - for name, tableSchema := range t.schema.Tables { - t.cache[name] = newRowCache(name, tableSchema, tableTypes[name]) + for name := range t.dbModel.Schema().Tables { + t.cache[name] = newRowCache(name, t.dbModel, tableTypes[name]) } } @@ -692,12 +697,8 @@ func (t *TableCache) AddEventHandler(handler EventHandler) { func (t *TableCache) Run(stopCh <-chan struct{}) { wg := sync.WaitGroup{} wg.Add(1) - go t.processUpdates(stopCh) - wg.Add(1) go t.eventProcessor.Run(stopCh) wg.Wait() - t.updates = make(chan ovsdb.TableUpdates, bufferSize) - t.updates2 = make(chan ovsdb.TableUpdates2, bufferSize) } // Errors returns a channel where errors that occur during cache propagation can be received @@ -705,40 +706,13 @@ func (t *TableCache) Errors() <-chan error { return t.errorChan } -func (t *TableCache) processUpdates(stopCh <-chan struct{}) { - for { - select { - case <-stopCh: - return - case update := <-t.updates: - if err := t.Populate(update); err != nil { - select { - case t.errorChan <- err: - // error sent to client - default: - // client not listening for errors - } - } - case update2 := <-t.updates2: - if err := t.Populate2(update2); err != nil { - select { - case t.errorChan <- err: - // error sent to client - default: - // client not listening for errors - } - } - } - } -} - // newRowCache creates a new row cache with the provided data // if the data is nil, and empty RowCache will be created -func newRowCache(name string, schema ovsdb.TableSchema, dataType reflect.Type) *RowCache { +func newRowCache(name string, dbModel *model.DatabaseModel, dataType reflect.Type) *RowCache { r := &RowCache{ name: name, - schema: schema, - indexes: newColumnToValue(schema.Indexes), + dbModel: dbModel, + indexes: newColumnToValue(dbModel.Schema().Table(name).Indexes), dataType: dataType, cache: make(map[string]model.Model), mutex: sync.RWMutex{}, @@ -846,7 +820,11 @@ func (e *eventProcessor) Run(stopCh <-chan struct{}) { // CreateModel creates a new Model instance based on the Row information func (t *TableCache) CreateModel(tableName string, row *ovsdb.Row, uuid string) (model.Model, error) { - table := t.mapper.Schema.Table(tableName) + if !t.dbModel.Valid() { + return nil, fmt.Errorf("database model not valid") + } + + table := t.dbModel.Schema().Table(tableName) if table == nil { return nil, fmt.Errorf("table %s not found", tableName) } @@ -854,18 +832,17 @@ func (t *TableCache) CreateModel(tableName string, row *ovsdb.Row, uuid string) if err != nil { return nil, err } - - err = t.mapper.GetRowData(tableName, row, model) + info, err := t.dbModel.NewModelInfo(model) + if err != nil { + return nil, err + } + err = t.dbModel.Mapper().GetRowData(row, info) if err != nil { return nil, err } if uuid != "" { - mapperInfo, err := mapper.NewInfo(table, model) - if err != nil { - return nil, err - } - if err := mapperInfo.SetField("_uuid", uuid); err != nil { + if err := info.SetField("_uuid", uuid); err != nil { return nil, err } } @@ -876,15 +853,18 @@ func (t *TableCache) CreateModel(tableName string, row *ovsdb.Row, uuid string) // ApplyModifications applies the contents of a RowUpdate2.Modify to a model // nolint: gocyclo func (t *TableCache) ApplyModifications(tableName string, base model.Model, update ovsdb.Row) error { - table := t.mapper.Schema.Table(tableName) + if !t.dbModel.Valid() { + return fmt.Errorf("database model not valid") + } + table := t.dbModel.Schema().Table(tableName) if table == nil { return fmt.Errorf("table %s not found", tableName) } - schema := t.schema.Table(tableName) + schema := t.dbModel.Schema().Table(tableName) if schema == nil { return fmt.Errorf("no schema for table %s", tableName) } - info, err := mapper.NewInfo(schema, base) + info, err := t.dbModel.NewModelInfo(base) if err != nil { return err } @@ -950,7 +930,7 @@ func (t *TableCache) ApplyModifications(tableName string, base model.Model, upda } // With a pointer type, an update value could be a set with 2 elements [old, new] if nv.Len() != 2 { - panic("expected a slice with 2 elements") + return fmt.Errorf("expected a slice with 2 elements for update: %+v", update) } // the new value is the value in the slice which isn't equal to the existing string for i := 0; i < nv.Len(); i++ { diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/client/api.go b/go-controller/vendor/github.com/ovn-org/libovsdb/client/api.go index 079fb01a2a5..db402594b7b 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/client/api.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/client/api.go @@ -8,7 +8,6 @@ import ( "github.com/go-logr/logr" "github.com/ovn-org/libovsdb/cache" - "github.com/ovn-org/libovsdb/mapper" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" ) @@ -187,13 +186,13 @@ func (a api) conditionFromModel(any bool, model model.Model, cond ...model.Condi } if len(cond) == 0 { - conditional, err = newEqualityConditional(a.cache.Mapper(), tableName, any, model) + conditional, err = newEqualityConditional(a.cache.DatabaseModel(), tableName, any, model) if err != nil { conditional = newErrorConditional(err) } } else { - conditional, err = newExplicitConditional(a.cache.Mapper(), tableName, any, model, cond...) + conditional, err = newExplicitConditional(a.cache.DatabaseModel(), tableName, any, model, cond...) if err != nil { conditional = newErrorConditional(err) } @@ -203,7 +202,7 @@ func (a api) conditionFromModel(any bool, model model.Model, cond ...model.Condi // Get is a generic Get function capable of returning (through a provided pointer) // a instance of any row in the cache. -// 'result' must be a pointer to an Model that exists in the DBModel +// 'result' must be a pointer to an Model that exists in the ClientDBModel // // The way the cache is searched depends on the fields already populated in 'result' // Any table index (including _uuid) will be used for comparison @@ -243,10 +242,8 @@ func (a api) Create(models ...model.Model) ([]ovsdb.Operation, error) { return nil, err } - table := a.cache.Mapper().Schema.Table(tableName) - // Read _uuid field, and use it as named-uuid - info, err := mapper.NewInfo(table, model) + info, err := a.cache.DatabaseModel().NewModelInfo(model) if err != nil { return nil, err } @@ -256,7 +253,7 @@ func (a api) Create(models ...model.Model) ([]ovsdb.Operation, error) { return nil, err } - row, err := a.cache.Mapper().NewRow(tableName, model) + row, err := a.cache.Mapper().NewRow(info) if err != nil { return nil, err } @@ -280,7 +277,7 @@ func (a api) Mutate(model model.Model, mutationObjs ...model.Mutation) ([]ovsdb. return nil, fmt.Errorf("at least one Mutation must be provided") } - tableName := a.cache.DBModel().FindTable(reflect.ValueOf(model).Type()) + tableName := a.cache.DatabaseModel().FindTable(reflect.ValueOf(model).Type()) if tableName == "" { return nil, fmt.Errorf("table not found for object") } @@ -294,7 +291,7 @@ func (a api) Mutate(model model.Model, mutationObjs ...model.Mutation) ([]ovsdb. return nil, err } - info, err := mapper.NewInfo(table, model) + info, err := a.cache.DatabaseModel().NewModelInfo(model) if err != nil { return nil, err } @@ -305,7 +302,7 @@ func (a api) Mutate(model model.Model, mutationObjs ...model.Mutation) ([]ovsdb. return nil, err } - mutation, err := a.cache.Mapper().NewMutation(tableName, model, col, mobj.Mutator, mobj.Value) + mutation, err := a.cache.Mapper().NewMutation(info, col, mobj.Mutator, mobj.Value) if err != nil { return nil, err } @@ -335,12 +332,12 @@ func (a api) Update(model model.Model, fields ...interface{}) ([]ovsdb.Operation return nil, err } tableSchema := a.cache.Mapper().Schema.Table(table) + info, err := a.cache.DatabaseModel().NewModelInfo(model) + if err != nil { + return nil, err + } if len(fields) > 0 { - info, err := mapper.NewInfo(tableSchema, model) - if err != nil { - return nil, err - } for _, f := range fields { colName, err := info.ColumnByPtr(f) if err != nil { @@ -357,7 +354,7 @@ func (a api) Update(model model.Model, fields ...interface{}) ([]ovsdb.Operation return nil, err } - row, err := a.cache.Mapper().NewRow(table, model, fields...) + row, err := a.cache.Mapper().NewRow(info, fields...) if err != nil { return nil, err } @@ -414,7 +411,7 @@ func (a api) getTableFromModel(m interface{}) (string, error) { if _, ok := m.(model.Model); !ok { return "", &ErrWrongType{reflect.TypeOf(m), "Type does not implement Model interface"} } - table := a.cache.DBModel().FindTable(reflect.TypeOf(m)) + table := a.cache.DatabaseModel().FindTable(reflect.TypeOf(m)) if table == "" { return "", &ErrWrongType{reflect.TypeOf(m), "Model not found in Database Model"} } @@ -439,7 +436,7 @@ func (a api) getTableFromFunc(predicate interface{}) (string, error) { fmt.Sprintf("Type %s does not implement Model interface", modelType.String())} } - table := a.cache.DBModel().FindTable(modelType) + table := a.cache.DatabaseModel().FindTable(modelType) if table == "" { return "", &ErrWrongType{predType, fmt.Sprintf("Model %s not found in Database Model", modelType.String())} diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/client/api_test_model.go b/go-controller/vendor/github.com/ovn-org/libovsdb/client/api_test_model.go index 776eb60bda7..6fafd15213a 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/client/api_test_model.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/client/api_test_model.go @@ -157,9 +157,11 @@ func apiTestCache(t *testing.T, data map[string]map[string]model.Model) *cache.T var schema ovsdb.DatabaseSchema err := json.Unmarshal(apiTestSchema, &schema) assert.Nil(t, err) - db, err := model.NewDBModel("OVN_NorthBound", map[string]model.Model{"Logical_Switch": &testLogicalSwitch{}, "Logical_Switch_Port": &testLogicalSwitchPort{}}) + db, err := model.NewClientDBModel("OVN_Northbound", map[string]model.Model{"Logical_Switch": &testLogicalSwitch{}, "Logical_Switch_Port": &testLogicalSwitchPort{}}) assert.Nil(t, err) - cache, err := cache.NewTableCache(&schema, db, data, nil) + dbModel, errs := model.NewDatabaseModel(&schema, db) + assert.Empty(t, errs) + cache, err := cache.NewTableCache(dbModel, data, nil) assert.Nil(t, err) return cache } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/client/client.go b/go-controller/vendor/github.com/ovn-org/libovsdb/client/client.go index c323582fd40..cf5f8b29340 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/client/client.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/client/client.go @@ -16,7 +16,6 @@ import ( "github.com/cenkalti/rpc2" "github.com/cenkalti/rpc2/jsonrpc" "github.com/ovn-org/libovsdb/cache" - "github.com/ovn-org/libovsdb/mapper" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" "github.com/ovn-org/libovsdb/ovsdb/serverdb" @@ -64,6 +63,12 @@ type Client interface { API } +type bufferedUpdate struct { + updates *ovsdb.TableUpdates + updates2 *ovsdb.TableUpdates2 + lastTxnID string +} + // ovsdbClient is an OVSDB client type ovsdbClient struct { options *options @@ -85,8 +90,7 @@ type ovsdbClient struct { // database is everything needed to map between go types and an ovsdb Database type database struct { - model *model.DBModel - schema *ovsdb.DatabaseSchema + model *model.DatabaseModel schemaMutex sync.RWMutex cache *cache.TableCache cacheMutex sync.RWMutex @@ -96,24 +100,30 @@ type database struct { // any ongoing monitors, so we can re-create them if we disconnect monitors map[string]*Monitor monitorsMutex sync.Mutex + + // tracks any outstanding updates while waiting for a monitor response + deferUpdates bool + deferredUpdates []*bufferedUpdate } // NewOVSDBClient creates a new OVSDB Client with the provided // database model. The client can be configured using one or more Option(s), // like WithTLSConfig. If no WithEndpoint option is supplied, the default of // unix:/var/run/openvswitch/ovsdb.sock is used -func NewOVSDBClient(databaseModel *model.DBModel, opts ...Option) (Client, error) { - return newOVSDBClient(databaseModel, opts...) +func NewOVSDBClient(clientDBModel *model.ClientDBModel, opts ...Option) (Client, error) { + return newOVSDBClient(clientDBModel, opts...) } // newOVSDBClient creates a new ovsdbClient -func newOVSDBClient(databaseModel *model.DBModel, opts ...Option) (*ovsdbClient, error) { +func newOVSDBClient(clientDBModel *model.ClientDBModel, opts ...Option) (*ovsdbClient, error) { ovs := &ovsdbClient{ - primaryDBName: databaseModel.Name(), + primaryDBName: clientDBModel.Name(), databases: map[string]*database{ - databaseModel.Name(): { - model: databaseModel, - monitors: make(map[string]*Monitor), + clientDBModel.Name(): { + model: model.NewPartialDatabaseModel(clientDBModel), + monitors: make(map[string]*Monitor), + deferUpdates: true, + deferredUpdates: make([]*bufferedUpdate, 0), }, }, disconnect: make(chan struct{}), @@ -131,11 +141,11 @@ func newOVSDBClient(databaseModel *model.DBModel, opts ...Option) (*ovsdbClient, return nil, fmt.Errorf("could not initialize model _Server: %w", err) } ovs.databases[serverDB] = &database{ - model: sm, + model: model.NewPartialDatabaseModel(sm), monitors: make(map[string]*Monitor), } } - ovs.metrics.init(databaseModel.Name()) + ovs.metrics.init(clientDBModel.Name()) return ovs, nil } @@ -145,6 +155,10 @@ func newOVSDBClient(databaseModel *model.DBModel, opts ...Option) (*ovsdbClient, // The connection can be configured using one or more Option(s), like WithTLSConfig // If no WithEndpoint option is supplied, the default of unix:/var/run/openvswitch/ovsdb.sock is used func (o *ovsdbClient) Connect(ctx context.Context) error { + // add the "model" value to the structured logger + // to make it easier to tell between different DBs (e.g. ovn nbdb vs. sbdb) + l := o.options.logger.WithValues("model", o.primaryDB().model.Client().Name()) + o.options.logger = &l o.registerMetrics() if err := o.connect(ctx, false); err != nil { @@ -283,7 +297,9 @@ func (o *ovsdbClient) tryEndpoint(ctx context.Context, u *url.URL) error { return err } - errors := db.model.Validate(schema) + db.schemaMutex.Lock() + errors := db.model.SetSchema(schema) + db.schemaMutex.Unlock() if len(errors) > 0 { var combined []string for _, err := range errors { @@ -296,13 +312,9 @@ func (o *ovsdbClient) tryEndpoint(ctx context.Context, u *url.URL) error { return err } - db.schemaMutex.Lock() - db.schema = schema - db.schemaMutex.Unlock() - db.cacheMutex.Lock() if db.cache == nil { - db.cache, err = cache.NewTableCache(schema, db.model, nil, o.options.logger) + db.cache, err = cache.NewTableCache(db.model, nil, o.options.logger) if err != nil { db.cacheMutex.Unlock() o.rpcClient.Close() @@ -311,7 +323,7 @@ func (o *ovsdbClient) tryEndpoint(ctx context.Context, u *url.URL) error { } db.api = newAPI(db.cache, o.options.logger) } else { - db.cache.Purge(db.schema) + db.cache.Purge(db.model) } db.cacheMutex.Unlock() } @@ -421,7 +433,7 @@ func (o *ovsdbClient) Schema() *ovsdb.DatabaseSchema { db := o.primaryDB() db.schemaMutex.RLock() defer db.schemaMutex.RUnlock() - return db.schema + return db.model.Schema() } // Cache returns the TableCache that is populated from @@ -479,6 +491,7 @@ func (o *ovsdbClient) echo(args []interface{}, reply *[]interface{}) error { // - table-updates: map of table name to table-update. Table-update is a map of uuid to (old, new) row paris func (o *ovsdbClient) update(params []json.RawMessage, reply *[]interface{}) error { cookie := MonitorCookie{} + *reply = []interface{}{} if len(params) > 2 { return fmt.Errorf("update requires exactly 2 args") } @@ -499,17 +512,27 @@ func (o *ovsdbClient) update(params []json.RawMessage, reply *[]interface{}) err for tableName := range updates { o.metrics.numTableUpdates.WithLabelValues(cookie.DatabaseName, tableName).Inc() } + + db.cacheMutex.Lock() + if db.deferUpdates { + db.deferredUpdates = append(db.deferredUpdates, &bufferedUpdate{&updates, nil, ""}) + db.cacheMutex.Unlock() + return nil + } + db.cacheMutex.Unlock() + // Update the local DB cache with the tableUpdates db.cacheMutex.RLock() - db.cache.Update(cookie.ID, updates) + err = db.cache.Update(cookie.ID, updates) db.cacheMutex.RUnlock() - *reply = []interface{}{} - return nil + + return err } // update2 handling from ovsdb-server.7 func (o *ovsdbClient) update2(params []json.RawMessage, reply *[]interface{}) error { cookie := MonitorCookie{} + *reply = []interface{}{} if len(params) > 2 { return fmt.Errorf("update2 requires exactly 2 args") } @@ -526,17 +549,27 @@ func (o *ovsdbClient) update2(params []json.RawMessage, reply *[]interface{}) er if db == nil { return fmt.Errorf("update: invalid database name: %s unknown", cookie.DatabaseName) } + + db.cacheMutex.Lock() + if db.deferUpdates { + db.deferredUpdates = append(db.deferredUpdates, &bufferedUpdate{nil, &updates, ""}) + db.cacheMutex.Unlock() + return nil + } + db.cacheMutex.Unlock() + // Update the local DB cache with the tableUpdates db.cacheMutex.RLock() - db.cache.Update2(cookie, updates) + err = db.cache.Update2(cookie, updates) db.cacheMutex.RUnlock() - *reply = []interface{}{} - return nil + + return err } // update3 handling from ovsdb-server.7 func (o *ovsdbClient) update3(params []json.RawMessage, reply *[]interface{}) error { cookie := MonitorCookie{} + *reply = []interface{}{} if len(params) > 3 { return fmt.Errorf("update requires exactly 3 args") } @@ -559,17 +592,28 @@ func (o *ovsdbClient) update3(params []json.RawMessage, reply *[]interface{}) er if db == nil { return fmt.Errorf("update: invalid database name: %s unknown", cookie.DatabaseName) } - db.monitorsMutex.Lock() - mon := db.monitors[cookie.ID] - mon.LastTransactionID = lastTransactionID - db.monitorsMutex.Unlock() + + db.cacheMutex.Lock() + if db.deferUpdates { + db.deferredUpdates = append(db.deferredUpdates, &bufferedUpdate{nil, &updates, lastTransactionID}) + db.cacheMutex.Unlock() + return nil + } + db.cacheMutex.Unlock() // Update the local DB cache with the tableUpdates db.cacheMutex.RLock() - db.cache.Update2(cookie, updates) + err = db.cache.Update2(cookie, updates) db.cacheMutex.RUnlock() - *reply = []interface{}{} - return nil + + if err == nil { + db.monitorsMutex.Lock() + mon := db.monitors[cookie.ID] + mon.LastTransactionID = lastTransactionID + db.monitorsMutex.Unlock() + } + + return err } // getSchema returns the schema in use for the provided database name @@ -615,7 +659,7 @@ func (o *ovsdbClient) transact(ctx context.Context, dbName string, operation ... var reply []ovsdb.OperationResult db := o.databases[dbName] db.schemaMutex.RLock() - schema := o.databases[dbName].schema + schema := o.databases[dbName].model.Schema() db.schemaMutex.RUnlock() if schema == nil { return nil, fmt.Errorf("cannot transact to database %s: schema unknown", dbName) @@ -684,6 +728,7 @@ func (o *ovsdbClient) Monitor(ctx context.Context, monitor *Monitor) (MonitorCoo return cookie, o.monitor(ctx, cookie, false, monitor) } +//gocyclo:ignore func (o *ovsdbClient) monitor(ctx context.Context, cookie MonitorCookie, reconnecting bool, monitor *Monitor) error { if len(monitor.Tables) == 0 { return fmt.Errorf("at least one table should be monitored") @@ -698,16 +743,24 @@ func (o *ovsdbClient) monitor(ctx context.Context, cookie MonitorCookie, reconne dbName := cookie.DatabaseName db := o.databases[dbName] db.schemaMutex.RLock() - mapper := mapper.NewMapper(db.schema) + mmapper := db.model.Mapper() db.schemaMutex.RUnlock() typeMap := o.databases[dbName].model.Types() requests := make(map[string]ovsdb.MonitorRequest) for _, o := range monitor.Tables { - m, ok := typeMap[o.Table] + _, ok := typeMap[o.Table] if !ok { return fmt.Errorf("type for table %s does not exist in model", o.Table) } - request, err := mapper.NewMonitorRequest(o.Table, m, o.Fields) + model, err := db.model.NewModel(o.Table) + if err != nil { + return err + } + info, err := db.model.NewModelInfo(model) + if err != nil { + return err + } + request, err := mmapper.NewMonitorRequest(info, o.Fields) if err != nil { return err } @@ -784,17 +837,41 @@ func (o *ovsdbClient) monitor(ctx context.Context, cookie MonitorCookie, reconne o.metrics.numMonitors.Inc() } + db.cacheMutex.Lock() + defer db.cacheMutex.Unlock() if monitor.Method == ovsdb.MonitorRPC { u := tableUpdates.(ovsdb.TableUpdates) - db.cacheMutex.Lock() - defer db.cacheMutex.Unlock() err = db.cache.Populate(u) } else { u := tableUpdates.(ovsdb.TableUpdates2) - db.cacheMutex.Lock() - defer db.cacheMutex.Unlock() err = db.cache.Populate2(u) } + + if err != nil { + return err + } + + // populate any deferred updates + db.deferUpdates = false + for _, update := range db.deferredUpdates { + if update.updates != nil { + if err = db.cache.Populate(*update.updates); err != nil { + return err + } + } + + if update.updates2 != nil { + if err = db.cache.Populate2(*update.updates2); err != nil { + return err + } + } + if len(update.lastTxnID) > 0 { + db.monitorsMutex.Lock() + db.monitors[cookie.ID].LastTransactionID = update.lastTxnID + db.monitorsMutex.Unlock() + } + } + return err } @@ -871,6 +948,15 @@ func (o *ovsdbClient) handleCacheErrors(stopCh <-chan struct{}, errorChan <-chan // trigger a reconnect, which will purge the cache // hopefully a rebuild will fix any inconsistency o.options.logger.V(3).Error(err, "triggering reconnect to rebuild cache") + // for rebuilding cache with mon_cond_since (not yet fully supported in libovsdb) we + // need to reset the last txn ID + for _, db := range o.databases { + db.monitorsMutex.Lock() + for _, mon := range db.monitors { + mon.LastTransactionID = emptyUUID + } + db.monitorsMutex.Unlock() + } o.Disconnect() } else { o.options.logger.V(3).Error(err, "error updating cache") @@ -881,7 +967,7 @@ func (o *ovsdbClient) handleCacheErrors(stopCh <-chan struct{}, errorChan <-chan func (o *ovsdbClient) handleDisconnectNotification() { <-o.rpcClient.DisconnectNotify() - // close the stopCh, which will stop the cache event processor and update processing + // close the stopCh, which will stop the cache event processor close(o.stopCh) o.metrics.numDisconnects.Inc() o.rpcMutex.Lock() @@ -916,10 +1002,12 @@ func (o *ovsdbClient) handleDisconnectNotification() { db.cacheMutex.Lock() defer db.cacheMutex.Unlock() db.cache = nil + // need to defer updates if/when we reconnect + db.deferUpdates = true db.schemaMutex.Lock() defer db.schemaMutex.Unlock() - db.schema = nil + db.model.ClearSchema() db.monitorsMutex.Lock() defer db.monitorsMutex.Unlock() diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/client/condition.go b/go-controller/vendor/github.com/ovn-org/libovsdb/client/condition.go index 99b32df28fd..1a745af7a61 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/client/condition.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/client/condition.go @@ -26,14 +26,18 @@ type Conditional interface { // The conditions are based on the equality of the first available index. // The priority of indexes is: uuid, {schema index} type equalityConditional struct { - mapper *mapper.Mapper + dbModel *model.DatabaseModel tableName string - model model.Model + info *mapper.Info singleOp bool } func (c *equalityConditional) Matches(m model.Model) (bool, error) { - return c.mapper.EqualFields(c.tableName, c.model, m) + info, err := c.dbModel.NewModelInfo(m) + if err != nil { + return false, err + } + return c.dbModel.Mapper().EqualFields(c.info, info) } func (c *equalityConditional) Table() string { @@ -44,7 +48,7 @@ func (c *equalityConditional) Table() string { func (c *equalityConditional) Generate() ([][]ovsdb.Condition, error) { var result [][]ovsdb.Condition - conds, err := c.mapper.NewEqualityCondition(c.tableName, c.model) + conds, err := c.dbModel.Mapper().NewEqualityCondition(c.info) if err != nil { return nil, err } @@ -59,20 +63,24 @@ func (c *equalityConditional) Generate() ([][]ovsdb.Condition, error) { } // NewEqualityCondition creates a new equalityConditional -func newEqualityConditional(mapper *mapper.Mapper, table string, all bool, model model.Model, fields ...interface{}) (Conditional, error) { +func newEqualityConditional(dbModel *model.DatabaseModel, table string, all bool, model model.Model, fields ...interface{}) (Conditional, error) { + info, err := dbModel.NewModelInfo(model) + if err != nil { + return nil, err + } return &equalityConditional{ - mapper: mapper, + dbModel: dbModel, tableName: table, - model: model, + info: info, singleOp: all, }, nil } // explicitConditional generates conditions based on the provided Condition list type explicitConditional struct { - mapper *mapper.Mapper + dbModel *model.DatabaseModel tableName string - model model.Model + info *mapper.Info conditions []model.Condition singleOp bool } @@ -91,7 +99,7 @@ func (c *explicitConditional) Generate() ([][]ovsdb.Condition, error) { var conds []ovsdb.Condition for _, cond := range c.conditions { - ovsdbCond, err := c.mapper.NewCondition(c.tableName, c.model, cond.Field, cond.Function, cond.Value) + ovsdbCond, err := c.dbModel.Mapper().NewCondition(c.info, cond.Field, cond.Function, cond.Value) if err != nil { return nil, err } @@ -109,11 +117,15 @@ func (c *explicitConditional) Generate() ([][]ovsdb.Condition, error) { } // newIndexCondition creates a new equalityConditional -func newExplicitConditional(mapper *mapper.Mapper, table string, all bool, model model.Model, cond ...model.Condition) (Conditional, error) { +func newExplicitConditional(dbModel *model.DatabaseModel, table string, all bool, model model.Model, cond ...model.Condition) (Conditional, error) { + info, err := dbModel.NewModelInfo(model) + if err != nil { + return nil, err + } return &explicitConditional{ - mapper: mapper, + dbModel: dbModel, tableName: table, - model: model, + info: info, conditions: cond, singleOp: all, }, nil @@ -152,7 +164,11 @@ func (c *predicateConditional) Generate() ([][]ovsdb.Condition, error) { return nil, err } if match { - elemCond, err := c.cache.Mapper().NewEqualityCondition(c.tableName, row) + info, err := c.cache.DatabaseModel().NewModelInfo(row) + if err != nil { + return nil, err + } + elemCond, err := c.cache.Mapper().NewEqualityCondition(info) if err != nil { return nil, err } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/client/doc.go b/go-controller/vendor/github.com/ovn-org/libovsdb/client/doc.go index b5f39f308e7..7f7dae51d61 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/client/doc.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/client/doc.go @@ -11,21 +11,21 @@ which column in the database. We refer to pointers to this structs as Models. Ex Config map[string]string `ovsdb:"other_config"` } -Based on these Models a Database Model (see DBModel type) is built to represent +Based on these Models a Database Model (see ClientDBModel type) is built to represent the entire OVSDB: - dbModel, _ := client.NewDBModel("OVN_Northbound", + clientDBModel, _ := client.NewClientDBModel("OVN_Northbound", map[string]client.Model{ "Logical_Switch": &MyLogicalSwitch{}, }) -The DBModel represents the entire Database (or the part of it we're interested in). +The ClientDBModel represents the entire Database (or the part of it we're interested in). Using it, the libovsdb.client package is able to properly encode and decode OVSDB messages and store them in Model instances. A client instance is created by simply specifying the connection information and the database model: - ovs, _ := client.Connect(context.Background(), dbModel) + ovs, _ := client.Connect(context.Background(), clientDBModel) Main API diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/client/monitor.go b/go-controller/vendor/github.com/ovn-org/libovsdb/client/monitor.go index d2fd7310f66..fe95d15f81e 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/client/monitor.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/client/monitor.go @@ -72,7 +72,7 @@ func WithTable(m model.Model, fields ...interface{}) MonitorOption { return func(o *ovsdbClient, monitor *Monitor) error { tableName := o.primaryDB().model.FindTable(reflect.TypeOf(m)) if tableName == "" { - return fmt.Errorf("object of type %s is not part of the DBModel", reflect.TypeOf(m)) + return fmt.Errorf("object of type %s is not part of the ClientDBModel", reflect.TypeOf(m)) } tableMonitor := TableMonitor{ Table: tableName, @@ -87,7 +87,7 @@ func WithConditionalTable(m model.Model, condition model.Condition, fields ...in return func(o *ovsdbClient, monitor *Monitor) error { tableName := o.primaryDB().model.FindTable(reflect.TypeOf(m)) if tableName == "" { - return fmt.Errorf("object of type %s is not part of the DBModel", reflect.TypeOf(m)) + return fmt.Errorf("object of type %s is not part of the ClientDBModel", reflect.TypeOf(m)) } tableMonitor := TableMonitor{ Table: tableName, diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/info.go b/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/info.go index 1ad981b6c18..7f620c9e9d0 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/info.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/info.go @@ -7,37 +7,42 @@ import ( "github.com/ovn-org/libovsdb/ovsdb" ) -// Info is a struct that handles the type map of an object -// The object must have exported tagged fields with the 'ovs' +// Info is a struct that wraps an object with its metadata type Info struct { // FieldName indexed by column - fields map[string]string - obj interface{} - table *ovsdb.TableSchema + Obj interface{} + Metadata *Metadata +} + +// Metadata represents the information needed to know how to map OVSDB columns into an objetss fields +type Metadata struct { + Fields map[string]string // Map of ColumnName -> FieldName + TableSchema *ovsdb.TableSchema // TableSchema associated + TableName string // Table name } // FieldByColumn returns the field value that corresponds to a column func (i *Info) FieldByColumn(column string) (interface{}, error) { - fieldName, ok := i.fields[column] + fieldName, ok := i.Metadata.Fields[column] if !ok { return nil, fmt.Errorf("FieldByColumn: column %s not found in orm info", column) } - return reflect.ValueOf(i.obj).Elem().FieldByName(fieldName).Interface(), nil + return reflect.ValueOf(i.Obj).Elem().FieldByName(fieldName).Interface(), nil } // FieldByColumn returns the field value that corresponds to a column func (i *Info) hasColumn(column string) bool { - _, ok := i.fields[column] + _, ok := i.Metadata.Fields[column] return ok } // SetField sets the field in the column to the specified value func (i *Info) SetField(column string, value interface{}) error { - fieldName, ok := i.fields[column] + fieldName, ok := i.Metadata.Fields[column] if !ok { return fmt.Errorf("SetField: column %s not found in orm info", column) } - fieldValue := reflect.ValueOf(i.obj).Elem().FieldByName(fieldName) + fieldValue := reflect.ValueOf(i.Obj).Elem().FieldByName(fieldName) if !fieldValue.Type().AssignableTo(reflect.TypeOf(value)) { return fmt.Errorf("column %s: native value %v (%s) is not assignable to field %s (%s)", @@ -53,12 +58,12 @@ func (i *Info) ColumnByPtr(fieldPtr interface{}) (string, error) { if fieldPtrVal.Kind() != reflect.Ptr { return "", ovsdb.NewErrWrongType("ColumnByPointer", "pointer to a field in the struct", fieldPtr) } - offset := fieldPtrVal.Pointer() - reflect.ValueOf(i.obj).Pointer() - objType := reflect.TypeOf(i.obj).Elem() + offset := fieldPtrVal.Pointer() - reflect.ValueOf(i.Obj).Pointer() + objType := reflect.TypeOf(i.Obj).Elem() for j := 0; j < objType.NumField(); j++ { if objType.Field(j).Offset == offset { column := objType.Field(j).Tag.Get("ovsdb") - if _, ok := i.fields[column]; !ok { + if _, ok := i.Metadata.Fields[column]; !ok { return "", fmt.Errorf("field does not have orm column information") } return column, nil @@ -74,7 +79,7 @@ func (i *Info) getValidIndexes() ([][]string, error) { var possibleIndexes [][]string possibleIndexes = append(possibleIndexes, []string{"_uuid"}) - possibleIndexes = append(possibleIndexes, i.table.Indexes...) + possibleIndexes = append(possibleIndexes, i.Metadata.TableSchema.Indexes...) // Iterate through indexes and validate them OUTER: @@ -83,7 +88,7 @@ OUTER: if !i.hasColumn(col) { continue OUTER } - columnSchema := i.table.Column(col) + columnSchema := i.Metadata.TableSchema.Column(col) if columnSchema == nil { continue OUTER } @@ -101,7 +106,7 @@ OUTER: } // NewInfo creates a MapperInfo structure around an object based on a given table schema -func NewInfo(table *ovsdb.TableSchema, obj interface{}) (*Info, error) { +func NewInfo(tableName string, table *ovsdb.TableSchema, obj interface{}) (*Info, error) { objPtrVal := reflect.ValueOf(obj) if objPtrVal.Type().Kind() != reflect.Ptr { return nil, ovsdb.NewErrWrongType("NewMapperInfo", "pointer to a struct", obj) @@ -146,8 +151,11 @@ func NewInfo(table *ovsdb.TableSchema, obj interface{}) (*Info, error) { } return &Info{ - fields: fields, - obj: obj, - table: table, + Obj: obj, + Metadata: &Metadata{ + Fields: fields, + TableSchema: table, + TableName: tableName, + }, }, nil } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/mapper.go b/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/mapper.go index 2a66680cde9..f0677eab4fd 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/mapper.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/mapper/mapper.go @@ -36,21 +36,6 @@ func (e *ErrMapper) Error() string { e.objType, e.field, e.fieldType, e.fieldTag, e.reason) } -// ErrNoTable describes a error in the provided table information -type ErrNoTable struct { - table string -} - -func (e *ErrNoTable) Error() string { - return fmt.Sprintf("Table not found: %s", e.table) -} - -func newErrNoTable(table string) error { - return &ErrNoTable{ - table: table, - } -} - // NewMapper returns a new mapper func NewMapper(schema *ovsdb.DatabaseSchema) *Mapper { return &Mapper{ @@ -60,29 +45,19 @@ func NewMapper(schema *ovsdb.DatabaseSchema) *Mapper { // GetRowData transforms a Row to a struct based on its tags // The result object must be given as pointer to an object with the right tags -func (m Mapper) GetRowData(tableName string, row *ovsdb.Row, result interface{}) error { +func (m Mapper) GetRowData(row *ovsdb.Row, result *Info) error { if row == nil { return nil } - return m.getData(tableName, *row, result) + return m.getData(*row, result) } // getData transforms a map[string]interface{} containing OvS types (e.g: a ResultRow // has this format) to orm struct // The result object must be given as pointer to an object with the right tags -func (m Mapper) getData(tableName string, ovsData ovsdb.Row, result interface{}) error { - table := m.Schema.Table(tableName) - if table == nil { - return newErrNoTable(tableName) - } - - mapperInfo, err := NewInfo(table, result) - if err != nil { - return err - } - - for name, column := range table.Columns { - if !mapperInfo.hasColumn(name) { +func (m Mapper) getData(ovsData ovsdb.Row, result *Info) error { + for name, column := range result.Metadata.TableSchema.Columns { + if !result.hasColumn(name) { // If provided struct does not have a field to hold this value, skip it continue } @@ -96,10 +71,10 @@ func (m Mapper) getData(tableName string, ovsData ovsdb.Row, result interface{}) nativeElem, err := ovsdb.OvsToNative(column, ovsElem) if err != nil { return fmt.Errorf("table %s, column %s: failed to extract native element: %s", - tableName, name, err.Error()) + result.Metadata.TableName, name, err.Error()) } - if err := mapperInfo.SetField(name, nativeElem); err != nil { + if err := result.SetField(name, nativeElem); err != nil { return err } } @@ -109,24 +84,15 @@ func (m Mapper) getData(tableName string, ovsData ovsdb.Row, result interface{}) // NewRow transforms an orm struct to a map[string] interface{} that can be used as libovsdb.Row // By default, default or null values are skipped. This behavior can be modified by specifying // a list of fields (pointers to fields in the struct) to be added to the row -func (m Mapper) NewRow(tableName string, data interface{}, fields ...interface{}) (ovsdb.Row, error) { - table := m.Schema.Table(tableName) - if table == nil { - return nil, newErrNoTable(tableName) - } - mapperInfo, err := NewInfo(table, data) - if err != nil { - return nil, err - } - +func (m Mapper) NewRow(data *Info, fields ...interface{}) (ovsdb.Row, error) { columns := make(map[string]*ovsdb.ColumnSchema) - for k, v := range table.Columns { + for k, v := range data.Metadata.TableSchema.Columns { columns[k] = v } columns["_uuid"] = &ovsdb.UUIDColumn ovsRow := make(map[string]interface{}, len(columns)) for name, column := range columns { - nativeElem, err := mapperInfo.FieldByColumn(name) + nativeElem, err := data.FieldByColumn(name) if err != nil { // If provided struct does not have a field to hold this value, skip it continue @@ -136,7 +102,7 @@ func (m Mapper) NewRow(tableName string, data interface{}, fields ...interface{} if len(fields) > 0 { found := false for _, f := range fields { - col, err := mapperInfo.ColumnByPtr(f) + col, err := data.ColumnByPtr(f) if err != nil { return nil, err } @@ -154,7 +120,7 @@ func (m Mapper) NewRow(tableName string, data interface{}, fields ...interface{} } ovsElem, err := ovsdb.NativeToOvs(column, nativeElem) if err != nil { - return nil, fmt.Errorf("table %s, column %s: failed to generate ovs element. %s", tableName, name, err.Error()) + return nil, fmt.Errorf("table %s, column %s: failed to generate ovs element. %s", data.Metadata.TableName, name, err.Error()) } ovsRow[name] = ovsElem } @@ -169,25 +135,15 @@ func (m Mapper) NewRow(tableName string, data interface{}, fields ...interface{} // object has valid data. The order in which they are traversed matches the order defined // in the schema. // By `valid data` we mean non-default data. -func (m Mapper) NewEqualityCondition(tableName string, data interface{}, fields ...interface{}) ([]ovsdb.Condition, error) { +func (m Mapper) NewEqualityCondition(data *Info, fields ...interface{}) ([]ovsdb.Condition, error) { var conditions []ovsdb.Condition var condIndex [][]string - table := m.Schema.Table(tableName) - if table == nil { - return nil, newErrNoTable(tableName) - } - - mapperInfo, err := NewInfo(table, data) - if err != nil { - return nil, err - } - // If index is provided, use it. If not, obtain the valid indexes from the mapper info if len(fields) > 0 { providedIndex := []string{} for i := range fields { - if col, err := mapperInfo.ColumnByPtr(fields[i]); err == nil { + if col, err := data.ColumnByPtr(fields[i]); err == nil { providedIndex = append(providedIndex, col) } else { return nil, err @@ -196,7 +152,7 @@ func (m Mapper) NewEqualityCondition(tableName string, data interface{}, fields condIndex = append(condIndex, providedIndex) } else { var err error - condIndex, err = mapperInfo.getValidIndexes() + condIndex, err = data.getValidIndexes() if err != nil { return nil, err } @@ -208,12 +164,12 @@ func (m Mapper) NewEqualityCondition(tableName string, data interface{}, fields // Pick the first valid index for _, col := range condIndex[0] { - field, err := mapperInfo.FieldByColumn(col) + field, err := data.FieldByColumn(col) if err != nil { return nil, err } - column := table.Column(col) + column := data.Metadata.TableSchema.Column(col) if column == nil { return nil, fmt.Errorf("column %s not found", col) } @@ -229,47 +185,27 @@ func (m Mapper) NewEqualityCondition(tableName string, data interface{}, fields // EqualFields compares two mapped objects. // The indexes to use for comparison are, the _uuid, the table indexes and the columns that correspond // to the mapped fields pointed to by 'fields'. They must be pointers to fields on the first mapped element (i.e: one) -func (m Mapper) EqualFields(tableName string, one, other interface{}, fields ...interface{}) (bool, error) { +func (m Mapper) EqualFields(one, other *Info, fields ...interface{}) (bool, error) { indexes := []string{} - - table := m.Schema.Table(tableName) - if table == nil { - return false, newErrNoTable(tableName) - } - - info, err := NewInfo(table, one) - if err != nil { - return false, err - } for _, f := range fields { - col, err := info.ColumnByPtr(f) + col, err := one.ColumnByPtr(f) if err != nil { return false, err } indexes = append(indexes, col) } - return m.equalIndexes(table, one, other, indexes...) + return m.equalIndexes(one, other, indexes...) } // NewCondition returns a ovsdb.Condition based on the model -func (m Mapper) NewCondition(tableName string, data interface{}, field interface{}, function ovsdb.ConditionFunction, value interface{}) (*ovsdb.Condition, error) { - table := m.Schema.Table(tableName) - if table == nil { - return nil, newErrNoTable(tableName) - } - - info, err := NewInfo(table, data) - if err != nil { - return nil, err - } - - column, err := info.ColumnByPtr(field) +func (m Mapper) NewCondition(data *Info, field interface{}, function ovsdb.ConditionFunction, value interface{}) (*ovsdb.Condition, error) { + column, err := data.ColumnByPtr(field) if err != nil { return nil, err } // Check that the condition is valid - columnSchema := table.Column(column) + columnSchema := data.Metadata.TableSchema.Column(column) if columnSchema == nil { return nil, fmt.Errorf("column %s not found", column) } @@ -290,23 +226,13 @@ func (m Mapper) NewCondition(tableName string, data interface{}, field interface // NewMutation creates a RFC7047 mutation object based on an ORM object and the mutation fields (in native format) // It takes care of field validation against the column type -func (m Mapper) NewMutation(tableName string, data interface{}, column string, mutator ovsdb.Mutator, value interface{}) (*ovsdb.Mutation, error) { - table := m.Schema.Table(tableName) - if table == nil { - return nil, newErrNoTable(tableName) - } - - mapperInfo, err := NewInfo(table, data) - if err != nil { - return nil, err - } - +func (m Mapper) NewMutation(data *Info, column string, mutator ovsdb.Mutator, value interface{}) (*ovsdb.Mutation, error) { // Check the column exists in the object - if !mapperInfo.hasColumn(column) { + if !data.hasColumn(column) { return nil, fmt.Errorf("mutation contains column %s that does not exist in object %v", column, data) } // Check that the mutation is valid - columnSchema := table.Column(column) + columnSchema := data.Metadata.TableSchema.Column(column) if columnSchema == nil { return nil, fmt.Errorf("column %s not found", column) } @@ -315,6 +241,7 @@ func (m Mapper) NewMutation(tableName string, data interface{}, column string, m } var ovsValue interface{} + var err error // Usually a mutation value is of the same type of the value being mutated // except for delete mutation of maps where it can also be a list of same type of // keys (rfc7047 5.1). Handle this special case here. @@ -341,24 +268,15 @@ func (m Mapper) NewMutation(tableName string, data interface{}, column string, m // For any of the indexes defined in the Table Schema, the values all of its columns are simultaneously equal // (as per RFC7047) // The values of all of the optional indexes passed as variadic parameter to this function are equal. -func (m Mapper) equalIndexes(table *ovsdb.TableSchema, one, other interface{}, indexes ...string) (bool, error) { +func (m Mapper) equalIndexes(one, other *Info, indexes ...string) (bool, error) { match := false - oneMapperInfo, err := NewInfo(table, one) - if err != nil { - return false, err - } - otherMapperInfo, err := NewInfo(table, other) - if err != nil { - return false, err - } - - oneIndexes, err := oneMapperInfo.getValidIndexes() + oneIndexes, err := one.getValidIndexes() if err != nil { return false, err } - otherIndexes, err := otherMapperInfo.getValidIndexes() + otherIndexes, err := other.getValidIndexes() if err != nil { return false, err } @@ -371,14 +289,14 @@ func (m Mapper) equalIndexes(table *ovsdb.TableSchema, one, other interface{}, i if reflect.DeepEqual(ridx, lidx) { // All columns in an index must be simultaneously equal for _, col := range lidx { - if !oneMapperInfo.hasColumn(col) || !otherMapperInfo.hasColumn(col) { + if !one.hasColumn(col) || !other.hasColumn(col) { break } - lfield, err := oneMapperInfo.FieldByColumn(col) + lfield, err := one.FieldByColumn(col) if err != nil { return false, err } - rfield, err := otherMapperInfo.FieldByColumn(col) + rfield, err := other.FieldByColumn(col) if err != nil { return false, err } @@ -401,23 +319,18 @@ func (m Mapper) equalIndexes(table *ovsdb.TableSchema, one, other interface{}, i // NewMonitorRequest returns a monitor request for the provided tableName // If fields is provided, the request will be constrained to the provided columns // If no fields are provided, all columns will be used -func (m *Mapper) NewMonitorRequest(tableName string, data interface{}, fields []interface{}) (*ovsdb.MonitorRequest, error) { +func (m *Mapper) NewMonitorRequest(data *Info, fields []interface{}) (*ovsdb.MonitorRequest, error) { var columns []string - schema := m.Schema.Tables[tableName] - info, err := NewInfo(&schema, data) - if err != nil { - return nil, err - } if len(fields) > 0 { for _, f := range fields { - column, err := info.ColumnByPtr(f) + column, err := data.ColumnByPtr(f) if err != nil { return nil, err } columns = append(columns, column) } } else { - for c := range info.table.Columns { + for c := range data.Metadata.TableSchema.Columns { columns = append(columns, c) } } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/model/client.go b/go-controller/vendor/github.com/ovn-org/libovsdb/model/client.go new file mode 100644 index 00000000000..80ffc669a61 --- /dev/null +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/model/client.go @@ -0,0 +1,84 @@ +package model + +import ( + "fmt" + "reflect" + + "github.com/ovn-org/libovsdb/mapper" + "github.com/ovn-org/libovsdb/ovsdb" +) + +// ClientDBModel contains the client information needed to build a DatabaseModel +type ClientDBModel struct { + name string + types map[string]reflect.Type +} + +// NewModel returns a new instance of a model from a specific string +func (db ClientDBModel) newModel(table string) (Model, error) { + mtype, ok := db.types[table] + if !ok { + return nil, fmt.Errorf("table %s not found in database model", string(table)) + } + model := reflect.New(mtype.Elem()) + return model.Interface().(Model), nil +} + +// Name returns the database name +func (db ClientDBModel) Name() string { + return db.name +} + +// Validate validates the DatabaseModel against the input schema +// Returns all the errors detected +func (db ClientDBModel) validate(schema *ovsdb.DatabaseSchema) []error { + var errors []error + if db.name != schema.Name { + errors = append(errors, fmt.Errorf("database model name (%s) does not match schema (%s)", + db.name, schema.Name)) + } + + for tableName := range db.types { + tableSchema := schema.Table(tableName) + if tableSchema == nil { + errors = append(errors, fmt.Errorf("database model contains a model for table %s that does not exist in schema", tableName)) + continue + } + model, err := db.newModel(tableName) + if err != nil { + errors = append(errors, err) + continue + } + if _, err := mapper.NewInfo(tableName, tableSchema, model); err != nil { + errors = append(errors, err) + } + } + return errors +} + +// NewClientDBModel constructs a ClientDBModel based on a database name and dictionary of models indexed by table name +func NewClientDBModel(name string, models map[string]Model) (*ClientDBModel, error) { + types := make(map[string]reflect.Type, len(models)) + for table, model := range models { + modelType := reflect.TypeOf(model) + if modelType.Kind() != reflect.Ptr || modelType.Elem().Kind() != reflect.Struct { + return nil, fmt.Errorf("model is expected to be a pointer to struct") + } + hasUUID := false + for i := 0; i < modelType.Elem().NumField(); i++ { + if field := modelType.Elem().Field(i); field.Tag.Get("ovsdb") == "_uuid" && + field.Type.Kind() == reflect.String { + hasUUID = true + } + } + if !hasUUID { + return nil, fmt.Errorf("model is expected to have a string field called uuid") + } + + types[table] = reflect.TypeOf(model) + } + return &ClientDBModel{ + types: types, + name: name, + }, nil +} diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/model/database.go b/go-controller/vendor/github.com/ovn-org/libovsdb/model/database.go new file mode 100644 index 00000000000..6f7ee4fdb0f --- /dev/null +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/model/database.go @@ -0,0 +1,157 @@ +package model + +import ( + "fmt" + "reflect" + "sync" + + "github.com/ovn-org/libovsdb/mapper" + "github.com/ovn-org/libovsdb/ovsdb" +) + +// A DatabaseModel represents libovsdb's metadata about the database. +// It's the result of combining the client's ClientDBModel and the server's Schema +type DatabaseModel struct { + client *ClientDBModel + schema *ovsdb.DatabaseSchema + mapper *mapper.Mapper + mutex sync.RWMutex + metadata map[reflect.Type]*mapper.Metadata +} + +// NewDatabaseModel returns a new DatabaseModel +func NewDatabaseModel(schema *ovsdb.DatabaseSchema, client *ClientDBModel) (*DatabaseModel, []error) { + dbModel := NewPartialDatabaseModel(client) + errs := dbModel.SetSchema(schema) + if len(errs) > 0 { + return nil, errs + } + return dbModel, nil +} + +// NewPartialDatabaseModel returns a DatabaseModel what does not have a schema yet +func NewPartialDatabaseModel(client *ClientDBModel) *DatabaseModel { + return &DatabaseModel{ + client: client, + } +} + +// Valid returns whether the DatabaseModel is fully functional +func (db *DatabaseModel) Valid() bool { + db.mutex.RLock() + defer db.mutex.RUnlock() + return db.schema != nil +} + +// SetSchema adds the Schema to the DatabaseModel making it valid if it was not before +func (db *DatabaseModel) SetSchema(schema *ovsdb.DatabaseSchema) []error { + db.mutex.Lock() + defer db.mutex.Unlock() + errors := db.client.validate(schema) + if len(errors) > 0 { + return errors + } + db.schema = schema + db.mapper = mapper.NewMapper(schema) + errs := db.generateModelInfo() + if len(errs) > 0 { + db.schema = nil + db.mapper = nil + return errs + } + return []error{} +} + +// ClearSchema removes the Schema from the DatabaseModel making it not valid +func (db *DatabaseModel) ClearSchema() { + db.mutex.Lock() + defer db.mutex.Unlock() + db.schema = nil + db.mapper = nil +} + +// Client returns the DatabaseModel's client dbModel +func (db *DatabaseModel) Client() *ClientDBModel { + return db.client +} + +// Schema returns the DatabaseModel's schema +func (db *DatabaseModel) Schema() *ovsdb.DatabaseSchema { + db.mutex.RLock() + defer db.mutex.RUnlock() + return db.schema +} + +// Mapper returns the DatabaseModel's mapper +func (db *DatabaseModel) Mapper() *mapper.Mapper { + db.mutex.RLock() + defer db.mutex.RUnlock() + return db.mapper +} + +// NewModel returns a new instance of a model from a specific string +func (db *DatabaseModel) NewModel(table string) (Model, error) { + mtype, ok := db.client.types[table] + if !ok { + return nil, fmt.Errorf("table %s not found in database model", string(table)) + } + model := reflect.New(mtype.Elem()) + return model.Interface().(Model), nil +} + +// Types returns the DatabaseModel Types +// the DatabaseModel types is a map of reflect.Types indexed by string +// The reflect.Type is a pointer to a struct that contains 'ovs' tags +// as described above. Such pointer to struct also implements the Model interface +func (db *DatabaseModel) Types() map[string]reflect.Type { + return db.client.types +} + +// FindTable returns the string associated with a reflect.Type or "" +func (db *DatabaseModel) FindTable(mType reflect.Type) string { + for table, tType := range db.client.types { + if tType == mType { + return table + } + } + return "" +} + +// generateModelMetadata creates metadata objects from all models included in the +// database and caches them for future re-use +func (db *DatabaseModel) generateModelInfo() []error { + errors := []error{} + metadata := make(map[reflect.Type]*mapper.Metadata, len(db.client.types)) + for tableName, tType := range db.client.types { + tableSchema := db.schema.Table(tableName) + if tableSchema == nil { + errors = append(errors, fmt.Errorf("Database Model contains model for table %s which is not present in schema", tableName)) + continue + } + obj, err := db.NewModel(tableName) + if err != nil { + errors = append(errors, err) + continue + } + info, err := mapper.NewInfo(tableName, tableSchema, obj) + if err != nil { + errors = append(errors, err) + continue + } + metadata[tType] = info.Metadata + } + db.metadata = metadata + return errors +} + +// NewModelInfo returns a mapper.Info object based on a provided model +func (db *DatabaseModel) NewModelInfo(obj interface{}) (*mapper.Info, error) { + meta, ok := db.metadata[reflect.TypeOf(obj)] + if !ok { + return nil, ovsdb.NewErrWrongType("NewModelInfo", "type that is part of the DatabaseModel", obj) + } + return &mapper.Info{ + Obj: obj, + Metadata: meta, + }, nil +} diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/model/model.go b/go-controller/vendor/github.com/ovn-org/libovsdb/model/model.go index ef77fc9d3a0..453fd2ea050 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/model/model.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/model/model.go @@ -5,7 +5,6 @@ import ( "fmt" "reflect" - "github.com/ovn-org/libovsdb/mapper" "github.com/ovn-org/libovsdb/ovsdb" ) @@ -34,99 +33,6 @@ func Clone(a Model) Model { return b } -// DBModel is a Database model -type DBModel struct { - name string - types map[string]reflect.Type -} - -// NewModel returns a new instance of a model from a specific string -func (db DBModel) NewModel(table string) (Model, error) { - mtype, ok := db.types[table] - if !ok { - return nil, fmt.Errorf("table %s not found in database model", string(table)) - } - model := reflect.New(mtype.Elem()) - return model.Interface().(Model), nil -} - -// Types returns the DBModel Types -// the DBModel types is a map of reflect.Types indexed by string -// The reflect.Type is a pointer to a struct that contains 'ovs' tags -// as described above. Such pointer to struct also implements the Model interface -func (db DBModel) Types() map[string]reflect.Type { - return db.types -} - -// Name returns the database name -func (db DBModel) Name() string { - return db.name -} - -// FindTable returns the string associated with a reflect.Type or "" -func (db DBModel) FindTable(mType reflect.Type) string { - for table, tType := range db.types { - if tType == mType { - return table - } - } - return "" -} - -// Validate validates the DatabaseModel against the input schema -// Returns all the errors detected -func (db DBModel) Validate(schema *ovsdb.DatabaseSchema) []error { - var errors []error - if db.name != schema.Name { - errors = append(errors, fmt.Errorf("database model name (%s) does not match schema (%s)", - db.name, schema.Name)) - } - - for tableName := range db.types { - tableSchema := schema.Table(tableName) - if tableSchema == nil { - errors = append(errors, fmt.Errorf("database model contains a model for table %s that does not exist in schema", tableName)) - continue - } - model, err := db.NewModel(tableName) - if err != nil { - errors = append(errors, err) - continue - } - if _, err := mapper.NewInfo(tableSchema, model); err != nil { - errors = append(errors, err) - } - } - return errors -} - -// NewDBModel constructs a DBModel based on a database name and dictionary of models indexed by table name -func NewDBModel(name string, models map[string]Model) (*DBModel, error) { - types := make(map[string]reflect.Type, len(models)) - for table, model := range models { - modelType := reflect.TypeOf(model) - if modelType.Kind() != reflect.Ptr || modelType.Elem().Kind() != reflect.Struct { - return nil, fmt.Errorf("model is expected to be a pointer to struct") - } - hasUUID := false - for i := 0; i < modelType.Elem().NumField(); i++ { - if field := modelType.Elem().Field(i); field.Tag.Get("ovsdb") == "_uuid" && - field.Type.Kind() == reflect.String { - hasUUID = true - } - } - if !hasUUID { - return nil, fmt.Errorf("model is expected to have a string field called uuid") - } - - types[table] = reflect.TypeOf(model) - } - return &DBModel{ - types: types, - name: name, - }, nil -} - func modelSetUUID(model Model, uuid string) error { modelVal := reflect.ValueOf(model).Elem() for i := 0; i < modelVal.NumField(); i++ { diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/ovsdb/serverdb/model.go b/go-controller/vendor/github.com/ovn-org/libovsdb/ovsdb/serverdb/model.go index 511112f8f6e..45ba9da0053 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/ovsdb/serverdb/model.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/ovsdb/serverdb/model.go @@ -11,8 +11,8 @@ import ( ) // FullDatabaseModel returns the DatabaseModel object to be used in libovsdb -func FullDatabaseModel() (*model.DBModel, error) { - return model.NewDBModel("_Server", map[string]model.Model{ +func FullDatabaseModel() (*model.ClientDBModel, error) { + return model.NewClientDBModel("_Server", map[string]model.Model{ "Database": &Database{}, }) } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/server/database.go b/go-controller/vendor/github.com/ovn-org/libovsdb/server/database.go index a5cb8bab634..f5e5b8206fb 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/server/database.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/server/database.go @@ -16,17 +16,17 @@ type Database interface { Exists(database string) bool Commit(database string, id uuid.UUID, updates ovsdb.TableUpdates2) error CheckIndexes(database string, table string, m model.Model) error - List(database, table string, conditions ...ovsdb.Condition) ([]model.Model, error) + List(database, table string, conditions ...ovsdb.Condition) (map[string]model.Model, error) Get(database, table string, uuid string) (model.Model, error) } type inMemoryDatabase struct { databases map[string]*cache.TableCache - models map[string]*model.DBModel + models map[string]*model.ClientDBModel mutex sync.RWMutex } -func NewInMemoryDatabase(models map[string]*model.DBModel) Database { +func NewInMemoryDatabase(models map[string]*model.ClientDBModel) Database { return &inMemoryDatabase{ databases: make(map[string]*cache.TableCache), models: models, @@ -37,12 +37,16 @@ func NewInMemoryDatabase(models map[string]*model.DBModel) Database { func (db *inMemoryDatabase) CreateDatabase(name string, schema *ovsdb.DatabaseSchema) error { db.mutex.Lock() defer db.mutex.Unlock() - var mo *model.DBModel + var mo *model.ClientDBModel var ok bool if mo, ok = db.models[schema.Name]; !ok { return fmt.Errorf("no db model provided for schema with name %s", name) } - database, err := cache.NewTableCache(schema, mo, nil, nil) + dbModel, errs := model.NewDatabaseModel(schema, mo) + if len(errs) > 0 { + return fmt.Errorf("Failed to create DatabaseModel: %#+v", errs) + } + database, err := cache.NewTableCache(dbModel, nil, nil) if err != nil { return nil } @@ -78,7 +82,7 @@ func (db *inMemoryDatabase) CheckIndexes(database string, table string, m model. return targetTable.IndexExists(m) } -func (db *inMemoryDatabase) List(database, table string, conditions ...ovsdb.Condition) ([]model.Model, error) { +func (db *inMemoryDatabase) List(database, table string, conditions ...ovsdb.Condition) (map[string]model.Model, error) { if !db.Exists(database) { return nil, fmt.Errorf("db does not exist") } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/server/server.go b/go-controller/vendor/github.com/ovn-org/libovsdb/server/server.go index 5036f04e84b..a0d243161f5 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/server/server.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/server/server.go @@ -22,34 +22,29 @@ type OvsdbServer struct { db Database ready bool readyMutex sync.RWMutex - models map[string]DatabaseModel + models map[string]*model.DatabaseModel modelsMutex sync.RWMutex monitors map[*rpc2.Client]*connectionMonitors monitorMutex sync.RWMutex } -type DatabaseModel struct { - Model *model.DBModel - Schema *ovsdb.DatabaseSchema -} - // NewOvsdbServer returns a new OvsdbServer -func NewOvsdbServer(db Database, models ...DatabaseModel) (*OvsdbServer, error) { +func NewOvsdbServer(db Database, models ...*model.DatabaseModel) (*OvsdbServer, error) { o := &OvsdbServer{ done: make(chan struct{}, 1), db: db, - models: make(map[string]DatabaseModel), + models: make(map[string]*model.DatabaseModel), modelsMutex: sync.RWMutex{}, monitors: make(map[*rpc2.Client]*connectionMonitors), monitorMutex: sync.RWMutex{}, } o.modelsMutex.Lock() for _, model := range models { - o.models[model.Schema.Name] = model + o.models[model.Schema().Name] = model } o.modelsMutex.Unlock() for database, model := range o.models { - if err := o.db.CreateDatabase(database, model.Schema); err != nil { + if err := o.db.CreateDatabase(database, model.Schema()); err != nil { return nil, err } } @@ -113,7 +108,7 @@ func (o *OvsdbServer) ListDatabases(client *rpc2.Client, args []interface{}, rep dbs := []string{} o.modelsMutex.RLock() for _, db := range o.models { - dbs = append(dbs, db.Schema.Name) + dbs = append(dbs, db.Schema().Name) } o.modelsMutex.RUnlock() *reply = dbs @@ -132,7 +127,7 @@ func (o *OvsdbServer) GetSchema(client *rpc2.Client, args []interface{}, reply * return fmt.Errorf("database %s does not exist", db) } o.modelsMutex.RUnlock() - *reply = *model.Schema + *reply = *model.Schema() return nil } @@ -141,8 +136,8 @@ type Transaction struct { Cache *cache.TableCache } -func NewTransaction(schema *ovsdb.DatabaseSchema, model *model.DBModel) Transaction { - cache, err := cache.NewTableCache(schema, model, nil, nil) +func NewTransaction(model *model.DatabaseModel) Transaction { + cache, err := cache.NewTableCache(model, nil, nil) if err != nil { panic(err) } diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/server/transact.go b/go-controller/vendor/github.com/ovn-org/libovsdb/server/transact.go index 5fc9b59886c..088d89037f9 100644 --- a/go-controller/vendor/github.com/ovn-org/libovsdb/server/transact.go +++ b/go-controller/vendor/github.com/ovn-org/libovsdb/server/transact.go @@ -6,7 +6,6 @@ import ( "github.com/google/uuid" "github.com/ovn-org/libovsdb/cache" - "github.com/ovn-org/libovsdb/mapper" "github.com/ovn-org/libovsdb/ovsdb" ) @@ -75,21 +74,26 @@ func (o *OvsdbServer) Insert(database string, table string, rowUUID string, row dbModel := o.models[database] o.modelsMutex.Unlock() - m := mapper.NewMapper(dbModel.Schema) - tSchema := dbModel.Schema.Table(table) + m := dbModel.Mapper() if rowUUID == "" { rowUUID = uuid.NewString() } - model, err := dbModel.Model.NewModel(table) + model, err := dbModel.NewModel(table) if err != nil { return ovsdb.OperationResult{ Error: err.Error(), }, nil } - err = m.GetRowData(table, &row, model) + mapperInfo, err := dbModel.NewModelInfo(model) + if err != nil { + return ovsdb.OperationResult{ + Error: err.Error(), + }, nil + } + err = m.GetRowData(&row, mapperInfo) if err != nil { return ovsdb.OperationResult{ Error: err.Error(), @@ -97,12 +101,6 @@ func (o *OvsdbServer) Insert(database string, table string, rowUUID string, row } if rowUUID != "" { - mapperInfo, err := mapper.NewInfo(tSchema, model) - if err != nil { - return ovsdb.OperationResult{ - Error: err.Error(), - }, nil - } if err := mapperInfo.SetField("_uuid", rowUUID); err != nil { return ovsdb.OperationResult{ Error: err.Error(), @@ -110,7 +108,7 @@ func (o *OvsdbServer) Insert(database string, table string, rowUUID string, row } } - resultRow, err := m.NewRow(table, model) + resultRow, err := m.NewRow(mapperInfo) if err != nil { return ovsdb.OperationResult{ Error: err.Error(), @@ -155,7 +153,7 @@ func (o *OvsdbServer) Select(database string, table string, where []ovsdb.Condit dbModel := o.models[database] o.modelsMutex.Unlock() - m := mapper.NewMapper(dbModel.Schema) + m := dbModel.Mapper() var results []ovsdb.Row rows, err := o.db.List(database, table, where...) @@ -163,7 +161,11 @@ func (o *OvsdbServer) Select(database string, table string, where []ovsdb.Condit panic(err) } for _, row := range rows { - resultRow, err := m.NewRow(table, row) + info, err := dbModel.NewModelInfo(row) + if err != nil { + panic(err) + } + resultRow, err := m.NewRow(info) if err != nil { panic(err) } @@ -184,8 +186,8 @@ func (o *OvsdbServer) Update(database, table string, where []ovsdb.Condition, ro dbModel := o.models[database] o.modelsMutex.Unlock() - m := mapper.NewMapper(dbModel.Schema) - schema := dbModel.Schema.Table(table) + m := dbModel.Mapper() + schema := dbModel.Schema().Table(table) tableUpdate := make(ovsdb.TableUpdate2) rows, err := o.db.List(database, table, where...) if err != nil { @@ -194,26 +196,26 @@ func (o *OvsdbServer) Update(database, table string, where []ovsdb.Condition, ro }, nil } for _, old := range rows { - info, _ := mapper.NewInfo(schema, old) - uuid, _ := info.FieldByColumn("_uuid") + oldInfo, _ := dbModel.NewModelInfo(old) + uuid, _ := oldInfo.FieldByColumn("_uuid") - oldRow, err := m.NewRow(table, old) + oldRow, err := m.NewRow(oldInfo) if err != nil { panic(err) } - new, err := dbModel.Model.NewModel(table) + new, err := dbModel.NewModel(table) if err != nil { panic(err) } - err = m.GetRowData(table, &oldRow, new) + newInfo, err := dbModel.NewModelInfo(new) if err != nil { panic(err) } - info, err = mapper.NewInfo(schema, new) + err = m.GetRowData(&oldRow, newInfo) if err != nil { panic(err) } - err = info.SetField("_uuid", uuid) + err = newInfo.SetField("_uuid", uuid) if err != nil { panic(err) } @@ -235,7 +237,7 @@ func (o *OvsdbServer) Update(database, table string, where []ovsdb.Condition, ro Details: fmt.Sprintf("column %s is of table %s not mutable", column, table), }, nil } - old, err := info.FieldByColumn(column) + old, err := newInfo.FieldByColumn(column) if err != nil { panic(err) } @@ -254,7 +256,7 @@ func (o *OvsdbServer) Update(database, table string, where []ovsdb.Condition, ro continue } - err = info.SetField(column, native) + err = newInfo.SetField(column, native) if err != nil { panic(err) } @@ -270,7 +272,7 @@ func (o *OvsdbServer) Update(database, table string, where []ovsdb.Condition, ro } } - newRow, err := m.NewRow(table, new) + newRow, err := m.NewRow(newInfo) if err != nil { panic(err) } @@ -313,8 +315,8 @@ func (o *OvsdbServer) Mutate(database, table string, where []ovsdb.Condition, mu dbModel := o.models[database] o.modelsMutex.Unlock() - m := mapper.NewMapper(dbModel.Schema) - schema := dbModel.Schema.Table(table) + m := dbModel.Mapper() + schema := dbModel.Schema().Table(table) tableUpdate := make(ovsdb.TableUpdate2) @@ -324,24 +326,24 @@ func (o *OvsdbServer) Mutate(database, table string, where []ovsdb.Condition, mu } for _, old := range rows { - oldInfo, err := mapper.NewInfo(schema, old) + oldInfo, err := dbModel.NewModelInfo(old) if err != nil { panic(err) } uuid, _ := oldInfo.FieldByColumn("_uuid") - oldRow, err := m.NewRow(table, old) + oldRow, err := m.NewRow(oldInfo) if err != nil { panic(err) } - new, err := dbModel.Model.NewModel(table) + new, err := dbModel.NewModel(table) if err != nil { panic(err) } - err = m.GetRowData(table, &oldRow, new) + newInfo, err := dbModel.NewModelInfo(new) if err != nil { panic(err) } - newInfo, err := mapper.NewInfo(schema, new) + err = m.GetRowData(&oldRow, newInfo) if err != nil { panic(err) } @@ -424,7 +426,7 @@ func (o *OvsdbServer) Mutate(database, table string, where []ovsdb.Condition, mu }, nil } - newRow, err := m.NewRow(table, new) + newRow, err := m.NewRow(newInfo) if err != nil { panic(err) } @@ -452,17 +454,17 @@ func (o *OvsdbServer) Delete(database, table string, where []ovsdb.Condition) (o o.modelsMutex.Lock() dbModel := o.models[database] o.modelsMutex.Unlock() - m := mapper.NewMapper(dbModel.Schema) - schema := dbModel.Schema.Table(table) + m := dbModel.Mapper() + tableUpdate := make(ovsdb.TableUpdate2) rows, err := o.db.List(database, table, where...) if err != nil { panic(err) } for _, row := range rows { - info, _ := mapper.NewInfo(schema, row) + info, _ := dbModel.NewModelInfo(row) uuid, _ := info.FieldByColumn("_uuid") - oldRow, err := m.NewRow(table, row) + oldRow, err := m.NewRow(info) if err != nil { panic(err) } diff --git a/go-controller/vendor/modules.txt b/go-controller/vendor/modules.txt index 93d507753a8..c0c935431a4 100644 --- a/go-controller/vendor/modules.txt +++ b/go-controller/vendor/modules.txt @@ -170,7 +170,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util github.com/onsi/gomega/types -# github.com/ovn-org/libovsdb v0.6.1-0.20211014201246-28345b9aeccf +# github.com/ovn-org/libovsdb v0.6.1-0.20211025161502-80be4acc6773 github.com/ovn-org/libovsdb/cache github.com/ovn-org/libovsdb/client github.com/ovn-org/libovsdb/mapper