When "--restart" is passed to ovn-controller's exit command, then database entries are not removed for this hypervisor. This means that * Encaps * Chassis * OVS ports are not removed.
The reasoning is that if the intent is to restart ovn-controller, this will allow for tunnels to remain up and allow for traffic not to be interrupted during the restart. When ovn-controller is started again, it picks back up from where it was. Signed-off-by: Mark Michelson <mmich...@redhat.com> --- ovn/controller/ovn-controller.c | 92 +++++++++++--------- tests/ovn.at | 186 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 40 deletions(-) diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 6ee72a9fa..bd8175af5 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -541,11 +541,18 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) physical_register_ovs_idl(ovs_idl); } +struct ovn_controller_exit_args { + bool *exiting; + bool *restart; +}; + int main(int argc, char *argv[]) { struct unixctl_server *unixctl; bool exiting; + bool restart; + struct ovn_controller_exit_args exit_args = {&exiting, &restart}; int retval; ovs_cmdl_proctitle_init(argc, argv); @@ -560,7 +567,8 @@ main(int argc, char *argv[]) if (retval) { exit(EXIT_FAILURE); } - unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting); + unixctl_command_register("exit", "", 0, 1, ovn_controller_exit, + &exit_args); /* Initialize group ids for loadbalancing. */ struct ovn_extend_table group_table; @@ -631,6 +639,7 @@ main(int argc, char *argv[]) stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); /* Main loop. */ exiting = false; + restart = false; while (!exiting) { /* Check OVN SB database. */ char *new_ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl); @@ -848,42 +857,45 @@ main(int argc, char *argv[]) } } - /* It's time to exit. Clean up the databases. */ - bool done = false; - while (!done) { - struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop); - struct ovsdb_idl_txn *ovnsb_idl_txn - = ovsdb_idl_loop_run(&ovnsb_idl_loop); + /* It's time to exit. Clean up the databases if we are not restarting */ + if (!restart) { + bool done = false; + while (!done) { + struct ovsdb_idl_txn *ovs_idl_txn + = ovsdb_idl_loop_run(&ovs_idl_loop); + struct ovsdb_idl_txn *ovnsb_idl_txn + = ovsdb_idl_loop_run(&ovnsb_idl_loop); + + const struct ovsrec_bridge_table *bridge_table + = ovsrec_bridge_table_get(ovs_idl_loop.idl); + const struct ovsrec_open_vswitch_table *ovs_table + = ovsrec_open_vswitch_table_get(ovs_idl_loop.idl); + + const struct sbrec_port_binding_table *port_binding_table + = sbrec_port_binding_table_get(ovnsb_idl_loop.idl); + + const struct ovsrec_bridge *br_int = get_br_int(ovs_idl_txn, + bridge_table, + ovs_table); + const char *chassis_id = get_chassis_id(ovs_table); + const struct sbrec_chassis *chassis + = (chassis_id + ? chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id) + : NULL); + + /* Run all of the cleanup functions, even if one of them returns + * false. We're done if all of them return true. */ + done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis); + done = chassis_cleanup(ovnsb_idl_txn, chassis) && done; + done = encaps_cleanup(ovs_idl_txn, br_int) && done; + if (done) { + poll_immediate_wake(); + } - const struct ovsrec_bridge_table *bridge_table - = ovsrec_bridge_table_get(ovs_idl_loop.idl); - const struct ovsrec_open_vswitch_table *ovs_table - = ovsrec_open_vswitch_table_get(ovs_idl_loop.idl); - - const struct sbrec_port_binding_table *port_binding_table - = sbrec_port_binding_table_get(ovnsb_idl_loop.idl); - - const struct ovsrec_bridge *br_int = get_br_int(ovs_idl_txn, - bridge_table, - ovs_table); - const char *chassis_id = get_chassis_id(ovs_table); - const struct sbrec_chassis *chassis - = (chassis_id - ? chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id) - : NULL); - - /* Run all of the cleanup functions, even if one of them returns false. - * We're done if all of them return true. */ - done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis); - done = chassis_cleanup(ovnsb_idl_txn, chassis) && done; - done = encaps_cleanup(ovs_idl_txn, br_int) && done; - if (done) { - poll_immediate_wake(); + ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); + ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop); + poll_block(); } - - ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); - ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop); - poll_block(); } unixctl_server_destroy(unixctl); @@ -999,12 +1011,12 @@ usage(void) } static void -ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, - const char *argv[] OVS_UNUSED, void *exiting_) +ovn_controller_exit(struct unixctl_conn *conn, int argc, + const char *argv[], void *exit_args_) { - bool *exiting = exiting_; - *exiting = true; - + struct ovn_controller_exit_args *exit_args = exit_args_; + *exit_args->exiting = true; + *exit_args->restart = argc == 2 && !strcmp(argv[1], "--restart"); unixctl_command_reply(conn, NULL); } diff --git a/tests/ovn.at b/tests/ovn.at index d1a8967dd..1001305c5 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -10541,3 +10541,189 @@ OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [vif2.expected]) OVN_CLEANUP([hv1], [hv2]) AT_CLEANUP + +AT_SETUP([ovn -- ovn-controller exit]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start +# Logical network: +# One Logical Router: ro, with two logical switches sw1 and sw2. +# sw1 is for subnet 10.0.0.0/8 +# sw2 is for subnet 20.0.0.0/8 +# sw1 has a single port bound on hv1 +# sw2 has a single port bound on hv2 + +ovn-nbctl lr-add ro +ovn-nbctl ls-add sw1 +ovn-nbctl ls-add sw2 + +sw1_ro_mac=00:00:10:00:00:01 +sw1_ro_ip=10.0.0.1 +sw2_ro_mac=00:00:20:00:00:01 +sw2_ro_ip=20.0.0.1 +sw1_p1_mac=00:00:10:00:00:02 +sw1_p1_ip=10.0.0.2 +sw2_p1_mac=00:00:20:00:00:02 +sw2_p1_ip=20.0.0.2 + +ovn-nbctl lrp-add ro ro-sw1 $sw1_ro_mac ${sw1_ro_ip}/8 +ovn-nbctl lrp-add ro ro-sw2 $sw2_ro_mac ${sw2_ro_ip}/8 +ovn-nbctl lsp-add sw1 sw1-ro -- set Logical_Switch_Port sw1-ro type=router \ + options:router-port=ro-sw1 addresses=\"$sw1_ro_mac\" +ovn-nbctl lsp-add sw2 sw2-ro -- set Logical_Switch_Port sw2-ro type=router \ + options:router-port=ro-sw2 addresses=\"$sw2_ro_mac\" + +ovn-nbctl lsp-add sw1 sw1-p1 \ +-- lsp-set-addresses sw1-p1 "$sw1_p1_mac $sw1_p1_ip" + +ovn-nbctl lsp-add sw2 sw2-p1 \ +-- lsp-set-addresses sw2-p1 "$sw2_p1_mac $sw2_p1_ip" + +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 hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=sw1-p1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=sw2-p1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + +OVN_POPULATE_ARP + +sleep 1 + +packet="inport==\"sw1-p1\" && eth.src==$sw1_p1_mac && eth.dst==$sw1_ro_mac && + ip4 && ip.ttl==64 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip && + udp && udp.src==53 && udp.dst==4369" + +# Start by Sending the packet and make sure it makes it there as expected +as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" + +# Expected packet has TTL decreased by 1 +expected="eth.src==$sw2_ro_mac && eth.dst==$sw2_p1_mac && + ip4 && ip.ttl==63 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip && + udp && udp.src==53 && udp.dst==4369" +echo $expected | ovstest test-ovn expr-to-packets > expected + +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +# Stop ovn-controller on hv2 +as hv2 ovs-appctl -t ovn-controller exit + +# Now send the packet again. This time, it should not arrive. +as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" + +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +# Start ovn-controller again just so OVN_CLEANUP doesn't complain +as hv2 start_daemon ovn-controller + +OVN_CLEANUP([hv1],[hv2]) +AT_CLEANUP + +AT_SETUP([ovn -- ovn-controller restart]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +# Logical network: +# One Logical Router: ro, with two logical switches sw1 and sw2. +# sw1 is for subnet 10.0.0.0/8 +# sw2 is for subnet 20.0.0.0/8 +# sw1 has a single port bound on hv1 +# sw2 has a single port bound on hv2 + +ovn-nbctl lr-add ro +ovn-nbctl ls-add sw1 +ovn-nbctl ls-add sw2 + +sw1_ro_mac=00:00:10:00:00:01 +sw1_ro_ip=10.0.0.1 +sw2_ro_mac=00:00:20:00:00:01 +sw2_ro_ip=20.0.0.1 +sw1_p1_mac=00:00:10:00:00:02 +sw1_p1_ip=10.0.0.2 +sw2_p1_mac=00:00:20:00:00:02 +sw2_p1_ip=20.0.0.2 + +ovn-nbctl lrp-add ro ro-sw1 $sw1_ro_mac ${sw1_ro_ip}/8 +ovn-nbctl lrp-add ro ro-sw2 $sw2_ro_mac ${sw2_ro_ip}/8 +ovn-nbctl lsp-add sw1 sw1-ro -- set Logical_Switch_Port sw1-ro type=router \ + options:router-port=ro-sw1 addresses=\"$sw1_ro_mac\" +ovn-nbctl lsp-add sw2 sw2-ro -- set Logical_Switch_Port sw2-ro type=router \ + options:router-port=ro-sw2 addresses=\"$sw2_ro_mac\" + +ovn-nbctl lsp-add sw1 sw1-p1 \ +-- lsp-set-addresses sw1-p1 "$sw1_p1_mac $sw1_p1_ip" + +ovn-nbctl lsp-add sw2 sw2-p1 \ +-- lsp-set-addresses sw2-p1 "$sw2_p1_mac $sw2_p1_ip" + +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 hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=sw1-p1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=sw2-p1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + +OVN_POPULATE_ARP + +sleep 1 + +packet="inport==\"sw1-p1\" && eth.src==$sw1_p1_mac && eth.dst==$sw1_ro_mac && + ip4 && ip.ttl==64 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip && + udp && udp.src==53 && udp.dst==4369" + +# Start by Sending the packet and make sure it makes it there as expected +as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" + +# Expected packet has TTL decreased by 1 +expected="eth.src==$sw2_ro_mac && eth.dst==$sw2_p1_mac && + ip4 && ip.ttl==63 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip && + udp && udp.src==53 && udp.dst==4369" +echo $expected | ovstest test-ovn expr-to-packets > expected + +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +# Stop ovn-controller on hv2 with --restart flag +as hv2 ovs-appctl -t ovn-controller exit --restart + +# Now send the packet again. This time, it should still arrive +as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" + +cat expected expected > expected2 + +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected2]) + +# Start ovn-controller again just so OVN_CLEANUP doesn't complain +as hv2 start_daemon ovn-controller + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP -- 2.14.4 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev