@@ -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);
}
@@ -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
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 <mmichels@redhat.com> --- ovn/controller/ovn-controller.c | 92 +++++++++++--------- tests/ovn.at | 186 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 40 deletions(-)