The big issue for me is your last comment: If you add bob2 on hv1, would bob2 be able to reach alice1?
No; take foo1 which is already on hv1 for this example; if I try to send b/w foo1 and alice1, I have “no route to alice1, the destination” because all I can use on hv1 is R1 routes. If I change the test with new permissive catchall routes everywhere, I could do it, of course, but that is something else and contrary to what you would want to do. There is a reason that I mentioned bob rather than foo. The logical connectivity is bob -- R1 -- foo and bob -- R2 -- alice Since there are no static routes configured between R1 and R2, foo has no logical connectivity to alice. There *is* logical connectivity between bob and alice through R2. With your code, you get different behavior depending on whether the endpoint in bob is on hv1 or hv2. Since bob1 and alice1 are both on hv2 along with R2, bob1 can reach alice1. If you add bob2 on hv1, bob2 cannot reach alice1. Why should you get different connectivity depending on placement of endpoints within logical switch bob? If bob is connected to R2, any endpoint anywhere in bob should be able to reach R2, and continue on to any endpoints in other logical switches (i.e. alice) connected to R2. Cutting the patch port as you did in v3 is not enough. Unless you restrict bob to the same subset of chassis as R2, I would think what you really want is to associate the patch port's port binding with hv2. If you do that, then on hv1, physical.c would add an entry in table 32 for datapath bob that would direct traffic to the patch port down a tunnel to hv2. This seems like it would be a lot simpler if each "local router" is limited to only one chassis. If a "local router" can be on multiple chassis, I start thinking about multiple possible chassis and ECMP which would add significant complexity. Mickey -----Darrell Ball <dlu...@gmail.com> wrote: ----- To: Mickey Spiegel/San Jose/IBM@IBMUS From: Darrell Ball <dlu...@gmail.com> Date: 05/09/2016 09:11PM Cc: d...@openvswitch.com Subject: Re: [ovs-dev] [patch_v1] ovn: add local router support (RFC) On Mon, May 9, 2016 at 4:40 PM, Mickey Spiegel <emspi...@us.ibm.com> wrote: I am missing something basic here. In your tests, you have logical switch bob that seems like it could be present on both hv1 and hv2, and a logical router R2 that is local to hv2 but not present on hv1. Exactly Wouldn't the logical switch bob flows on hv1 still send packets locally to the patch port rp-bob? But when the packets come back around to logical router R2 flows, they are not present on hv1? Exactly; I don’t want R2 flows processed and downloaded on HV1. Don't you need to change the patch ports so that they are not considered local if the associated logical router is not local to this chassis? Its not required. The router<->switch patch cabling is still there. I did not “cut the cable” as it were; I enabled selective router flow processing and download in this patch. The idea is to not process and download flows that are not needed on a given HV. Its probably fine to also “cut the patch cable” for the router to switch. Its pretty cheap to do that in a minimal way, so I added it for now. I really want to keep it as simple as possible and not add unnecessary code, however. If you add bob2 on hv1, would bob2 be able to reach alice1? No; take foo1 which is already on hv1 for this example; if I try to send b/w foo1 and alice1, I have “no route to alice1, the destination” because all I can use on hv1 is R1 routes. If I change the test with new permissive catchall routes everywhere, I could do it, of course, but that is something else and contrary to what you would want to do. Mickey -----"dev" <dev-boun...@openvswitch.org> wrote: ----- To: dlu...@gmail.com, d...@openvswitch.com From: Darrell Ball Sent by: "dev" Date: 05/09/2016 11:58AM Subject: [ovs-dev] [patch_v1] ovn: add local router support (RFC) This patch adds local router support. A logical router can be declared local at northbound level via external_ids. This is communicated to the southbound database via northd which populates the external_id of the datapath_binding table. ovn-controller will also allow a chassis to be configured for 1 or more local routers. When ovn-controller configures openflow rules it looks at whether a logical router is local and it so it checks where its chassis instance local routers has this particular logical router configured as local; if it does, it allows the southbound flows of the local logical router to be translated into openflow rules and programmed on this chassis. If a logical router is labelled as local but a given chassis is not configured for that logical router to be local, then the corresponding openflow rules are not programmed on this chassis. Signed-off-by: Darrell Ball <dlu...@gmail.com> --- ovn/controller/chassis.c | 32 +++++- ovn/controller/chassis.h | 4 +- ovn/controller/lflow.c | 16 ++- ovn/controller/lflow.h | 4 +- ovn/controller/ovn-controller.8.xml | 8 ++ ovn/controller/ovn-controller.c | 50 +++++++++- ovn/controller/ovn-controller.h | 4 + ovn/northd/ovn-northd.c | 17 +++- ovn/ovn-nb.xml | 6 +- ovn/ovn-sb.xml | 8 ++ tests/ovn.at | 189 ++++++++++++++++++++++++++++++++++++ 11 files changed, 324 insertions(+), 14 deletions(-) diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c index d40181b..e58145b 100644 --- a/ovn/controller/chassis.c +++ b/ovn/controller/chassis.c @@ -24,6 +24,7 @@ #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn-controller.h" +#include "sset.h" VLOG_DEFINE_THIS_MODULE(chassis); @@ -63,8 +64,16 @@ get_bridge_mappings(const struct smap *ext_ids) return bridge_mappings ? bridge_mappings : ""; } +static const char * +get_local_routers(const struct smap *ext_ids) +{ + const char *local_routers = smap_get(ext_ids, "local-routers"); + return local_routers ? local_routers : ""; +} + void -chassis_run(struct controller_ctx *ctx, const char *chassis_id) +chassis_run(struct controller_ctx *ctx, const char *chassis_id, + struct sset *local_router_datapaths) { if (!ctx->ovnsb_idl_txn) { return; @@ -112,6 +121,8 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) const char *bridge_mappings = get_bridge_mappings(&cfg->external_ids); + const char *local_routers = get_local_routers(&cfg->external_ids); + const struct sbrec_chassis *chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id); @@ -131,6 +142,17 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) smap_destroy(&new_ids); } + const char *chassis_local_routers + = get_local_routers(&chassis_rec->external_ids); + if (strcmp(local_routers, chassis_local_routers)) { + struct smap new_ids; + smap_clone(&new_ids, &chassis_rec->external_ids); + smap_replace(&new_ids, "local-router", local_routers); + sbrec_chassis_verify_external_ids(chassis_rec); + sbrec_chassis_set_external_ids(chassis_rec, &new_ids); + smap_destroy(&new_ids); + } + /* Compare desired tunnels against those currently in the database. */ uint32_t cur_tunnels = 0; bool same = true; @@ -166,12 +188,16 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) chassis_id); if (!chassis_rec) { - struct smap ext_ids = SMAP_CONST1(&ext_ids, "ovn-bridge-mappings", - bridge_mappings); + + struct smap ext_ids = SMAP_INITIALIZER(&ext_ids); + smap_add(&ext_ids, "ovn-bridge-mappings", + bridge_mappings); + smap_add(&ext_ids, "local-routers", local_routers); chassis_rec = sbrec_chassis_insert(ctx->ovnsb_idl_txn); sbrec_chassis_set_name(chassis_rec, chassis_id); sbrec_chassis_set_hostname(chassis_rec, hostname); sbrec_chassis_set_external_ids(chassis_rec, &ext_ids); + smap_destroy(&ext_ids); } int n_encaps = count_1bits(req_tunnels); diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h index 26017d0..324e3cf 100644 --- a/ovn/controller/chassis.h +++ b/ovn/controller/chassis.h @@ -21,9 +21,11 @@ struct controller_ctx; struct ovsdb_idl; struct ovsrec_bridge; +struct sset; void chassis_register_ovs_idl(struct ovsdb_idl *); -void chassis_run(struct controller_ctx *, const char *chassis_id); +void chassis_run(struct controller_ctx *, const char *chassis_id, + struct sset *); bool chassis_cleanup(struct controller_ctx *, const char *chassis_id); #endif /* ovn/chassis.h */ diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c index 96b7c66..d86646a 100644 --- a/ovn/controller/lflow.c +++ b/ovn/controller/lflow.c @@ -199,7 +199,8 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, const struct mcgroup_index *mcgroups, const struct hmap *local_datapaths, const struct hmap *patched_datapaths, - const struct simap *ct_zones, struct hmap *flow_table) + const struct simap *ct_zones, struct hmap *flow_table, + const struct sset *local_routers) { uint32_t conj_id_ofs = 1; @@ -250,6 +251,13 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, continue; } } + } else { + const char *local_router = + smap_get(&ldp->external_ids, "local-router"); + if (strcmp(local_router, "") && + !is_local_router(local_routers, local_router)) { + continue; + } } /* Determine translation of logical table IDs to physical table IDs. */ @@ -429,10 +437,12 @@ lflow_run(struct controller_ctx *ctx, const struct lport_index *lports, const struct mcgroup_index *mcgroups, const struct hmap *local_datapaths, const struct hmap *patched_datapaths, - const struct simap *ct_zones, struct hmap *flow_table) + const struct simap *ct_zones, struct hmap *flow_table, + const struct sset *local_router_datapaths) { add_logical_flows(ctx, lports, mcgroups, local_datapaths, - patched_datapaths, ct_zones, flow_table); + patched_datapaths, ct_zones, flow_table, + local_router_datapaths); add_neighbor_flows(ctx, lports, flow_table); } diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h index a3fc50c..4144481 100644 --- a/ovn/controller/lflow.h +++ b/ovn/controller/lflow.h @@ -41,6 +41,7 @@ struct lport_index; struct mcgroup_index; struct simap; struct uuid; +struct sset; /* OpenFlow table numbers. * @@ -64,7 +65,8 @@ void lflow_run(struct controller_ctx *, const struct lport_index *, const struct hmap *local_datapaths, const struct hmap *patched_datapaths, const struct simap *ct_zones, - struct hmap *flow_table); + struct hmap *flow_table, + const struct sset *local_routers); void lflow_destroy(void); #endif /* ovn/lflow.h */ diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml index 1ee3a6e..582a00b 100644 --- a/ovn/controller/ovn-controller.8.xml +++ b/ovn/controller/ovn-controller.8.xml @@ -154,6 +154,14 @@ value mapping two physical network names to two ovs bridges would be: <code>physnet1:br-eth0,physnet2:br-eth1</code>. </dd> + <dt><code>external_ids:local-routers</code></dt> + <dd> + A set of a local routers on this chassis instance. The logical flows of a + local logical router datapath are only translated into openflow + flows if the chassis is configured for that local logical router. + An example set with two local routers would be: + <code>local_router_1,local_router_2</code>. + </dd> </dl> <h1>Open vSwitch Database Usage</h1> diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index f68f842..e6c390a 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -51,6 +51,7 @@ #include "stream.h" #include "unixctl.h" #include "util.h" +#include "sset.h" VLOG_DEFINE_THIS_MODULE(main); @@ -252,6 +253,37 @@ get_ovnsb_remote_probe_interval(struct ovsdb_idl *ovs_idl, int *value) return false; } +bool is_local_router(const struct sset *local_routers, + const char *local_router) +{ + if (sset_find(local_routers, local_router)) { + return true; + } + return false; +} + +static void +build_local_routers(const struct smap *ext_ids, + struct sset *local_routers) +{ + const char *local_router_cfg = smap_get(ext_ids, "local-routers"); + if (!local_router_cfg) { + local_router_cfg = ""; + } + + char *cur, *next, *start; + next = start = xstrdup(local_router_cfg); + while ((cur = strsep(&next, ",")) && *cur) { + char *local_router = cur; + + if (sset_find(local_routers, local_router)) { + continue; + } + sset_add(local_routers, local_router); + } + free(start); +} + int main(int argc, char *argv[]) { @@ -353,11 +385,22 @@ main(int argc, char *argv[]) struct hmap patched_datapaths = HMAP_INITIALIZER(&patched_datapaths); + struct sset local_routers = + SSET_INITIALIZER(&local_routers); + const struct ovsrec_bridge *br_int = get_br_int(&ctx); const char *chassis_id = get_chassis_id(ctx.ovs_idl); if (chassis_id) { - chassis_run(&ctx, chassis_id); + chassis_run(&ctx, chassis_id, &local_routers); + + const struct sbrec_chassis *chassis_rec = + get_chassis(ctx.ovnsb_idl, chassis_id); + if (chassis_rec) { + build_local_routers(&chassis_rec->external_ids, + &local_routers); + } + encaps_run(&ctx, br_int, chassis_id); binding_run(&ctx, br_int, chassis_id, &ct_zones, ct_zone_bitmap, &local_datapaths); @@ -377,7 +420,8 @@ main(int argc, char *argv[]) struct hmap flow_table = HMAP_INITIALIZER(&flow_table); lflow_run(&ctx, &lports, &mcgroups, &local_datapaths, - &patched_datapaths, &ct_zones, &flow_table); + &patched_datapaths, &ct_zones, &flow_table, + &local_routers); if (chassis_id) { physical_run(&ctx, mff_ovn_geneve, br_int, chassis_id, &ct_zones, &flow_table, @@ -404,6 +448,8 @@ main(int argc, char *argv[]) } hmap_destroy(&patched_datapaths); + sset_destroy(&local_routers); + unixctl_server_run(unixctl); unixctl_server_wait(unixctl); diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h index 9af7959..012560c 100644 --- a/ovn/controller/ovn-controller.h +++ b/ovn/controller/ovn-controller.h @@ -23,6 +23,7 @@ /* Linux supports a maximum of 64K zones, which seems like a fine default. */ #define MAX_CT_ZONES 65535 +struct sset; struct controller_ctx { struct ovsdb_idl *ovnsb_idl; struct ovsdb_idl_txn *ovnsb_idl_txn; @@ -53,6 +54,9 @@ struct patched_datapath { struct patched_datapath *get_patched_datapath(const struct hmap *, uint32_t tunnel_key); +bool is_local_router(const struct sset *local_routers, + const char *local_router); + const struct ovsrec_bridge *get_bridge(struct ovsdb_idl *, const char *br_name); diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 9e03606..b271f7f 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -460,8 +460,21 @@ build_datapaths(struct northd_context *ctx, struct hmap *datapaths) char uuid_s[UUID_LEN + 1]; sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key)); const char *key = od->nbs ? "logical-switch" : "logical-router"; - const struct smap id = SMAP_CONST1(&id, key, uuid_s); - sbrec_datapath_binding_set_external_ids(od->sb, &id); + struct smap ids = SMAP_INITIALIZER(&ids); + smap_add(&ids, key, uuid_s); + + if (od->nbr) { + const char *value = smap_get(&od->nbr->external_ids, + "local-router"); + + if (value) { + smap_add(&ids, "local-router", value); + } else { + smap_add(&ids, "local-router", ""); + } + } + sbrec_datapath_binding_set_external_ids(od->sb, &ids); + smap_destroy(&ids); sbrec_datapath_binding_set_tunnel_key(od->sb, tunnel_key); } diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 34251af..5b84d6c 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -639,8 +639,10 @@ </column> <group title="Common Columns"> - <column name="external_ids"> - See <em>External IDs</em> at the beginning of this document. + <column name="external_ids" key="local-router"> + The CMS populates this optional key with the a local router name, which + <code>ovn-northd</code> writes into the southbound database + <code>datapath_binding</code> table. </column> </group> </table> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index efd2f9a..aa0d8bd 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1189,6 +1189,14 @@ tcp.flags = RST; corresponding <ref table="Logical_Router" db="OVN_Northbound"/> row in the <ref db="OVN_Northbound"/> database. </column> + + <column name="external_ids" key="local-router"> + <code>ovn-northd</code> populates this key with the a local router name, + which <code>ovn-northd</code> reads from the corresponding logical + router in the northbound database. The use of this external_id is + optional. + </column> + </group> <group title="Common Columns"> diff --git a/tests/ovn.at b/tests/ovn.at index 173dc27..b7b3ad5 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -2748,3 +2748,192 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP + +AT_SETUP([ovn -- 2 HVs, 3 LS, 1 lport/LS, 2 peer LRs, local router]) +AT_KEYWORDS([ovnlocalrouter]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +# Logical network: +# Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24 +# network. R1 has switch foo (192.168.1.0/24) connected to it. +# R2 has alice (172.16.1.0/24) and bob (172.16.2.0/24) connected to it. + +ovn-nbctl create Logical_Router name=R1 +ovn-nbctl create Logical_Router name=R2 external-ids:local-router=llr2 + +ovn-nbctl lswitch-add foo +ovn-nbctl lswitch-add alice +ovn-nbctl lswitch-add bob + +# Connect foo to R1 +ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \ +network=192.168.1.1/24 mac=\"00:00:00:01:02:03\" -- add Logical_Router R1 \ +ports @lrp -- lport-add foo rp-foo + +ovn-nbctl set Logical_port rp-foo type=router options:router-port=foo \ +addresses=\"00:00:00:01:02:03\" + +# Connect alice to R2 +ovn-nbctl -- --id=@lrp create Logical_Router_port name=alice \ +network=172.16.1.1/24 mac=\"00:00:00:01:02:04\" -- add Logical_Router R2 \ +ports @lrp -- lport-add alice rp-alice + +ovn-nbctl set Logical_port rp-alice type=router options:router-port=alice \ +addresses=\"00:00:00:01:02:04\" + +# Connect bob to R1 +ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob-r1 \ +network=172.16.2.3/24 mac=\"00:00:00:01:02:06\" -- add Logical_Router R1 \ +ports @lrp -- lport-add bob rp-bob-r1 + +ovn-nbctl set Logical_port rp-bob-r1 type=router options:router-port=bob-r1 \ +addresses=\"00:00:00:01:02:06\" + +# Connect bob to R2 +ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob \ +network=172.16.2.1/24 mac=\"00:00:00:01:02:05\" -- add Logical_Router R2 \ +ports @lrp -- lport-add bob rp-bob + +ovn-nbctl set Logical_port rp-bob type=router options:router-port=bob \ +addresses=\"00:00:00:01:02:05\" + +# Create logical port foo1 in foo +ovn-nbctl lport-add foo foo1 \ +-- lport-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" + +# Create logical port alice1 in alice +ovn-nbctl lport-add alice alice1 \ +-- lport-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" + +# Create logical port bob1 in bob +ovn-nbctl lport-add bob bob1 \ +-- lport-set-addresses bob1 "f0:00:00:01:02:05 172.16.2.2" + +# Create two hypervisor and create OVS ports corresponding to logical ports. +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=foo1 \ + 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 +ovs-vsctl set open . external-ids:local-routers=llr2 +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=bob1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + +ovs-vsctl -- add-port br-int hv2-vif2 -- \ + set interface hv2-vif2 external-ids:iface-id=alice1 \ + options:tx_pcap=hv2/vif2-tx.pcap \ + options:rxq_pcap=hv2/vif2-rx.pcap \ + ofport-request=2 + + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +ovn_populate_arp + +# Allow some time for ovn-northd and ovn-controller to catch up. +# XXX This should be more systematic. +sleep 1 + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} +trim_zeros() { + sed 's/\(00\)\{1,\}$//' +} + +# Send ip packets between foo1 and bob1 +src_mac="f00000010203" +dst_mac="000000010203" +src_ip=`ip_to_hex 192 168 1 2` +dst_ip=`ip_to_hex 172 16 2 2` +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet + +# Send ip packets between bob1 and alice1 +src_mac="f00000010205" +dst_mac="000000010205" +src_ip=`ip_to_hex 172 16 2 2` +dst_ip=`ip_to_hex 172 16 1 2` +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 +as hv2 ovs-appctl netdev-dummy/receive hv2-vif1 $packet + +echo "---------NB dump-----" +ovn-nbctl show +echo "---------------------" +ovn-nbctl list logical_router +echo "---------------------" +ovn-nbctl list logical_router_port +echo "---------------------" + +echo "---------SB dump-----" +ovn-sbctl list datapath_binding +echo "---------------------" +ovn-sbctl list port_binding +echo "---------------------" +ovn-sbctl dump-flows + +#echo "------ hv1 dump ----------" +#as hv1 ovs-ofctl dump-flows br-int +#echo "------ hv2 dump ----------" +#as hv2 ovs-ofctl dump-flows br-int + +# Packet to Expect at bob1 +src_mac="000000010206" +dst_mac="f00000010205" +src_ip=`ip_to_hex 192 168 1 2` +dst_ip=`ip_to_hex 172 16 2 2` +expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | trim_zeros > received.packets +echo $expected | trim_zeros > expout +AT_CHECK([cat received.packets], [0], [expout]) + +# Packet to Expect at alice1 +src_mac="000000010204" +dst_mac="f00000010204" +src_ip=`ip_to_hex 172 16 2 2` +dst_ip=`ip_to_hex 172 16 1 2` +expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif2-tx.pcap | trim_zeros > received1.packets +echo $expected | trim_zeros > expout +AT_CHECK([cat received1.packets], [0], [expout]) + +for sim in hv1 hv2; do + as $sim + OVS_APP_EXIT_AND_WAIT([ovn-controller]) + OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) + OVS_APP_EXIT_AND_WAIT([ovsdb-server]) +done + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as main +OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +AT_CLEANUP -- 1.9.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev