diff options
-rw-r--r-- | include/openvswitch/list.h | 6 | ||||
-rw-r--r-- | lib/ovsdb-idl.c | 5 | ||||
-rw-r--r-- | lib/ovsdb-idl.h | 10 | ||||
-rw-r--r-- | ovn/controller/chassis.c | 15 | ||||
-rw-r--r-- | ovn/controller/chassis.h | 5 | ||||
-rw-r--r-- | ovn/controller/ofctrl.c | 163 | ||||
-rw-r--r-- | ovn/controller/ofctrl.h | 5 | ||||
-rw-r--r-- | ovn/controller/ovn-controller.c | 19 | ||||
-rw-r--r-- | ovn/northd/ovn-northd.c | 85 | ||||
-rw-r--r-- | ovn/ovn-architecture.7.xml | 74 | ||||
-rw-r--r-- | ovn/ovn-nb.ovsschema | 14 | ||||
-rw-r--r-- | ovn/ovn-nb.xml | 41 | ||||
-rw-r--r-- | ovn/ovn-sb.ovsschema | 13 | ||||
-rw-r--r-- | ovn/ovn-sb.xml | 34 | ||||
-rw-r--r-- | ovn/utilities/ovn-nbctl.8.xml | 38 | ||||
-rw-r--r-- | ovn/utilities/ovn-nbctl.c | 86 | ||||
-rw-r--r-- | ovn/utilities/ovn-sbctl.8.in | 4 | ||||
-rw-r--r-- | ovn/utilities/ovn-sbctl.c | 21 | ||||
-rw-r--r-- | tests/ofproto-macros.at | 4 | ||||
-rw-r--r-- | tests/ovn-nbctl.at | 1 | ||||
-rwxr-xr-x | tutorial/ovs-sandbox | 5 | ||||
-rwxr-xr-x | utilities/ovs-sim.in | 5 |
22 files changed, 593 insertions, 60 deletions
diff --git a/include/openvswitch/list.h b/include/openvswitch/list.h index ea5b3dbbb..2bc294c5f 100644 --- a/include/openvswitch/list.h +++ b/include/openvswitch/list.h @@ -80,6 +80,12 @@ static inline bool ovs_list_is_short(const struct ovs_list *); for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER); \ &(ITER)->MEMBER != (LIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) +#define LIST_FOR_EACH_REVERSE_SAFE(ITER, PREV, MEMBER, LIST) \ + for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER); \ + (&(ITER)->MEMBER != (LIST) \ + ? INIT_CONTAINER(PREV, (ITER)->MEMBER.prev, MEMBER), 1 \ + : 0); \ + (ITER) = (PREV)) #define LIST_FOR_EACH_REVERSE_CONTINUE(ITER, MEMBER, LIST) \ for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER); \ &(ITER)->MEMBER != (LIST); \ diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c index 15002fa5a..d70fb106b 100644 --- a/lib/ovsdb-idl.c +++ b/lib/ovsdb-idl.c @@ -3707,12 +3707,16 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) /* If the database has already changed since we started the * commit, re-evaluate it immediately to avoid missing a change * for a while. */ + loop->cur_cfg = loop->next_cfg; if (ovsdb_idl_get_seqno(loop->idl) != loop->precommit_seqno) { poll_immediate_wake(); } break; case TXN_UNCHANGED: + loop->cur_cfg = loop->next_cfg; + break; + case TXN_ABORTED: case TXN_NOT_LOCKED: case TXN_ERROR: @@ -3721,7 +3725,6 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); - } ovsdb_idl_txn_destroy(txn); loop->committing_txn = NULL; diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h index c08e31bbf..e25bfeffa 100644 --- a/lib/ovsdb-idl.h +++ b/lib/ovsdb-idl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. +/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -295,6 +295,14 @@ struct ovsdb_idl_loop { unsigned int precommit_seqno; struct ovsdb_idl_txn *open_txn; + + /* These members allow a client a simple, stateless way to keep track of + * transactions that commit: when a transaction commits successfully, + * ovsdb_idl_loop_commit_and_wait() copies 'next_cfg' to 'cur_cfg'. Thus, + * the client can set 'next_cfg' to a value that indicates a successful + * commit and check 'cur_cfg' on each iteration. */ + int64_t cur_cfg; + int64_t next_cfg; }; #define OVSDB_IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) } diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c index 502e74d50..a1545ecdd 100644 --- a/ovn/controller/chassis.c +++ b/ovn/controller/chassis.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Nicira, Inc. +/* Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,11 +64,13 @@ get_bridge_mappings(const struct smap *ext_ids) return bridge_mappings ? bridge_mappings : ""; } -void +/* Returns this chassis's Chassis record, if it is available and is currently + * amenable to a transaction. */ +const struct sbrec_chassis * chassis_run(struct controller_ctx *ctx, const char *chassis_id) { if (!ctx->ovnsb_idl_txn) { - return; + return NULL; } const struct ovsrec_open_vswitch *cfg; @@ -78,14 +80,14 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) cfg = ovsrec_open_vswitch_first(ctx->ovs_idl); if (!cfg) { VLOG_INFO("No Open_vSwitch row defined."); - return; + return NULL; } encap_type = smap_get(&cfg->external_ids, "ovn-encap-type"); encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip"); if (!encap_type || !encap_ip) { VLOG_INFO("Need to specify an encap type and ip"); - return; + return NULL; } char *tokstr = xstrdup(encap_type); @@ -144,7 +146,7 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) if (same) { /* Nothing changed. */ inited = true; - return; + return chassis_rec; } else if (!inited) { struct ds cur_encaps = DS_EMPTY_INITIALIZER; for (int i = 0; i < chassis_rec->n_encaps; i++) { @@ -190,6 +192,7 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) free(encaps); inited = true; + return chassis_rec; } /* Returns true if the database is all cleaned up, false if more work is diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h index 26017d06c..a14da1cf3 100644 --- a/ovn/controller/chassis.h +++ b/ovn/controller/chassis.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Nicira, Inc. +/* Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ struct ovsdb_idl; struct ovsrec_bridge; void chassis_register_ovs_idl(struct ovsdb_idl *); -void chassis_run(struct controller_ctx *, const char *chassis_id); +const struct sbrec_chassis *chassis_run(struct controller_ctx *, + const char *chassis_id); bool chassis_cleanup(struct controller_ctx *, const char *chassis_id); #endif /* ovn/chassis.h */ diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c index f0451b7c1..95f84d18a 100644 --- a/ovn/controller/ofctrl.c +++ b/ovn/controller/ofctrl.c @@ -68,14 +68,9 @@ static char *ovn_flow_to_string(const struct ovn_flow *); static void ovn_flow_log(const struct ovn_flow *, const char *action); static void ovn_flow_destroy(struct ovn_flow *); -static ovs_be32 queue_msg(struct ofpbuf *); -static void queue_flow_mod(struct ofputil_flow_mod *); - /* OpenFlow connection to the switch. */ static struct rconn *swconn; -static void queue_group_mod(struct ofputil_group_mod *); - /* Last seen sequence number for 'swconn'. When this differs from * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ static unsigned int seqno; @@ -93,6 +88,30 @@ enum ofctrl_state { #undef STATE }; +/* An in-flight update to the switch's flow table. + * + * When we receive a barrier reply from the switch with the given 'xid', we + * know that the switch is caught up to northbound database sequence number + * 'nb_cfg' (and make that available to the client via ofctrl_get_cur_cfg(), so + * that it can store it into our Chassis record's nb_cfg column). */ +struct ofctrl_flow_update { + struct ovs_list list_node; /* In 'flow_updates'. */ + ovs_be32 xid; /* OpenFlow transaction ID for barrier. */ + int64_t nb_cfg; /* Northbound database sequence number. */ +}; + +static struct ofctrl_flow_update * +ofctrl_flow_update_from_list_node(const struct ovs_list *list_node) +{ + return CONTAINER_OF(list_node, struct ofctrl_flow_update, list_node); +} + +/* Currently in-flight updates. */ +static struct ovs_list flow_updates; + +/* nb_cfg of latest committed flow update. */ +static int64_t cur_cfg; + /* Current state. */ static enum ofctrl_state state; @@ -116,10 +135,14 @@ static struct group_table *groups; * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */ static enum mf_field_id mff_ovn_geneve; +static ovs_be32 queue_msg(struct ofpbuf *); + static void ovn_flow_table_destroy(void); +static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *); static void ovn_group_table_clear(struct group_table *group_table, bool existing); +static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *); static void ofctrl_recv(const struct ofp_header *, enum ofptype); @@ -132,6 +155,7 @@ ofctrl_init(void) swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); tx_counter = rconn_packet_counter_create(); hmap_init(&installed_flows); + ovs_list_init(&flow_updates); } /* S_NEW, for a new connection. @@ -330,16 +354,17 @@ run_S_CLEAR_FLOWS(void) .table_id = OFPTT_ALL, .command = OFPFC_DELETE, }; - queue_flow_mod(&fm); + queue_msg(encode_flow_mod(&fm)); VLOG_DBG("clearing all flows"); + /* Send a group_mod to delete all groups. */ struct ofputil_group_mod gm; memset(&gm, 0, sizeof gm); gm.command = OFPGC11_DELETE; gm.group_id = OFPG_ALL; gm.command_bucket_id = OFPG15_BUCKET_ALL; ovs_list_init(&gm.buckets); - queue_group_mod(&gm); + queue_msg(encode_group_mod(&gm)); ofputil_bucket_list_destroy(&gm.buckets); /* Clear installed_flows, to match the state of the switch. */ @@ -350,6 +375,13 @@ run_S_CLEAR_FLOWS(void) ovn_group_table_clear(groups, true); } + /* All flow updates are irrelevant now. */ + struct ofctrl_flow_update *fup, *next; + LIST_FOR_EACH_SAFE (fup, next, list_node, &flow_updates) { + ovs_list_remove(&fup->list_node); + free(fup); + } + state = S_UPDATE_FLOWS; } @@ -378,7 +410,19 @@ run_S_UPDATE_FLOWS(void) static void recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type) { - ofctrl_recv(oh, type); + if (type == OFPTYPE_BARRIER_REPLY && !ovs_list_is_empty(&flow_updates)) { + struct ofctrl_flow_update *fup = ofctrl_flow_update_from_list_node( + ovs_list_front(&flow_updates)); + if (fup->xid == oh->xid) { + if (fup->nb_cfg >= cur_cfg) { + cur_cfg = fup->nb_cfg; + } + ovs_list_remove(&fup->list_node); + free(fup); + } + } else { + ofctrl_recv(oh, type); + } } /* Runs the OpenFlow state machine against 'br_int', which is local to the @@ -476,6 +520,12 @@ ofctrl_destroy(void) ovn_flow_table_destroy(); rconn_packet_counter_destroy(tx_counter); } + +int64_t +ofctrl_get_cur_cfg(void) +{ + return cur_cfg; +} static ovs_be32 queue_msg(struct ofpbuf *msg) @@ -765,15 +815,21 @@ ovn_flow_table_destroy(void) /* Flow table update. */ -static void -queue_flow_mod(struct ofputil_flow_mod *fm) +static struct ofpbuf * +encode_flow_mod(struct ofputil_flow_mod *fm) { fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_ANY; fm->out_group = OFPG_ANY; - queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM)); + return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM); } +static void +add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs) +{ + struct ofpbuf *msg = encode_flow_mod(fm); + ovs_list_push_back(msgs, &msg->list_node); +} /* group_table. */ @@ -811,10 +867,17 @@ ovn_group_table_clear(struct group_table *group_table, bool existing) } } +static struct ofpbuf * +encode_group_mod(const struct ofputil_group_mod *gm) +{ + return ofputil_encode_group_mod(OFP13_VERSION, gm); +} + static void -queue_group_mod(struct ofputil_group_mod *gm) +add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs) { - queue_msg(ofputil_encode_group_mod(OFP13_VERSION, gm)); + struct ofpbuf *msg = encode_group_mod(gm); + ovs_list_push_back(msgs, &msg->list_node); } @@ -829,7 +892,7 @@ queue_group_mod(struct ofputil_group_mod *gm) * * This should be called after ofctrl_run() within the main loop. */ void -ofctrl_put(struct group_table *group_table) +ofctrl_put(struct group_table *group_table, int64_t nb_cfg) { if (!groups) { groups = group_table; @@ -845,6 +908,9 @@ ofctrl_put(struct group_table *group_table) return; } + /* OpenFlow messages to send to the switch to bring it up-to-date. */ + struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs); + /* Iterate through all the desired groups. If there are new ones, * add them to the switch. */ struct group_info *desired; @@ -862,7 +928,7 @@ ofctrl_put(struct group_table *group_table) ds_cstr(&group_string), &usable_protocols); if (!error) { - queue_group_mod(&gm); + add_group_mod(&gm, &msgs); } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_ERR_RL(&rl, "new group %s %s", error, @@ -890,7 +956,7 @@ ofctrl_put(struct group_table *group_table) .table_id = i->table_id, .command = OFPFC_DELETE_STRICT, }; - queue_flow_mod(&fm); + add_flow_mod(&fm, &msgs); ovn_flow_log(i, "removing installed"); hmap_remove(&installed_flows, &i->match_hmap_node); @@ -917,7 +983,7 @@ ofctrl_put(struct group_table *group_table) .ofpacts_len = d->ofpacts_len, .command = OFPFC_MODIFY_STRICT, }; - queue_flow_mod(&fm); + add_flow_mod(&fm, &msgs); ovn_flow_log(i, "updating installed"); /* Replace 'i''s actions by 'd''s. */ @@ -950,7 +1016,7 @@ ofctrl_put(struct group_table *group_table) .ofpacts_len = d->ofpacts_len, .command = OFPFC_ADD, }; - queue_flow_mod(&fm); + add_flow_mod(&fm, &msgs); ovn_flow_log(d, "adding installed"); /* Copy 'd' from 'flow_table' to installed_flows. */ @@ -977,7 +1043,7 @@ ofctrl_put(struct group_table *group_table) ds_cstr(&group_string), &usable_protocols); if (!error) { - queue_group_mod(&gm); + add_group_mod(&gm, &msgs); } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_ERR_RL(&rl, "Error deleting group %d: %s", @@ -1009,4 +1075,63 @@ ofctrl_put(struct group_table *group_table) free(desired); } } + + if (!ovs_list_is_empty(&msgs)) { + /* Add a barrier to the list of messages. */ + struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP13_VERSION); + const struct ofp_header *oh = barrier->data; + ovs_be32 xid = oh->xid; + ovs_list_push_back(&msgs, &barrier->list_node); + + /* Queue the messages. */ + struct ofpbuf *msg; + LIST_FOR_EACH_POP (msg, list_node, &msgs) { + queue_msg(msg); + } + + /* Track the flow update. */ + struct ofctrl_flow_update *fup, *prev; + LIST_FOR_EACH_REVERSE_SAFE (fup, prev, list_node, &flow_updates) { + if (nb_cfg < fup->nb_cfg) { + /* This ofctrl_flow_update is for a configuration later than + * 'nb_cfg'. This should not normally happen, because it means + * that 'nb_cfg' in the SB_Global table of the southbound + * database decreased, and it should normally be monotonically + * increasing. */ + VLOG_WARN("nb_cfg regressed from %"PRId64" to %"PRId64, + fup->nb_cfg, nb_cfg); + ovs_list_remove(&fup->list_node); + free(fup); + } else if (nb_cfg == fup->nb_cfg) { + /* This ofctrl_flow_update is for the same configuration as + * 'nb_cfg'. Probably, some change to the physical topology + * means that we had to revise the OpenFlow flow table even + * though the logical topology did not change. Update fp->xid, + * so that we don't send a notification that we're up-to-date + * until we're really caught up. */ + VLOG_DBG("advanced xid target for nb_cfg=%"PRId64, nb_cfg); + fup->xid = xid; + goto done; + } else { + break; + } + } + + /* Add a flow update. */ + fup = xmalloc(sizeof *fup); + ovs_list_push_back(&flow_updates, &fup->list_node); + fup->xid = xid; + fup->nb_cfg = nb_cfg; + done:; + } else if (!ovs_list_is_empty(&flow_updates)) { + /* Getting up-to-date with 'nb_cfg' didn't require any extra flow table + * changes, so whenever we get up-to-date with the most recent flow + * table update, we're also up-to-date with 'nb_cfg'. */ + struct ofctrl_flow_update *fup = ofctrl_flow_update_from_list_node( + ovs_list_back(&flow_updates)); + fup->nb_cfg = nb_cfg; + } else { + /* We were completely up-to-date before and still are. */ + cur_cfg = nb_cfg; + } } diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h index 49b95b0fd..befae0175 100644 --- a/ovn/controller/ofctrl.h +++ b/ovn/controller/ofctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Nicira, Inc. +/* Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,9 +32,10 @@ struct group_table; /* Interface for OVN main loop. */ void ofctrl_init(void); enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int); -void ofctrl_put(struct group_table *group_table); +void ofctrl_put(struct group_table *group_table, int64_t nb_cfg); void ofctrl_wait(void); void ofctrl_destroy(void); +int64_t ofctrl_get_cur_cfg(void); struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source); diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 4d9490a5a..ecf13063f 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -299,6 +299,13 @@ update_ct_zones(struct sset *lports, struct hmap *patched_datapaths, sset_destroy(&all_users); } +static int64_t +get_nb_cfg(struct ovsdb_idl *idl) +{ + const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl); + return sb ? sb->nb_cfg : 0; +} + /* Contains "struct local_datapath" nodes whose hash values are the * tunnel_key of datapaths with at least one local port binding. */ static struct hmap local_datapaths = HMAP_INITIALIZER(&local_datapaths); @@ -378,6 +385,7 @@ main(int argc, char *argv[]) char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl); struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true)); + ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg); /* Track the southbound idl. */ ovsdb_idl_track_add_all(ovnsb_idl_loop.idl); @@ -422,8 +430,9 @@ main(int argc, char *argv[]) const struct ovsrec_bridge *br_int = get_br_int(&ctx); const char *chassis_id = get_chassis_id(ctx.ovs_idl); + const struct sbrec_chassis *chassis = NULL; if (chassis_id) { - chassis_run(&ctx, chassis_id); + chassis = chassis_run(&ctx, chassis_id); encaps_run(&ctx, br_int, chassis_id); binding_run(&ctx, br_int, chassis_id, &local_datapaths); } @@ -448,7 +457,13 @@ main(int argc, char *argv[]) br_int, chassis_id, &ct_zones, &local_datapaths, &patched_datapaths); - ofctrl_put(&group_table); + ofctrl_put(&group_table, get_nb_cfg(ctx.ovnsb_idl)); + if (ctx.ovnsb_idl_txn) { + int64_t cur_cfg = ofctrl_get_cur_cfg(); + if (cur_cfg && cur_cfg != chassis->nb_cfg) { + sbrec_chassis_set_nb_cfg(chassis, cur_cfg); + } + } } sset_destroy(&all_lports); diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 17fbf29c5..a83688192 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -3171,9 +3171,9 @@ sync_address_sets(struct northd_context *ctx) } static void -ovnnb_db_run(struct northd_context *ctx) +ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop) { - if (!ctx->ovnsb_txn) { + if (!ctx->ovnsb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnnb_idl)) { return; } struct hmap datapaths, ports; @@ -3194,19 +3194,24 @@ ovnnb_db_run(struct northd_context *ctx) ovn_port_destroy(&ports, port); } hmap_destroy(&ports); + + /* Copy nb_cfg from northbound to southbound database. + * + * Also set up to update sb_cfg once our southbound transaction commits. */ + const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl); + const struct sbrec_sb_global *sb = sbrec_sb_global_first(ctx->ovnsb_idl); + if (nb && sb) { + sbrec_sb_global_set_nb_cfg(sb, nb->nb_cfg); + sb_loop->next_cfg = nb->nb_cfg; + } } -/* - * The only change we get notified about is if the 'chassis' column of the - * 'Port_Binding' table changes. When this column is not empty, it means we - * need to set the corresponding logical port as 'up' in the northbound DB. - */ +/* Handle changes to the 'chassis' column of the 'Port_Binding' table. When + * this column is not empty, it means we need to set the corresponding logical + * port as 'up' in the northbound DB. */ static void -ovnsb_db_run(struct northd_context *ctx) +update_logical_port_status(struct northd_context *ctx) { - if (!ctx->ovnnb_txn) { - return; - } struct hmap lports_hmap; const struct sbrec_port_binding *sb; const struct nbrec_logical_switch_port *nbsp; @@ -3256,7 +3261,6 @@ ovnsb_db_run(struct northd_context *ctx) } hmap_destroy(&lports_hmap); } - static struct dhcp_opts_map supported_dhcp_opts[] = { OFFERIP, @@ -3318,6 +3322,48 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx) hmap_destroy(&dhcp_opts_to_add); } +/* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */ +static void +update_northbound_cfg(struct northd_context *ctx, + struct ovsdb_idl_loop *sb_loop) +{ + /* Update northbound sb_cfg if appropriate. */ + const struct nbrec_nb_global *nbg = nbrec_nb_global_first(ctx->ovnnb_idl); + int64_t sb_cfg = sb_loop->cur_cfg; + if (nbg && sb_cfg && nbg->sb_cfg != sb_cfg) { + nbrec_nb_global_set_sb_cfg(nbg, sb_cfg); + } + + /* Update northbound hv_cfg if appropriate. */ + if (nbg) { + /* Find minimum nb_cfg among all chassis. */ + const struct sbrec_chassis *chassis; + int64_t hv_cfg = nbg->nb_cfg; + SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) { + if (chassis->nb_cfg < hv_cfg) { + hv_cfg = chassis->nb_cfg; + } + } + + /* Update hv_cfg. */ + if (nbg->hv_cfg != hv_cfg) { + nbrec_nb_global_set_hv_cfg(nbg, hv_cfg); + } + } +} + +/* Handle a fairly small set of changes in the southbound database. */ +static void +ovnsb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop) +{ + if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) { + return; + } + + update_logical_port_status(ctx); + update_northbound_cfg(ctx, sb_loop); +} + static char *default_nb_db_; static const char * @@ -3443,13 +3489,19 @@ main(int argc, char *argv[]) nbrec_init(); sbrec_init(); - /* We want to detect all changes to the ovn-nb db. */ + /* We want to detect (almost) all changes to the ovn-nb db. */ struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true)); + ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_sb_cfg); + ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_hv_cfg); + /* We want to detect only selected changes to the ovn-sb db. */ struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true)); + ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global); + add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg); + ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_logical_datapath); @@ -3495,6 +3547,9 @@ main(int argc, char *argv[]) add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses); + ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis); + ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg); + /* Main loop. */ exiting = false; while (!exiting) { @@ -3505,8 +3560,8 @@ main(int argc, char *argv[]) .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; - ovnnb_db_run(&ctx); - ovnsb_db_run(&ctx); + ovnnb_db_run(&ctx, &ovnsb_idl_loop); + ovnsb_db_run(&ctx, &ovnsb_idl_loop); if (ctx.ovnsb_txn) { check_and_add_supported_dhcp_opts_to_sb_db(&ctx); } diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml index ead4feb71..fe20a14ef 100644 --- a/ovn/ovn-architecture.7.xml +++ b/ovn/ovn-architecture.7.xml @@ -200,6 +200,80 @@ +-------------------------------+ +-------------------------------+ </pre> + <h2>Information Flow in OVN</h2> + + <p> + Configuration data in OVN flows from north to south. The CMS, through its + OVN/CMS plugin, passes the logical network configuration to + <code>ovn-northd</code> via the northbound database. In turn, + <code>ovn-northd</code> compiles the configuration into a lower-level form + and passes it to all of the chassis via the southbound database. + </p> + + <p> + Status information in OVN flows from south to north. OVN currently + provides only a few forms of status information. First, + <code>ovn-northd</code> populates the <code>up</code> column in the + northbound <code>Logical_Switch_Port</code> table: if a logical port's + <code>chassis</code> column in the southbound <code>Port_Binding</code> + table is nonempty, it sets <code>up</code> to <code>true</code>, otherwise + to <code>false</code>. This allows the CMS to detect when a VM's + networking has come up. + </p> + + <p> + Second, OVN provides feedback to the CMS on the realization of its + configuration, that is, whether the configuration provided by the CMS has + taken effect. This feature requires the CMS to participate in a sequence + number protocol, which works the following way: + </p> + + <ol> + <li> + When the CMS updates the configuration in the northbound database, as + part of the same transaction, it increments the value of the + <code>nb_cfg</code> column in the <code>NB_Global</code> table. (This is + only necessary if the CMS wants to know when the configuration has been + realized.) + </li> + + <li> + When <code>ovn-northd</code> updates the southbound database based on a + given snapshot of the northbound database, it copies <code>nb_cfg</code> + from northbound <code>NB_Global</code> into the southbound database + <code>SB_Global</code> table, as part of the same transaction. (Thus, an + observer monitoring both databases can determine when the southbound + database is caught up with the northbound.) + </li> + + <li> + After <code>ovn-northd</code> receives confirmation from the southbound + database server that its changes have committed, it updates + <code>sb_cfg</code> in the northbound <code>NB_Global</code> table to the + <code>nb_cfg</code> version that was pushed down. (Thus, the CMS or + another observer can determine when the southbound database is caught up + without a connection to the southbound database.) + </li> + + <li> + The <code>ovn-controller</code> process on each chassis receives the + updated southbound database, with the updated <code>nb_cfg</code>. This + process in turn updates the physical flows installed in the chassis's + Open vSwitch instances. When it receives confirmation from Open vSwitch + that the physical flows have been updated, it updates <code>nb_cfg</code> + in its own <code>Chassis</code> record in the southbound database. + </li> + + <li> + <code>ovn-northd</code> monitors the <code>nb_cfg</code> column in all of + the <code>Chassis</code> records in the southbound database. It keeps + track of the minimum value among all the records and copies it into the + <code>hv_cfg</code> column in the northbound <code>NB_Global</code> + table. (Thus, the CMS or another observer can determine when all of the + hypervisors have caught up to the northbound configuration.) + </li> + </ol> + <h2>Chassis Setup</h2> <p> diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema index 3cf07c19c..a5dc66947 100644 --- a/ovn/ovn-nb.ovsschema +++ b/ovn/ovn-nb.ovsschema @@ -1,8 +1,18 @@ { "name": "OVN_Northbound", - "version": "5.1.0", - "cksum": "2201958537 8295", + "version": "5.2.0", + "cksum": "650844440 8727", "tables": { + "NB_Global": { + "columns": { + "nb_cfg": {"type": {"key": "integer"}}, + "sb_cfg": {"type": {"key": "integer"}}, + "hv_cfg": {"type": {"key": "integer"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "maxRows": 1, + "isRoot": true}, "Logical_Switch": { "columns": { "name": {"type": "string"}, diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index abd034045..4b61bbc76 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -30,6 +30,47 @@ </dd> </dl> + <table name="NB_Global" title="Northbound configuration"> + <p> + Northbound configuration for an OVN system. This table must have exactly + one row. + </p> + + <group title="Status"> + These columns allow a client to track the overall configuration state of + the system. + + <column name="nb_cfg"> + Sequence number for client to increment. When a client modifies any + part of the northbound database configuration and wishes to wait for + <code>ovn-northd</code> and possibly all of the hypervisors to finish + applying the changes, it may increment this sequence number. + </column> + + <column name="sb_cfg"> + Sequence number that <code>ovn-northd</code> sets to the value of <ref + column="nb_cfg"/> after it finishes applying the corresponding + configuration changes to the <ref db="OVN_Southbound"/> database. + </column> + + <column name="hv_cfg"> + Sequence number that <code>ovn-northd</code> sets to the smallest + sequence number of all the chassis in the system, as reported in the + <code>Chassis</code> table in the southbound database. Thus, <ref + column="hv_cfg"/> equals <ref column="nb_cfg"/> if all chassis are + caught up with the northbound configuration (which may never happen, if + any chassis is down). This value can regress, if a chassis was removed + from the system and rejoins before catching up. + </column> + </group> + + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + </group> + </table> + <table name="Logical_Switch" title="L2 logical switch"> <p> Each row represents one L2 logical switch. diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema index 605b60588..b1737f560 100644 --- a/ovn/ovn-sb.ovsschema +++ b/ovn/ovn-sb.ovsschema @@ -1,8 +1,16 @@ { "name": "OVN_Southbound", - "version": "1.6.0", - "cksum": "1715817174 6541", + "version": "1.7.0", + "cksum": "3677179333 6917", "tables": { + "SB_Global": { + "columns": { + "nb_cfg": {"type": {"key": "integer"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "maxRows": 1, + "isRoot": true}, "Chassis": { "columns": { "name": {"type": "string"}, @@ -13,6 +21,7 @@ "vtep_logical_switches" : {"type": {"key": "string", "min": 0, "max": "unlimited"}}, + "nb_cfg": {"type": {"key": "integer"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 3d26e6531..eac5f7695 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -137,6 +137,33 @@ </dd> </dl> + <table name="SB_Global" title="Southbound configuration"> + <p> + Southbound configuration for an OVN system. This table must have exactly + one row. + </p> + + <group title="Status"> + This column allow a client to track the overall configuration state of + the system. + + <column name="nb_cfg"> + Sequence number for the configuration. When a CMS or + <code>ovn-nbctl</code> updates the northbound database, it increments + the <code>nb_cfg</code> column in the <code>NB_Global</code> table in + the northbound database. In turn, when <code>ovn-northd</code> updates + the southbound database to bring it up to date with these changes, it + updates this column to the same value. + </column> + </group> + + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + </group> + </table> + <table name="Chassis" title="Physical Network Hypervisor and Gateway Information"> <p> Each row in this table represents a hypervisor or gateway (a chassis) in @@ -172,6 +199,13 @@ ovn-controller-vtep will leave this column empty. </column> + <column name="nb_cfg"> + Sequence number for the configuration. When <code>ovn-controller</code> + updates the configuration of a chassis from the contents of the + southbound database, it copies <ref table="SB_Global" column="nb_cfg"/> + from the <ref table="SB_Global"/> table into this column. + </column> + <column name="external_ids" key="ovn-bridge-mappings"> <code>ovn-controller</code> populates this key with the set of bridge mappings it has been configured to use. Other applications should treat diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index 9d58bc50e..122a11410 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -12,6 +12,12 @@ <h1>General Commands</h1> <dl> + <dt><code>init</code></dt> + <dd> + Initializes the database, if it is empty. If the database has already + been initialized, this command has no effect. + </dd> + <dt><code>show [<var>switch</var> | <var>router</var>]</code></dt> <dd> Prints a brief overview of the database contents. If @@ -472,6 +478,38 @@ <h1>Options</h1> <dl> + <dt><code>--no-wait</code> | <code>--wait=none</code></dt> + <dt><code>--wait=sb</code></dt> + <dt><code>--wait=hv</code></dt> + + <dd> + <p> + These options control whether and how <code>ovn-nbctl</code> waits + for the OVN system to become up-to-date with changes made in an + <code>ovn-nbctl</code> invocation. + </p> + + <p> + By default, or if <code>--no-wait</code> or <code>--wait=none</code>, + <code>ovn-nbctl</code> exits immediately after confirming that + changes have been committed to the northbound database, without + waiting. + </p> + + <p> + With <code>--wait=sb</code>, before <code>ovn-nbctl</code> exits, it + waits for <code>ovn-northd</code> to bring the southbound database + up-to-date with the northbound database updates. + </p> + + <p> + With <code>--wait=hv</code>, before <code>ovn-nbctl</code> exits, it + additionally waits for all OVN chassis (hypervisors and gateways) to + become up-to-date with the northbound database updates. (This can + become an indefinite wait if any chassis is malfunctioning.) + </p> + </dd> + <dt><code>--db</code> <var>database</var></dt> <dd> The OVSDB database remote to contact. If the <env>OVN_NB_DB</env> diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index d34a92ced..e594a3238 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -49,6 +49,14 @@ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; +/* --wait=TYPE: Wait for configuration change to take effect? */ +enum nbctl_wait_type { + NBCTL_WAIT_NONE, /* Do not wait. */ + NBCTL_WAIT_SB, /* Wait for southbound database updates. */ + NBCTL_WAIT_HV /* Wait for hypervisors to catch up. */ +}; +static enum nbctl_wait_type wait_type = NBCTL_WAIT_NONE; + /* --timeout: Time to wait for a connection to 'db'. */ static int timeout; @@ -160,6 +168,8 @@ parse_options(int argc, char *argv[], struct shash *local_options) enum { OPT_DB = UCHAR_MAX + 1, OPT_NO_SYSLOG, + OPT_NO_WAIT, + OPT_WAIT, OPT_DRY_RUN, OPT_ONELINE, OPT_LOCAL, @@ -171,6 +181,8 @@ parse_options(int argc, char *argv[], struct shash *local_options) static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, + {"no-wait", no_argument, NULL, OPT_NO_WAIT}, + {"wait", required_argument, NULL, OPT_WAIT}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, @@ -227,6 +239,23 @@ parse_options(int argc, char *argv[], struct shash *local_options) vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); break; + case OPT_NO_WAIT: + wait_type = NBCTL_WAIT_NONE; + break; + + case OPT_WAIT: + if (!strcmp(optarg, "none")) { + wait_type = NBCTL_WAIT_NONE; + } else if (!strcmp(optarg, "sb")) { + wait_type = NBCTL_WAIT_SB; + } else if (!strcmp(optarg, "hv")) { + wait_type = NBCTL_WAIT_HV; + } else { + ctl_fatal("argument to --wait must be " + "\"none\", \"sb\", or \"hv\""); + } + break; + case OPT_DRY_RUN: dry_run = true; break; @@ -294,6 +323,7 @@ usage(void) usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ General commands:\n\ + init initialize the database\n\ show print overview of database contents\n\ show SWITCH print overview of database contents for SWITCH\n\ show ROUTER print overview of database contents for ROUTER\n\ @@ -381,6 +411,9 @@ DHCP Options commands:\n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ + --no-wait, --wait=none do not wait for OVN reconfiguration (default)\n\ + --wait=sb wait for southbound database update\n\ + --wait=hv wait for all chassis to catch up\n\ -t, --timeout=SECS wait at most SECS seconds\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", @@ -521,6 +554,11 @@ print_ls(const struct nbrec_logical_switch *ls, struct ds *s) } static void +nbctl_init(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static void nbctl_show(struct ctl_context *ctx) { const struct nbrec_logical_switch *ls; @@ -2064,6 +2102,10 @@ nbctl_lr_route_list(struct ctl_context *ctx) } static const struct ctl_table_class tables[] = { + {&nbrec_table_nb_global, + {{&nbrec_table_nb_global, NULL, NULL}, + {NULL, NULL, NULL}}}, + {&nbrec_table_logical_switch, {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL}, {NULL, NULL, NULL}}}, @@ -2116,9 +2158,14 @@ static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { - struct ctl_command *c; + ovsdb_idl_add_table(idl, &nbrec_table_nb_global); + if (wait_type == NBCTL_WAIT_SB) { + ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg); + } else if (wait_type == NBCTL_WAIT_HV) { + ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg); + } - for (c = commands; c < &commands[n_commands]; c++) { + for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct ctl_context ctx; @@ -2145,6 +2192,7 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ctl_context ctx; struct ctl_command *c; struct shash_node *node; + int64_t next_cfg = 0; char *error = NULL; txn = the_idl_txn = ovsdb_idl_txn_create(idl); @@ -2154,6 +2202,17 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args); + const struct nbrec_nb_global *nb = nbrec_nb_global_first(idl); + if (!nb) { + /* XXX add verification that table is empty */ + nb = nbrec_nb_global_insert(txn); + } + + if (wait_type != NBCTL_WAIT_NONE) { + ovsdb_idl_txn_increment(txn, &nb->header_, + &nbrec_nb_global_col_nb_cfg); + } + symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); @@ -2195,6 +2254,9 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, } status = ovsdb_idl_txn_commit_block(txn); + if (wait_type != NBCTL_WAIT_NONE && status == TXN_SUCCESS) { + next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); + } if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { @@ -2271,6 +2333,25 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, shash_destroy_free_data(&c->options); } free(commands); + + if (wait_type != NBCTL_WAIT_NONE && status != TXN_UNCHANGED) { + ovsdb_idl_enable_reconnect(idl); + for (;;) { + ovsdb_idl_run(idl); + NBREC_NB_GLOBAL_FOR_EACH (nb, idl) { + int64_t cur_cfg = (wait_type == NBCTL_WAIT_SB + ? nb->sb_cfg + : nb->hv_cfg); + if (cur_cfg >= next_cfg) { + goto done; + } + } + ovsdb_idl_wait(idl); + poll_block(); + } + done: ; + } + ovsdb_idl_txn_destroy(txn); ovsdb_idl_destroy(idl); @@ -2312,6 +2393,7 @@ nbctl_exit(int status) } static const struct ctl_command_syntax nbctl_commands[] = { + { "init", 0, 0, "", NULL, nbctl_init, NULL, "", RW }, { "show", 0, 1, "[SWITCH]", NULL, nbctl_show, NULL, "", RO }, /* logical switch commands. */ diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in index 8203771ab..5f0462aa5 100644 --- a/ovn/utilities/ovn-sbctl.8.in +++ b/ovn/utilities/ovn-sbctl.8.in @@ -108,6 +108,10 @@ sections below. .SS "OVN_Southbound Commands" These commands work with an \fBOVN_Southbound\fR database as a whole. . +.IP "\fBinit\fR" +Initializes the database, if it is empty. If the database has already +been initialized, this command has no effect. +. .IP "\fBshow\fR" Prints a brief overview of the database contents. . diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c index 85dab067c..936915b3a 100644 --- a/ovn/utilities/ovn-sbctl.c +++ b/ovn/utilities/ovn-sbctl.c @@ -526,6 +526,11 @@ static struct cmd_show_table cmd_show_tables[] = { }; static void +sbctl_init(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static void cmd_chassis_add(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); @@ -744,6 +749,10 @@ cmd_lflow_list(struct ctl_context *ctx) static const struct ctl_table_class tables[] = { + {&sbrec_table_sb_global, + {{&sbrec_table_sb_global, NULL, NULL}, + {NULL, NULL, NULL}}}, + {&sbrec_table_chassis, {{&sbrec_table_chassis, &sbrec_chassis_col_name, NULL}, {NULL, NULL, NULL}}}, @@ -817,9 +826,9 @@ static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { - struct ctl_command *c; + ovsdb_idl_add_table(idl, &sbrec_table_sb_global); - for (c = commands; c < &commands[n_commands]; c++) { + for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct sbctl_context sbctl_ctx; @@ -855,6 +864,12 @@ do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands, ovsdb_idl_txn_add_comment(txn, "ovs-sbctl: %s", args); + const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl); + if (!sb) { + /* XXX add verification that table is empty */ + sb = sbrec_sb_global_insert(txn); + } + symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); @@ -1013,6 +1028,8 @@ sbctl_exit(int status) } static const struct ctl_command_syntax sbctl_commands[] = { + { "init", 0, 0, "", NULL, sbctl_init, NULL, "", RW }, + /* Chassis commands. */ {"chassis-add", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info, cmd_chassis_add, NULL, "--may-exist", RW}, diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at index 2d65f2869..3dbf6b2f6 100644 --- a/tests/ofproto-macros.at +++ b/tests/ofproto-macros.at @@ -183,8 +183,8 @@ ovn_init_db () { # ovn-sbctl and ovn-nbctl use them by default, and starts ovn-northd running # against them. ovn_start () { - ovn_init_db ovn-sb - ovn_init_db ovn-nb + ovn_init_db ovn-sb; ovn-sbctl init + ovn_init_db ovn-nb; ovn-nbctl init echo "starting ovn-northd" mkdir "$ovs_base"/northd diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 8e8580187..5357cedda 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -9,6 +9,7 @@ m4_define([OVN_NBCTL_TEST_START], dnl Start ovsdb-server. AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/ovnnb_db.sock ovn-nb.db], [0], [], [stderr]) on_exit "kill `cat ovsdb-server.pid`" + AT_CHECK([ovn-nbctl init]) AT_CHECK([[sed < stderr ' /vlog|INFO|opened log file/d /ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']]) diff --git a/tutorial/ovs-sandbox b/tutorial/ovs-sandbox index 412c98289..69c2c689d 100755 --- a/tutorial/ovs-sandbox +++ b/tutorial/ovs-sandbox @@ -1,6 +1,6 @@ #! /bin/sh # -# Copyright (c) 2013, 2015 Nicira, Inc. +# Copyright (c) 2013, 2015, 2016 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -361,6 +361,9 @@ if $ovn; then ovs-vsctl set open . external-ids:ovn-encap-type=geneve ovs-vsctl set open . external-ids:ovn-encap-ip=127.0.0.1 + ovn-nbctl init + ovn-sbctl init + rungdb $gdb_ovn_northd $gdb_ovn_northd_ex ovn-northd --detach \ --no-chdir --pidfile -vconsole:off --log-file \ --ovnsb-db=unix:"$sandbox"/ovnsb_db.sock \ diff --git a/utilities/ovs-sim.in b/utilities/ovs-sim.in index cebbd419c..7f6081596 100755 --- a/utilities/ovs-sim.in +++ b/utilities/ovs-sim.in @@ -1,6 +1,6 @@ #! /usr/bin/env bash # -# Copyright (c) 2013, 2015 Nicira, Inc. +# Copyright (c) 2013, 2015, 2016 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -263,6 +263,9 @@ EOF OVN_NB_DB=unix:$sim_base/ovn-nb/ovn-nb.sock; export OVN_NB_DB OVN_SB_DB=unix:$sim_base/ovn-sb/ovn-sb.sock; export OVN_SB_DB + ovn-nbctl init + ovn-sbctl init + mkdir "$sim_base"/northd as northd ovn-northd $daemon_opts \ --ovnnb-db="$OVN_NB_DB" \ |