Use requested-encap-ip (with requested-chassis) to set Port_Binding.encap, clear on removal, and prefer geneve when both types exist. Add a northd test and document the ovn-k8s interconnect use case.
CC: Han Zhou <[email protected]> Signed-off-by: Lei Huang <[email protected]> --- NEWS | 3 ++ northd/northd.c | 116 +++++++++++++++++++++++++++++++++++++++++--- ovn-nb.xml | 18 +++++++ tests/ovn-northd.at | 63 ++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 2a2b5e12d..040577519 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ Post v25.09.0 ------------- + - Added LSP/LRP option "requested-encap-ip" to let CMS request a specific + SB Port_Binding encap IP (e.g., for remote transit ports in ovn-k8s + interconnect mode). - Added DNS query statistics tracking in ovn-controller using OVS coverage counters. Statistics can be queried using "ovn-appctl -t ovn-controller coverage/read-counter <counter_name>" or "coverage/show". Tracked metrics diff --git a/northd/northd.c b/northd/northd.c index b4bb4ba6d..3e8e9a994 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -2531,6 +2531,82 @@ ovn_port_update_sbrec_chassis( free(requested_chassis_sb); } +static void +encap_ip_map_init(struct shash *encap_by_ip, + const struct sbrec_chassis_table *sbrec_chassis_table) +{ + shash_init(encap_by_ip); + if (!sbrec_chassis_table) { + return; + } + + const struct sbrec_chassis *chassis; + SBREC_CHASSIS_TABLE_FOR_EACH (chassis, sbrec_chassis_table) { + for (size_t i = 0; i < chassis->n_encaps; i++) { + const struct sbrec_encap *encap = chassis->encaps[i]; + if (!encap || !encap->ip || !encap->type || !encap->chassis_name) { + continue; + } + + enum chassis_tunnel_type tun_type = get_tunnel_type(encap->type); + if (tun_type == TUNNEL_TYPE_INVALID) { + continue; + } + + char *key = xasprintf("%s@%s", encap->chassis_name, encap->ip); + struct shash_node *node = shash_find(encap_by_ip, key); + if (!node) { + shash_add_nocopy(encap_by_ip, key, (void *) encap); + } else { + free(key); + const struct sbrec_encap *existing = node->data; + /* Pick the highest-preference tunnel type (geneve > vxlan) + * when multiple encap types share the same chassis+IP. */ + if (get_tunnel_type(existing->type) < tun_type) { + node->data = (void *) encap; + } + } + } + } +} + +static const struct sbrec_encap * +encap_ip_map_lookup(const struct shash *encap_by_ip, const char *chassis_name, + const char *ip) +{ + if (!encap_by_ip || !chassis_name || !chassis_name[0] || !ip || !ip[0]) { + return NULL; + } + char *key = xasprintf("%s@%s", chassis_name, ip); + const struct sbrec_encap *encap = shash_find_data(encap_by_ip, key); + free(key); + return encap; +} + +static void +ovn_port_update_requested_encap(const struct shash *encap_by_ip, + const struct ovn_port *op) +{ + if (is_cr_port(op)) { + return; + } + + /* requested-chassis is resolved into SB first; reuse that binding. */ + const struct smap *options = op->nbsp ? &op->nbsp->options + : &op->nbrp->options; + const char *requested_ip = smap_get(options, "requested-encap-ip"); + const struct sbrec_encap *encap = NULL; + if (requested_ip && requested_ip[0] && op->sb->requested_chassis) { + encap = encap_ip_map_lookup(encap_by_ip, + op->sb->requested_chassis->name, + requested_ip); + } + + if (op->sb->encap != encap) { + sbrec_port_binding_set_encap(op->sb, encap); + } +} + static void check_and_do_sb_mirror_deletion(const struct ovn_port *op) { @@ -2601,6 +2677,7 @@ ovn_port_update_sbrec(struct ovsdb_idl_txn *ovnsb_txn, struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, const struct sbrec_mirror_table *sbrec_mirror_table, + const struct shash *encap_by_ip, const struct ovn_port *op, unsigned long *queue_id_bitmap, struct sset *active_ha_chassis_grps) @@ -2937,6 +3014,10 @@ common: sbrec_port_binding_set_tunnel_key(op->sb, op->tunnel_key); } + if (encap_by_ip) { + ovn_port_update_requested_encap(encap_by_ip, op); + } + /* ovn-controller will update 'Port_Binding.up' only if it was explicitly * set to 'false'. */ @@ -4096,6 +4177,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, const struct sbrec_mirror_table *sbrec_mirror_table, const struct sbrec_mac_binding_table *sbrec_mac_binding_table, const struct sbrec_ha_chassis_group_table *sbrec_ha_chassis_group_table, + const struct sbrec_chassis_table *sbrec_chassis_table, struct ovsdb_idl_index *sbrec_chassis_by_name, struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, @@ -4113,6 +4195,9 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, struct sset active_ha_chassis_grps = SSET_INITIALIZER(&active_ha_chassis_grps); + struct shash encap_by_ip; + encap_ip_map_init(&encap_by_ip, sbrec_chassis_table); + /* Borrow ls_ports for joining NB and SB for both LSPs and LRPs. * We will split them later. */ struct hmap *ports = ls_ports; @@ -4172,6 +4257,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, sbrec_chassis_by_hostname, sbrec_ha_chassis_grp_by_name, sbrec_mirror_table, + &encap_by_ip, op, queue_id_bitmap, &active_ha_chassis_grps); op->od->is_transit_router |= is_transit_router_port(op); @@ -4185,6 +4271,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, sbrec_chassis_by_hostname, sbrec_ha_chassis_grp_by_name, sbrec_mirror_table, + &encap_by_ip, op, queue_id_bitmap, &active_ha_chassis_grps); sbrec_port_binding_set_logical_port(op->sb, op->key); @@ -4215,6 +4302,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, cleanup_mac_bindings(sbrec_mac_binding_table, lr_datapaths, lr_ports); } + shash_destroy(&encap_by_ip); tag_alloc_destroy(&tag_alloc_table); bitmap_free(queue_id_bitmap); cleanup_sb_ha_chassis_groups(sbrec_ha_chassis_group_table, @@ -4401,7 +4489,8 @@ ls_port_init(struct ovn_port *op, struct ovsdb_idl_txn *ovnsb_txn, const struct sbrec_port_binding *sb, const struct sbrec_mirror_table *sbrec_mirror_table, struct ovsdb_idl_index *sbrec_chassis_by_name, - struct ovsdb_idl_index *sbrec_chassis_by_hostname) + struct ovsdb_idl_index *sbrec_chassis_by_hostname, + const struct shash *encap_by_ip) { op->od = od; parse_lsp_addrs(op); @@ -4431,6 +4520,7 @@ ls_port_init(struct ovn_port *op, struct ovsdb_idl_txn *ovnsb_txn, } ovn_port_update_sbrec(ovnsb_txn, sbrec_chassis_by_name, sbrec_chassis_by_hostname, NULL, sbrec_mirror_table, + encap_by_ip, op, NULL, NULL); return true; } @@ -4441,13 +4531,15 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports, struct ovn_datapath *od, const struct sbrec_mirror_table *sbrec_mirror_table, struct ovsdb_idl_index *sbrec_chassis_by_name, - struct ovsdb_idl_index *sbrec_chassis_by_hostname) + struct ovsdb_idl_index *sbrec_chassis_by_hostname, + const struct shash *encap_by_ip) { struct ovn_port *op = ovn_port_create(ls_ports, key, nbsp, NULL, NULL); hmap_insert(&od->ports, &op->dp_node, hmap_node_hash(&op->key_node)); if (!ls_port_init(op, ovnsb_txn, od, NULL, sbrec_mirror_table, - sbrec_chassis_by_name, sbrec_chassis_by_hostname)) { + sbrec_chassis_by_name, sbrec_chassis_by_hostname, + encap_by_ip)) { ovn_port_destroy(ls_ports, op); return NULL; } @@ -4462,14 +4554,16 @@ ls_port_reinit(struct ovn_port *op, struct ovsdb_idl_txn *ovnsb_txn, const struct sbrec_port_binding *sb, const struct sbrec_mirror_table *sbrec_mirror_table, struct ovsdb_idl_index *sbrec_chassis_by_name, - struct ovsdb_idl_index *sbrec_chassis_by_hostname) + struct ovsdb_idl_index *sbrec_chassis_by_hostname, + const struct shash *encap_by_ip) { ovn_port_cleanup(op); op->sb = sb; ovn_port_set_nb(op, nbsp, NULL); op->primary_port = op->cr_port = NULL; return ls_port_init(op, ovnsb_txn, od, sb, sbrec_mirror_table, - sbrec_chassis_by_name, sbrec_chassis_by_hostname); + sbrec_chassis_by_name, sbrec_chassis_by_hostname, + encap_by_ip); } /* Returns true if the logical switch has changes which can be @@ -4632,6 +4726,9 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn, return true; } + struct shash encap_by_ip; + encap_ip_map_init(&encap_by_ip, ni->sbrec_chassis_table); + bool ls_had_only_router_ports = (!vector_is_empty(&od->router_ports) && (vector_len(&od->router_ports) == hmap_count(&od->ports))); @@ -4658,7 +4755,8 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn, new_nbsp->name, new_nbsp, od, ni->sbrec_mirror_table, ni->sbrec_chassis_by_name, - ni->sbrec_chassis_by_hostname); + ni->sbrec_chassis_by_hostname, + &encap_by_ip); if (!op) { goto fail; } @@ -4697,7 +4795,8 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn, new_nbsp, od, sb, ni->sbrec_mirror_table, ni->sbrec_chassis_by_name, - ni->sbrec_chassis_by_hostname)) { + ni->sbrec_chassis_by_hostname, + &encap_by_ip)) { if (sb) { sbrec_port_binding_delete(sb); } @@ -4798,9 +4897,11 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn, } sset_destroy(&created_or_deleted_ports); + shash_destroy(&encap_by_ip); return true; fail: + shash_destroy(&encap_by_ip); destroy_tracked_ovn_ports(trk_lsps); return false; } @@ -20622,6 +20723,7 @@ ovnnb_db_run(struct northd_input *input_data, input_data->sbrec_mirror_table, input_data->sbrec_mac_binding_table, input_data->sbrec_ha_chassis_group_table, + input_data->sbrec_chassis_table, input_data->sbrec_chassis_by_name, input_data->sbrec_chassis_by_hostname, input_data->sbrec_ha_chassis_grp_by_name, diff --git a/ovn-nb.xml b/ovn-nb.xml index 1acbf202b..33bf8993a 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1550,6 +1550,15 @@ </p> </column> + <column name="options" key="requested-encap-ip"> + Requests the encapsulation IP address for the port binding. If set, + <code>ovn-northd</code> uses this IP to select the + <ref table="Encap" db="OVN_Southbound"/> entry written to + <ref table="Port_Binding" column="encap" db="OVN_Southbound"/>. + This is intended for ports without a local OVS interface, e.g. remote + transit switch ports in ovn-kubernetes interconnect mode. + </column> + <column name="options" key="activation-strategy"> If used with multiple chassis set in <ref column="requested-chassis"/>, specifies an activation strategy @@ -4393,6 +4402,15 @@ or </p> </column> + <column name="options" key="requested-encap-ip"> + Requests the encapsulation IP address for the port binding. If set, + <code>ovn-northd</code> uses this IP to select the + <ref table="Encap" db="OVN_Southbound"/> entry written to + <ref table="Port_Binding" column="encap" db="OVN_Southbound"/>. + This is intended for ports without a local OVS interface, e.g. remote + transit router ports in ovn-kubernetes interconnect mode. + </column> + <column name="options" key="dynamic-routing-redistribute" type='{"type": "string"}'> <p> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 512e42036..9245ff639 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -2896,6 +2896,69 @@ OVN_CLEANUP_NORTHD AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([check options:requested-encap-ip fills port binding encap col]) +AT_KEYWORDS([requested encap ip]) +ovn_start + +check_uuid ovn-sbctl \ + -- --id=@e11 create encap chassis_name=ch1 ip="192.168.1.1" type="geneve" \ + -- --id=@e12 create encap chassis_name=ch1 ip="192.168.1.2" type="geneve" \ + -- --id=@c1 create chassis name=ch1 encaps=@e11,@e12 +check_uuid ovn-sbctl \ + -- --id=@e21 create encap chassis_name=ch2 ip="192.168.2.1" type="geneve" \ + -- --id=@e22 create encap chassis_name=ch2 ip="192.168.2.2" type="geneve" \ + -- --id=@c2 create chassis name=ch2 encaps=@e21,@e22 + +wait_row_count Chassis 2 +wait_row_count Encap 4 +en11_uuid=`ovn-sbctl --bare --columns _uuid find Encap ip="192.168.1.1"` +en12_uuid=`ovn-sbctl --bare --columns _uuid find Encap ip="192.168.1.2"` +en21_uuid=`ovn-sbctl --bare --columns _uuid find Encap ip="192.168.2.1"` +en22_uuid=`ovn-sbctl --bare --columns _uuid find Encap ip="192.168.2.2"` +ovn-sbctl show + +echo "__file__:__line__: encap uuid: $en11_uuid, ip: 192.168.1.1" +echo "__file__:__line__: encap uuid: $en12_uuid, ip: 192.168.1.2" +echo "__file__:__line__: encap uuid: $en21_uuid, ip: 192.168.2.1" +echo "__file__:__line__: encap uuid: $en22_uuid, ip: 192.168.2.2" + +check ovn-nbctl --wait=sb ls-add ls1 +check ovn-nbctl --wait=sb lsp-add ls1 lsp1 +check ovn-nbctl --wait=sb lsp-add ls1 lsp2 +ovn-nbctl show + +echo "options:requested-chassis is required to set options:requested-encap-ip" +check ovn-nbctl --wait=sb set logical-switch-port lsp1 \ + options:requested-encap-ip=192.168.1.1 +check ovn-nbctl --wait=sb sync +wait_row_count Port_Binding 1 logical_port=lsp1 'encap=[[]]' + +# Now set both options +check ovn-nbctl --wait=sb set logical-switch-port lsp1 \ + options:requested-chassis=ch1 \ + options:requested-encap-ip=192.168.1.1 +check ovn-nbctl --wait=sb set logical-switch-port lsp2 \ + options:requested-chassis=ch2 \ + options:requested-encap-ip=192.168.2.2 + +wait_row_count Port_Binding 1 logical_port=lsp1 encap="$en11_uuid" +wait_row_count Port_Binding 1 logical_port=lsp2 encap="$en22_uuid" + +# remove options:requested-encap-ip from lsp1 +check ovn-nbctl --wait=sb remove logical_switch_port lsp1 \ + options requested-encap-ip=192.168.1.1 +wait_row_count Port_Binding 1 logical_port=lsp1 'encap=[[]]' + +# remove options:requested-chassis from lsp2 +check ovn-nbctl --wait=sb remove logical_switch_port lsp2 \ + options requested-chassis=ch2 +wait_row_count Port_Binding 1 logical_port=lsp2 'encap=[[]]' + +OVN_CLEANUP_NORTHD +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD_NO_HV([ AT_SETUP([port requested-tnl-key]) AT_KEYWORDS([requested tnl tunnel key keys]) -- 2.39.5 (Apple Git-154) _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
