Hi! The OVN project recommends upgrading controllers first, then 
updating ovn-northd and ovsdb-servers. However, some deployments might 
upgrade ovn-northd before ovn-controllers, which can cause downtime in 
large-scale environments if older controllers don’t understand new 
logical flows or actions from ovn-northd. I made a patch to validate 
currently supported actions in the SB, but I don’t like storing them in 
the sb_global table. I’ve thought of three options: 1) Keep the current 
approach (storing in sb_global)—pro: uses existing string-array 
structures, con: requires schema changes; 2) Store them in options —con: 
awkward to parse smap; or 3) Create a dedicated SB table, which feels 
more extensible. Ideally, we’d validate not just actions but their 
arguments too, though I haven’t settled on a clean design for that: what 
is the best way to implement the argument signature. A separate table 
seems better for future flexibility. We should also validate 
ingress/egress OpenFlow table numbering if the NB changes. I’d love 
feedback on these options!

On 28.05.2025 23:59, Alexandra Rukomoinikova wrote:
> According to OVN, northd should be updated before controllers
> since northd may introduce new actions that older controllers don't support.
> This commit adds an optional validation step to ensure controllers are
> compatible with northd actions before proceeding with upgrades.
>
> Signed-off-by: Alexandra Rukomoinikova <arukomoinikova@k2.cloud>
> ---
>   controller/ovn-controller.c | 73 +++++++++++++++++++++++++++++++++++++
>   include/ovn/actions.h       |  9 +++++
>   northd/en-global-config.c   | 21 +++++++++++
>   ovn-sb.ovsschema            |  5 ++-
>   ovn-sb.xml                  |  4 ++
>   tests/ovn.at                | 28 ++++++++++++++
>   6 files changed, 139 insertions(+), 1 deletion(-)
>
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 89207bb4b..0b665f28a 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -173,6 +173,8 @@ struct pending_pkt {
>   /* Registered ofctrl seqno type for nb_cfg propagation. */
>   static size_t ofctrl_seq_type_nb_cfg;
>   
> +static bool conntroller_validate_actions = false;
> +
>   static void
>   remove_newline(char *s)
>   {
> @@ -203,6 +205,57 @@ static char *get_file_system_id(void)
>       free(filename);
>       return ret;
>   }
> +
> +static void
> +log_actions_diff(const void *arg OVS_UNUSED,
> +                 const char *item, bool northd_supported)
> +{
> +    VLOG_WARN("Validate actions: action %s supported by %s "
> +              "but not supported by %s.", item,
> +              northd_supported ? "northd" : "controller",
> +              northd_supported ? "controller" : "northd");
> +}
> +
> +static void
> +validate_compatibility_with_northd(const struct sbrec_sb_global *sb,
> +                                   bool sb_global_upgraded)
> +{
> +    static bool executed = false;
> +
> +    if (executed) {
> +        return;
> +    }
> +
> +    if (!sb || !sb->n_supported_actions) {
> +        VLOG_WARN("Validate actions: %s",
> +                  !sb ? "No SB Global record found." :
> +                  "No northd actions available in SB Global.");
> +        return;
> +    }
> +    struct svec controller_action_entries = SVEC_EMPTY_INITIALIZER;
> +    ovn_action_get_names(&controller_action_entries);
> +
> +    struct sorted_array controller_actions =
> +        sorted_array_from_svec(&controller_action_entries);
> +
> +    struct sorted_array northd_actions =
> +        sorted_array_create((const char **) sb->supported_actions,
> +                            sb->n_supported_actions,
> +                            false);
> +
> +    sorted_array_apply_diff(&northd_actions,
> +                            &controller_actions,
> +                            log_actions_diff, NULL);
> +
> +    VLOG_WARN("Validate actions: All northd actions are validated. ");
> +
> +    executed = sb_global_upgraded ? false : true;
> +
> +    sorted_array_destroy(&northd_actions);
> +    sorted_array_destroy(&controller_actions);
> +    svec_destroy(&controller_action_entries);
> +}
> +
>   /* Only set monitor conditions on tables that are available in the
>    * server schema.
>    */
> @@ -755,6 +808,18 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl 
> *ovnsb_idl,
>       if (monitor_all_p) {
>           *monitor_all_p = monitor_all;
>       }
> +
> +    /* Check actions compatibility when updating SB_DB. */
> +    conntroller_validate_actions =
> +        get_chassis_external_id_value_bool(
> +            &cfg->external_ids, chassis_id,
> +            "validate-northd-actions", false);
> +
> +    if (conntroller_validate_actions) {
> +        const struct sbrec_sb_global *sb = sbrec_sb_global_first(ovnsb_idl);
> +        validate_compatibility_with_northd(sb, false);
> +    }
> +
>       if (reset_ovnsb_idl_min_index && *reset_ovnsb_idl_min_index) {
>           VLOG_INFO("Resetting southbound database cluster state");
>           engine_set_force_recompute();
> @@ -3619,6 +3684,10 @@ en_northd_options_run(struct engine_node *node, void 
> *data)
>                           true)
>           : true;
>   
> +    if (conntroller_validate_actions) {
> +        validate_compatibility_with_northd(sb_global, false);
> +    }
> +
>       return EN_UPDATED;
>   }
>   
> @@ -3676,6 +3745,10 @@ en_northd_options_sb_sb_global_handler(struct 
> engine_node *node, void *data)
>           result = EN_HANDLED_UPDATED;
>       }
>   
> +    if (conntroller_validate_actions) {
> +        validate_compatibility_with_northd(sb_global, false);
> +    }
> +
>       return result;
>   }
>   
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index 055d3b26c..3b3912152 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -23,6 +23,7 @@
>   #include "expr.h"
>   #include "openvswitch/dynamic-string.h"
>   #include "openvswitch/hmap.h"
> +#include "lib/svec.h"
>   #include "openvswitch/ofp-actions.h"
>   #include "openvswitch/uuid.h"
>   #include "util.h"
> @@ -152,6 +153,14 @@ enum {
>   #undef OVNACT
>   };
>   
> +static inline void
> +ovn_action_get_names(struct svec *ovnacts)
> +{
> +    #define OVNACT(ENUM, STRUCT) svec_add(ovnacts, #ENUM);
> +    OVNACTS
> +    #undef OVNACT
> +}
> +
>   /* Header for an action.
>    *
>    * Each action is a structure "struct ovnact_*" that begins with "struct
> diff --git a/northd/en-global-config.c b/northd/en-global-config.c
> index ee5adfd3b..19b05f7e8 100644
> --- a/northd/en-global-config.c
> +++ b/northd/en-global-config.c
> @@ -26,6 +26,7 @@
>   #include "en-global-config.h"
>   #include "en-sampling-app.h"
>   #include "include/ovn/features.h"
> +#include "include/ovn/actions.h"
>   #include "ipam.h"
>   #include "lib/ovn-nb-idl.h"
>   #include "lib/ovn-sb-idl.h"
> @@ -51,6 +52,7 @@ static void update_sb_config_options_to_sbrec(struct 
> ed_type_global_config *,
>                                                 const struct sbrec_sb_global 
> *);
>   static bool is_vxlan_mode(const struct smap *nb_options,
>                             const struct sbrec_chassis_table *);
> +static void set_supported_actions_to_sbrec(const struct sbrec_sb_global *sb);
>   
>   void *
>   en_global_config_init(struct engine_node *node OVS_UNUSED,
> @@ -179,6 +181,8 @@ en_global_config_run(struct engine_node *node , void 
> *data)
>       /* Set up SB_Global (depends on chassis features). */
>       update_sb_config_options_to_sbrec(config_data, sb);
>   
> +    set_supported_actions_to_sbrec(sb);
> +
>       return EN_UPDATED;
>   }
>   
> @@ -713,3 +717,20 @@ is_vxlan_mode(const struct smap *nb_options,
>       }
>       return false;
>   }
> +
> +static void
> +set_supported_actions_to_sbrec(const struct sbrec_sb_global *sb)
> +{
> +    struct svec ovnacts = SVEC_EMPTY_INITIALIZER;
> +
> +    ovn_action_get_names(&ovnacts);
> +
> +    struct sorted_array ovnacts_sorted =
> +        sorted_array_from_svec(&ovnacts);
> +
> +    sbrec_sb_global_set_supported_actions(sb, ovnacts_sorted.arr,
> +                                          ovnacts_sorted.n);
> +
> +    sorted_array_destroy(&ovnacts_sorted);
> +    svec_destroy(&ovnacts);
> +}
> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
> index 4c24f5b51..c956ef888 100644
> --- a/ovn-sb.ovsschema
> +++ b/ovn-sb.ovsschema
> @@ -1,7 +1,7 @@
>   {
>       "name": "OVN_Southbound",
>       "version": "21.2.0",
> -    "cksum": "29145795 34859",
> +    "cksum": "2732310741 35034",
>       "tables": {
>           "SB_Global": {
>               "columns": {
> @@ -18,6 +18,9 @@
>                       "type": {"key": {"type": "uuid",
>                                        "refTable": "SSL"},
>                                        "min": 0, "max": 1}},
> +                "supported_actions" : {"type": {"key": "string",
> +                                       "min": 0,
> +                                       "max": "unlimited"}},
>                   "options": {
>                       "type": {"key": "string", "value": "string",
>                                "min": 0, "max": "unlimited"}},
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index db5faac66..d4924c3f8 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -165,6 +165,10 @@
>   
>         <column name="options">
>         </column>
> +
> +    <column name="supported_actions">
> +        Logical flow actions supported by northd.
> +    </column>
>       </group>
>   
>       <group title="Common options">
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 46a0f09bc..1f9a1fa88 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -43500,3 +43500,31 @@ AT_CHECK([grep -q "Postponing virtual lport sw0-vir" 
> hv3/ovn-controller.log], [1
>   OVN_CLEANUP([hv1])
>   AT_CLEANUP
>   ])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([Controller actions validating])
> +ovn_start
> +
> +net_add n1
> +
> +sim_add hv1
> +as hv1
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +ovs-vsctl add-port br-int vif1 -- \
> +          set Interface vif1 external-ids:iface-id=lp1 \
> +          options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap 
> \
> +          ofport-request=1
> +
> +# Since from the tests we can't check the situation when the controller 
> doesn't support all
> +# northd actions - we will check the opposite situation.
> +check  ovn-sbctl remove SB_G . supported_actions MOVE
> +
> +check ovs-vsctl set Open_Vswitch . external_ids:validate-northd-actions=true
> +
> +AT_CHECK([grep -o "action MOVE supported by controller but not supported by 
> northd." hv1/ovn-controller.log], [0], [dnl
> +action MOVE supported by controller but not supported by northd.
> +])
> +AT_CLEANUP
> +])
> +


-- 
regards,
Alexandra.

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to